diff options
Diffstat (limited to 'ansible_collections/community/grafana')
144 files changed, 6607 insertions, 3299 deletions
diff --git a/ansible_collections/community/grafana/.all-contributorsrc b/ansible_collections/community/grafana/.all-contributorsrc index 878f911bf..f1a1cb6dc 100644 --- a/ansible_collections/community/grafana/.all-contributorsrc +++ b/ansible_collections/community/grafana/.all-contributorsrc @@ -177,6 +177,26 @@ "contributions": [ "bug" ] + }, + { + "login": "amenzhinsky", + "name": "Aliaksandr Mianzhynski", + "avatar_url": "https://avatars.githubusercontent.com/u/1308953?v=4", + "profile": "https://github.com/amenzhinsky", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "Nemental", + "name": "Moritz", + "avatar_url": "https://avatars.githubusercontent.com/u/15136847?v=4", + "profile": "https://nemental.de", + "contributions": [ + "bug", + "code" + ] } ], "contributorsPerLine": 7, @@ -185,5 +205,6 @@ "repoType": "github", "repoHost": "https://github.com", "skipCi": true, - "commitConvention": "angular" + "commitConvention": "angular", + "commitType": "docs" } diff --git a/ansible_collections/community/grafana/.config/ansible-lint.yml b/ansible_collections/community/grafana/.config/ansible-lint.yml new file mode 100644 index 000000000..81424ef79 --- /dev/null +++ b/ansible_collections/community/grafana/.config/ansible-lint.yml @@ -0,0 +1,5 @@ +--- +exclude_paths: + - .github/ + - tests/ + - changelogs/ diff --git a/ansible_collections/community/grafana/.config/yamllint/config b/ansible_collections/community/grafana/.config/yamllint/config new file mode 100644 index 000000000..e907140bf --- /dev/null +++ b/ansible_collections/community/grafana/.config/yamllint/config @@ -0,0 +1,2 @@ +--- +extends: default diff --git a/ansible_collections/community/grafana/.github/CODEOWNERS b/ansible_collections/community/grafana/.github/CODEOWNERS index 3035cfbbd..2cc403628 100644 --- a/ansible_collections/community/grafana/.github/CODEOWNERS +++ b/ansible_collections/community/grafana/.github/CODEOWNERS @@ -1,13 +1,9 @@ .github # Repo maintainers, and goverance team (like Anisble's @ansible-commit-external) -* @rrey @seuf @gundalow-collections/community-goverance-team +* @rrey @seuf # or possibly, we may want to define teams at the org level. -# * @gundalow-collections/grafana-maintainers # Example of maintainer of just a single plugin #grafana/plugins/modules/grafana_plugin.py @someone-else - - -.github/ @gundalow diff --git a/ansible_collections/community/grafana/.github/workflows/ansible-test.yml b/ansible_collections/community/grafana/.github/workflows/ansible-test.yml index e8f2cd6b3..a5717781f 100644 --- a/ansible_collections/community/grafana/.github/workflows/ansible-test.yml +++ b/ansible_collections/community/grafana/.github/workflows/ansible-test.yml @@ -12,8 +12,8 @@ jobs: timeout-minutes: 30 strategy: matrix: - python_version: ["3.9"] - ansible_version: ["stable-2.13", "stable-2.14", "devel"] + python_version: ["3.10"] + ansible_version: ["stable-2.15", "stable-2.16", "devel"] steps: - name: Perform testing uses: ansible-community/ansible-test-gh-action@release/v1 @@ -29,8 +29,8 @@ jobs: timeout-minutes: 30 strategy: matrix: - python_version: ["3.9"] - ansible_version: ["stable-2.13", "stable-2.14", "devel"] + python_version: ["3.10"] + ansible_version: ["stable-2.15", "stable-2.16", "devel"] steps: - name: Perform testing uses: ansible-community/ansible-test-gh-action@release/v1 @@ -45,9 +45,9 @@ jobs: strategy: fail-fast: false matrix: - grafana_version: ["9.2.6", "8.5.15", "7.5.16"] - ansible_version: ["stable-2.13", "stable-2.14", "devel"] - python_version: ["3.9"] + grafana_version: ["9.5.14", "8.5.27", "10.2.2"] + ansible_version: ["stable-2.15", "stable-2.16", "devel"] + python_version: ["3.10"] services: grafana: image: grafana/grafana:${{ matrix.grafana_version }} @@ -58,3 +58,37 @@ jobs: ansible-core-version: ${{ matrix.ansible_version }} target-python-version: ${{ matrix.python_version }} testing-type: integration + + molecule: + runs-on: ubuntu-latest + env: + PY_COLORS: 1 + ANSIBLE_FORCE_COLOR: 1 + strategy: + fail-fast: false + matrix: + grafana_version: ["9.5.14", "8.5.27", "10.2.2"] + ansible_version: ["stable-2.15", "stable-2.16", "devel"] + python_version: ["3.10"] + services: + grafana: + image: grafana/grafana:${{ matrix.grafana_version }} + ports: ["3000:3000"] + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python_version }} + + - name: Install dependencies + run: | + python -m pip install --no-cache-dir --upgrade pip + pip install "git+https://github.com/ansible/ansible@${{ matrix.ansible_version }}" molecule molecule-docker + + - name: Test with molecule + run: | + molecule --version + molecule test diff --git a/ansible_collections/community/grafana/.github/workflows/lint.yml b/ansible_collections/community/grafana/.github/workflows/lint.yml new file mode 100644 index 000000000..26216d89b --- /dev/null +++ b/ansible_collections/community/grafana/.github/workflows/lint.yml @@ -0,0 +1,36 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + +jobs: + python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v41 + with: + files: "**/*.py" + + - if: ${{ steps.changed-files.outputs.all_changed_files != '' }} + uses: psf/black@stable + with: + src: ${{ steps.changed-files.outputs.all_changed_files }} + + - if: ${{ steps.changed-files.outputs.all_changed_files != '' }} + uses: chartboost/ruff-action@v1 + with: + src: ${{ steps.changed-files.outputs.all_changed_files }} + ansible: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Run ansible-lint + uses: ansible/ansible-lint@main diff --git a/ansible_collections/community/grafana/.gitignore b/ansible_collections/community/grafana/.gitignore index 9f11b755a..2483976dc 100644 --- a/ansible_collections/community/grafana/.gitignore +++ b/ansible_collections/community/grafana/.gitignore @@ -1 +1,2 @@ .idea/ +__pycache__/ diff --git a/ansible_collections/community/grafana/CHANGELOG.rst b/ansible_collections/community/grafana/CHANGELOG.rst index 426f3cdf5..8a40ac18f 100644 --- a/ansible_collections/community/grafana/CHANGELOG.rst +++ b/ansible_collections/community/grafana/CHANGELOG.rst @@ -5,6 +5,71 @@ Grafana Collection Release Notes .. contents:: Topics +v1.8.0 +====== + +Minor Changes +------------- + +- Manage `grafana_folder` for organizations +- Merged ansible role telekom-mms/ansible-role-grafana into ansible-collections/community.grafana +- added `community.grafana.notification_channel` to role +- grafana_dashboard - add check_mode support + +Bugfixes +-------- + +- test: replace deprecated `TestCase.assertEquals` to support Python 3.12 + +v1.7.0 +====== + +Minor Changes +------------- + +- Add Quickwit search engine datasource (https://quickwit.io). +- Add parameter `org_name` to `grafana_dashboard` +- Add parameter `org_name` to `grafana_datasource` +- Add parameter `org_name` to `grafana_organization_user` +- Add support for Grafana Tempo datasource type (https://grafana.com/docs/grafana/latest/datasources/tempo/) +- default to true/false in docs and code + +Bugfixes +-------- + +- Add `grafana_organiazion_user` to `action_groups.grafana` +- Fixed orgId handling in diff comparison for `grafana_datasource` if using org_name + +v1.6.1 +====== + +Minor Changes +------------- + +- Bump version of Python used in tests to 3.10 +- Enable datasource option `time_interval` for prometheus +- Fix documentation url for Ansible doc website +- Now testing against Grafana 9.5.13, 8.5.27, 10.2.0 + +Bugfixes +-------- + +- Fix error with datasources configured without basicAuth +- grafana_folder, fix an issue during delete (starting Grafana 9.3) + +v1.6.0 +====== + +Minor Changes +------------- + +- Add `grafana_organization_user` module + +New Modules +----------- + +- community.grafana.grafana_organization_user - Manage Grafana Organization Users. + v1.5.4 ====== diff --git a/ansible_collections/community/grafana/FILES.json b/ansible_collections/community/grafana/FILES.json index 4dc1e3d44..be95fa2bc 100644 --- a/ansible_collections/community/grafana/FILES.json +++ b/ansible_collections/community/grafana/FILES.json @@ -8,6 +8,34 @@ "format": 1 }, { + "name": ".config", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".config/yamllint", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": ".config/yamllint/config", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b5910022dcb8c8295c99271d1c55ad728bb9ea20ae873c18aa2df97b8befae92", + "format": 1 + }, + { + "name": ".config/ansible-lint.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "dbf92afa23eaa6ef3d4461829dc8a7d3f8f14caab53e5a74d4070720c721b8af", + "format": 1 + }, + { "name": ".github", "ftype": "dir", "chksum_type": null, @@ -25,7 +53,7 @@ "name": ".github/workflows/ansible-test.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "105c6c1fb8b82644fddd6c6eaa59b21ab581662ff4a673483ee05f16cf44a48a", + "chksum_sha256": "e3e774ec33a3dc2a5ea1da40931ab5e6b96d3b67e57454fc867f9009cbc9797f", "format": 1 }, { @@ -43,6 +71,13 @@ "format": 1 }, { + "name": ".github/workflows/lint.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "405ea441af55f0770ae8d90ed355e34e31cef75a3b70dddd558a013df24e3b5d", + "format": 1 + }, + { "name": ".github/workflows/rebase.yml", "ftype": "file", "chksum_type": "sha256", @@ -53,7 +88,7 @@ "name": ".github/CODEOWNERS", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "27ba6e53b61251034d894049f866e4e15f9946b605b91756597e2a4727beef98", + "chksum_sha256": "81b8c5a2958345d497da521973b94f9c50210535765d8a4cb713d51468e5fb58", "format": 1 }, { @@ -225,6 +260,13 @@ "format": 1 }, { + "name": "changelogs/fragments/238_checkmode.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "7111820e7939b5645f6ebd20cdc03b166f0e8df983808c964334f4449d2fe73c", + "format": 1 + }, + { "name": "changelogs/fragments/239_keyerror_grafana_url.yml", "ftype": "file", "chksum_type": "sha256", @@ -232,6 +274,13 @@ "format": 1 }, { + "name": "changelogs/fragments/242_add_grafana_organization_user_module.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "8c86a0c996a356a2884ab70ef4fadfa58c7e504a26f055bc0926f8065de1f556", + "format": 1 + }, + { "name": "changelogs/fragments/248_ds_update_error_grafana_9.yml", "ftype": "file", "chksum_type": "sha256", @@ -267,6 +316,13 @@ "format": 1 }, { + "name": "changelogs/fragments/285_fix_doc.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "eed5d4709bb9447344c7ca0d8fe47ca3ecb3641697b1e18de865e7ee6b0ec9eb", + "format": 1 + }, + { "name": "changelogs/fragments/288_get_actual_org_encode.yml", "ftype": "file", "chksum_type": "sha256", @@ -274,6 +330,146 @@ "format": 1 }, { + "name": "changelogs/fragments/294-bump-grafana-version.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "91f1f5dd43943a5955f18df300a3497c0d89c9fd54ee8ddb8fbbd881262aa10f", + "format": 1 + }, + { + "name": "changelogs/fragments/300_datasource_prometheus_time_interval.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f8b238f88f025de74c456235a2a04283d22233da36e33d4c8887bdca56d0fcd0", + "format": 1 + }, + { + "name": "changelogs/fragments/308_datasource_quickwit.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da003c09e654cf46ca2de33f32782fdd30f19f5d3196d7d8b3258ff6804565d7", + "format": 1 + }, + { + "name": "changelogs/fragments/311_dashboard_check_mode.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0601ccd2ae0421c355e6183110c64df7243f93e1376f07cf593be5ff4a9f4470", + "format": 1 + }, + { + "name": "changelogs/fragments/318-org_users_by_org_name.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "23c95e1b4d10f2a89dfb8a2857af543b08361ac54e831919c46f30a03f749301", + "format": 1 + }, + { + "name": "changelogs/fragments/321-action-groups-org-users.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f559e5db8e8b85da1b937996024f5f0c23324d40ea6ee5d7fc5c419aa71ebb95", + "format": 1 + }, + { + "name": "changelogs/fragments/324_formatting.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "25665f818c24b36058bcaa706604440967276be1296a2bdc6037271519505e04", + "format": 1 + }, + { + "name": "changelogs/fragments/325_linting.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "af5aec2049dfeae982e2d47daa44e6e84a5f800092b4b818fa8d2a569afbe511", + "format": 1 + }, + { + "name": "changelogs/fragments/325_true_false.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "14a107d144554667832aec72c86f83c5a0fecb31c8645daec3c6933906633459", + "format": 1 + }, + { + "name": "changelogs/fragments/331-dashboard-by-org-name.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "57095fa0bcf6aea3b7f2b79565d3d4f71d01c9d3735ee7044041217c518c4439", + "format": 1 + }, + { + "name": "changelogs/fragments/332-datasource-by-org-name.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "31ad79d93624848dc7b4a30c79938200b94a809ae84df411e7afb993b32529bc", + "format": 1 + }, + { + "name": "changelogs/fragments/335-add-datasource-type-tempo.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "da3c13510502e83b46b7ddc8fffe7d007ec76b4586f311d59c9e7dc33e424754", + "format": 1 + }, + { + "name": "changelogs/fragments/339-lint-black.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "60ede1111ab7b74ab1f933bfe06df23bbcc2fed7fe1cd60270095d6ab315572f", + "format": 1 + }, + { + "name": "changelogs/fragments/341-lint-ruff.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c1549e022f201f9e5dc7d984a84a38a10bce720f12b046abfcaeab24ddfd27b8", + "format": 1 + }, + { + "name": "changelogs/fragments/342-ruff-findings.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ce61f53984c913b4c8f7bbc099938fce8aed19d566422e530b427d7685b797fa", + "format": 1 + }, + { + "name": "changelogs/fragments/343-telekom-mms-role.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "bfe2053658fe18ffcefbb8b3d7f739281ae24693c16d36b13189229994d3e07c", + "format": 1 + }, + { + "name": "changelogs/fragments/345-datasource-compare-diff-orgid.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4698e5052edfce0dc51171d390bb7070e8a70bd3181475e6d8f1f90abe7695db", + "format": 1 + }, + { + "name": "changelogs/fragments/347-folder-for-orgs.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "55a59bceab13dfa96e7a63a0a9b0912ecf9248ae8a9037ffff49ecc78a716ad9", + "format": 1 + }, + { + "name": "changelogs/fragments/349-role-notification-channel.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e46d6a7d7175a33ba08ce335b4fc839164afd9d49ea788bc71777e4aee3ec4fb", + "format": 1 + }, + { + "name": "changelogs/fragments/350-python3.12.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "38410fc78c051c85a770d57487c0e44afcfb7788cc23dc675b47018c31ca2be0", + "format": 1 + }, + { "name": "changelogs/fragments/add-units-datasource.yml", "ftype": "file", "chksum_type": "sha256", @@ -302,6 +498,13 @@ "format": 1 }, { + "name": "changelogs/fragments/fix-316.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "599f961644d8b43f3f8d1c291b4ff1db8c09834d8c493560b91e030f7f2dfb7d", + "format": 1 + }, + { "name": "changelogs/fragments/fix_slashes_in_datasources.yml", "ftype": "file", "chksum_type": "sha256", @@ -340,7 +543,7 @@ "name": "changelogs/changelog.yaml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "672317831941badc24b8b2b99f256386289e6774791ad54e107bd1a20cab4e0b", + "chksum_sha256": "641de48bd2e1d3ab8daeace7b522413299bd4d2521204d1f707e8c23bb445480", "format": 1 }, { @@ -375,14 +578,14 @@ "name": "hacking/find_grafana_versions.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f208e226d9f51971c45afd07149e2f9b6a6b583b35abde56c75bacba9961e84d", + "chksum_sha256": "39acc81ec9733183bbb4eeae0c10c3aeecdeedd1ffc2dabcc675b84e16565f3e", "format": 1 }, { "name": "hacking/requirements.txt", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f68f48b3014372d3e3859aac2030508871fb7f8cd29a5e5b51be92674a192570", + "chksum_sha256": "1d277ef3981a3e49b02912a0f03fe1ab563539d7e4e1b5c1e6404a57b19d883f", "format": 1 }, { @@ -396,7 +599,42 @@ "name": "meta/runtime.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ad30b0b10736f60b92a231e7f825472184755320479821a6486acbc6b502b4f0", + "chksum_sha256": "6872a5ea5042e0a7a43f2b60fe6c02a24e3e76b25deb50a46567df781345e41b", + "format": 1 + }, + { + "name": "molecule", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "molecule/default", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "molecule/default/converge.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c1e0e02c807ceafdf7bd707e0def1e9d2118e6389af9af44dc832dfc0483dab9", + "format": 1 + }, + { + "name": "molecule/default/molecule.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "93fdbc0ddeba791f7c430b0838ef87de49feb9b0662b8b2f1502b5a949e1c301", + "format": 1 + }, + { + "name": "molecule/default/test_dashboard.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ce0f583b94ead7ef7e0ae7e696dc87fefb2ba70106b2fda6cf072f17f8d1a136", "format": 1 }, { @@ -424,7 +662,7 @@ "name": "plugins/callback/grafana_annotations.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fac40baac9edab0ff30d12f63ee7d13f29f1dd4777d65f3a3a80d04cffb5a2e2", + "chksum_sha256": "2f0a1b0cdb40b90e44dd5c23bba4f65773a163cc92ca26263ca20de5a702e8e0", "format": 1 }, { @@ -445,14 +683,14 @@ "name": "plugins/doc_fragments/api_key.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "bb4e6492b954294664aa87ac44bd12ffa42e59ddfcedd693e5a486b42c352dc1", + "chksum_sha256": "624cdc3b10d6a2bbdf9cd7d58fa47281658aa8b1377e702a8ecc6ca1f43bf630", "format": 1 }, { "name": "plugins/doc_fragments/basic_auth.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fff91970e34eca089abc8275b82957c07acd78889db684281be97252a5b6d27b", + "chksum_sha256": "4e16ce6e91408d53d9b482bfdfc1fd5919801aeaa00830e2703bae467fcc3a3d", "format": 1 }, { @@ -473,7 +711,7 @@ "name": "plugins/lookup/grafana_dashboard.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a2329d0c45e452ae9c63ed39a921de5aa7b073be4b405eccfde4abdae6603055", + "chksum_sha256": "7483dabbd84c95d3cd603e2d9d905b51f00e9f6ab4974164f31387c3ee63535d", "format": 1 }, { @@ -487,7 +725,7 @@ "name": "plugins/module_utils/base.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f3a2355da8ff10f76b0a5886d5915556efd87942a640d9f31fb84fe09b4e35c5", + "chksum_sha256": "edc463e8bea58732fd5a43b11c31c86b06ca5f5f5af1c81e4c0e1cc8a00ee59c", "format": 1 }, { @@ -508,56 +746,126 @@ "name": "plugins/modules/grafana_dashboard.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a941cd951e30bc40d8e50b5f94b6fce724e52c427043a45da3490c7d19b69710", + "chksum_sha256": "4f0fa0bd616646667b30cc6373a3983b802704396d0cba184173244b13820b7e", "format": 1 }, { "name": "plugins/modules/grafana_datasource.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7da547912f54e294da3ba2bd529a1541e7837e6d59f9657b32af1c842cb13eee", + "chksum_sha256": "23081add13f2aba994b4cddb264972d219bee823ac1e4f454931d5a6a374bd94", "format": 1 }, { "name": "plugins/modules/grafana_folder.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "3f8b5ff973317b96b2df44d557b4541e3f8878f31c3ac9524e670900a4d94c23", + "chksum_sha256": "0690d114c97f3cfabc115b17b1e90c9304737d6681085473f7321365797be9b8", "format": 1 }, { "name": "plugins/modules/grafana_notification_channel.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f33607a8e2ab8b641f23e37c13c191d4b536d7f17a0e53167491341f0953ba36", + "chksum_sha256": "abfa2ecf38dfa35cfcf4ee3256a0f2f21fb33134669041c613c64522c27511a4", "format": 1 }, { "name": "plugins/modules/grafana_organization.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0770da78a9c0c20ecd893ffff6c949757b3d56e69512c074c097af210de62a95", + "chksum_sha256": "e3007e6f1b18309adf4c2d591cdf8fe7ff7095e16cb8f712d6118e353e74c4fb", + "format": 1 + }, + { + "name": "plugins/modules/grafana_organization_user.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "805358f94bc8d54afb5ceeba18f1539ace43f713c02175441d195837aa96da2e", "format": 1 }, { "name": "plugins/modules/grafana_plugin.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d655afaca63400ea116ea4a2f0629b3c9a614e363a3bc587bdfe40404b13865f", + "chksum_sha256": "fb0ee164a2936bc35381cf79026b6725c7a1df139e71d58989f8912432043c00", "format": 1 }, { "name": "plugins/modules/grafana_team.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "59d95c0432cdc854f7d9176e37c9c0ee0676c27678bbac5a467b762f4985a50e", + "chksum_sha256": "1f455f7615dcc33a7612b2dd6e881dbc90d37fadc02bcd1c9b8edcf3c80cc1f5", "format": 1 }, { "name": "plugins/modules/grafana_user.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "74081babe6e887d9701f85ad6cfba434d534480b84d8eca89c47c587b7f4400b", + "chksum_sha256": "0b1c4c363f47bcdda8f909005816a7f9a87c691050d5123e0b2f3ad2da7cbaed", + "format": 1 + }, + { + "name": "roles", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/grafana", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/grafana/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/grafana/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ee6026b2dd62777a731f2214949124f45d73309102123cad31ad55489a90ca45", + "format": 1 + }, + { + "name": "roles/grafana/meta", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/grafana/meta/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "192ef2ac687cd5594febffbc0ab6f3d184cceed34dc66c11ff217db19f4ccfc2", + "format": 1 + }, + { + "name": "roles/grafana/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "roles/grafana/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0de2cd11feb7e45f3427d063c3123024f94401bc5c1b48eaecea9716efcc03d5", + "format": 1 + }, + { + "name": "roles/grafana/README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ab4f69856777b7b0c208c3c3d06a9771c98f81a729618dc94df80ea981b1b6ab", "format": 1 }, { @@ -599,7 +907,7 @@ "name": "tests/integration/targets/grafana_dashboard/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1abe5b960f170fe88ae3643d49bb82030dec115a22324db1664f6b52dfc874a8", + "chksum_sha256": "3080ff0153745941649566c421bdd85715c4174dc651656a241aef27be424ae7", "format": 1 }, { @@ -624,52 +932,59 @@ "format": 1 }, { + "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "a43bbb6de9a723b95b69c7c51663f1b4601a58353ca226b63a1df474b800590a", + "format": 1 + }, + { "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5f6af8316c89f6a61b424540b5779682e9a411052b63c94d3d411a926a38cc1a", + "chksum_sha256": "6a1fc18ba64dd7e8f1aa7ca2633608e2932e34147acbf2974e8d882ac0087189", "format": 1 }, { "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "621fd78f70b4381fc4a346b59f8c71886a74283d439debe973e6a90677322ae1", + "chksum_sha256": "23e068c4c5a4d2e77166bc442aa40d61678a09f5923e392bc9ed45be961cd238", "format": 1 }, { - "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml", + "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4987b17b353b126c059e60e83ea081eacd83b149d6aeed0ab9d34c013322d574", + "chksum_sha256": "3d80e67a2a60f491578a45be9cce19178a9fb675c2fd04932c353d984db9591d", "format": 1 }, { "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "f679381b8db000fb5bf0e1166fd78196a63efc8c0bba4a8917ae02f85e54c295", + "chksum_sha256": "d2b528f99db43d1a36c10aedcfca5cf98dd3c34c0087a3d5a0c12ac43871efd1", "format": 1 }, { "name": "tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "468c55bbc58254f1e956f8b6eaf318b863e88270f9741a05fd63643d18ced748", + "chksum_sha256": "d1bb246bb41e3b47171953bb4194988ae57015b462208287b5e678e157197776", "format": 1 }, { "name": "tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "abaf3c4b643d2f893fd05849de8ee8f15ed14ae74a24cb9b252db54c64415478", + "chksum_sha256": "7544b7ea2374965a6b14ef47c517058ecc614ed2ce388ed96c7301670b4d2a3e", "format": 1 }, { "name": "tests/integration/targets/grafana_dashboard/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "83298bca7c6ae5f5467168e5c75dbf34497d458f81258935c1b929126b984916", + "chksum_sha256": "9ac539607fab1744739465d19d35eb69337d367f4834f5b260a9f83ccb34a225", "format": 1 }, { @@ -680,6 +995,20 @@ "format": 1 }, { + "name": "tests/integration/targets/grafana_dashboard/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9aa6c8dadef0b4ce9cb08a05d0cf6c40f882d04f9e698bff795b26896e8853c", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_dashboard/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f30cb92dff7bcfa3c7b92ea0fb4d0cd62d2704f282f9a74bd68a5ba3ac34b142", + "format": 1 + }, + { "name": "tests/integration/targets/grafana_datasource", "ftype": "dir", "chksum_type": null, @@ -697,7 +1026,7 @@ "name": "tests/integration/targets/grafana_datasource/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1abe5b960f170fe88ae3643d49bb82030dec115a22324db1664f6b52dfc874a8", + "chksum_sha256": "d7d3a06f224d1576e39fcc2083e33261f30e3ceac18506f17772d8bcb72315c2", "format": 1 }, { @@ -711,91 +1040,105 @@ "name": "tests/integration/targets/grafana_datasource/tasks/azure.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "19ee9ae9eb2a07401743f0cbe9618c10765b9fe008070344676e9c49aa385a14", + "chksum_sha256": "e7ebd714ea8613e5bdfb5ed632526f6435978dfe164e2358cb4d9fa000f7cf65", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/cloudwatch.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "464c12422283a217c8075285d2ea2c05b005b864ea4437c551701925ff44d7b7", + "chksum_sha256": "d3acce4b415027eebf9cef909e2b351fdcf6fadc8fc441da205db041a595010b", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/elastic.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1635d2de6380a9ee458af3ca590f5640b22381bb12417f6bf4dfbdeebc2f4757", + "chksum_sha256": "69fba433ef869f3bb1916f2036a43985af48d21118f08e969f6020532a963d43", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/errors.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a0d109a1c3408a51bde3d5cf753990c46176d8372164b7e4a2f681dae3920352", + "chksum_sha256": "10fac33e0d5bb44d834579e719c498e4e0eb91535625881bb8cb027dc778c39a", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/influx.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2ce76fc18d2405a0ac4964cac4b6aa2a6685788c98bc42a1b2de3ac8e907e02b", + "chksum_sha256": "7852e7f6e7cb6bd5359a29766fa1810faead04e5760536dc5812fafdf32c7e67", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/issues.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "432147d3f437fb0438de8e13581d6c878ef9f74c4fdf8c2c343be2772003608d", + "chksum_sha256": "4ea6ef2c0ff7ab17d4f454b0aca0429dba3bed373b9e7e9f45679354bbb197b6", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/loki.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9a4b7278d0b86f6a140f618fae5ca135078c4e2c6f45c5c9ce1d392e5bc51e00", + "chksum_sha256": "7f6a14c7eb76ee8c8846955ff98bd71c082b741dc1d67b5c7e0f2dd188eea8de", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "721d230780309d65c7e436717d0c5097886a4d8952c51b4a3b60ee6191f37a8f", + "chksum_sha256": "713a162941560a20ecadd97bd185b09e8a0073451286554ead9c7a8deb71b08f", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/postgres.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "705a43192be5211f28a87f6b4123859b98fe8b286ba340a3baf0cc6331afcd99", + "chksum_sha256": "4ed316aab332c0dfcc005213ef28087ea49ed011c0a73bdd0e5f8481e9403de4", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_datasource/tasks/quickwit.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3a2adf0e3207fe51808c044312d537f11321cf0b685eb4f1e01f1dfdafad54e9", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/redis.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "95b32572dabe963cc378c6256b442febb157a41aac8a88b7dfe8aee9010a4bb2", + "chksum_sha256": "18869fb58bfbd29d666c02c18a25050fdd87fb6efe6f3945c7d472ab00addc22", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_datasource/tasks/tempo.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "d9626fb7988f6c16a407d15d0397a4a2fee1b4a25ec1e8c256441afcdcfdbfa4", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/thruk.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "305abe7c14283b27645b7cf40e6e72f37d7c981cf4ec8802624534cbd566bee6", + "chksum_sha256": "40964d1ada4844b2896d15e8d6291fcc3cce2de461e23d6a484271ecbe01bea4", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/uid.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "08990e1b6427c88c54e19eb5bb1ecf7982e82225398dc99df095a8a8d757290b", + "chksum_sha256": "3cd162f42bd87ed70b42a7f1eec4465f46470f959e1995d4da16ba7c985cbab5", "format": 1 }, { "name": "tests/integration/targets/grafana_datasource/tasks/zabbix.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c6a9c292a023c3da296ac2bd326b90c376b2302e07ce94808172313891421f36", + "chksum_sha256": "c2871a04f46eea697114ace8a8ed977e35db18617b6d7d605895d8dc42231d70", "format": 1 }, { @@ -806,6 +1149,20 @@ "format": 1 }, { + "name": "tests/integration/targets/grafana_datasource/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9aa6c8dadef0b4ce9cb08a05d0cf6c40f882d04f9e698bff795b26896e8853c", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_datasource/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "16d7e16475eb96288139560e75d3f538a276ce843d42c922b73bcbdb3061129e", + "format": 1 + }, + { "name": "tests/integration/targets/grafana_folder", "ftype": "dir", "chksum_type": null, @@ -823,7 +1180,7 @@ "name": "tests/integration/targets/grafana_folder/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1abe5b960f170fe88ae3643d49bb82030dec115a22324db1664f6b52dfc874a8", + "chksum_sha256": "d7d3a06f224d1576e39fcc2083e33261f30e3ceac18506f17772d8bcb72315c2", "format": 1 }, { @@ -834,10 +1191,38 @@ "format": 1 }, { + "name": "tests/integration/targets/grafana_folder/tasks/create-delete.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "122f5e9db2674a05c1ee476e946dae3a04e3cd321665c7db71b2cba315097d20", + "format": 1 + }, + { "name": "tests/integration/targets/grafana_folder/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "47bc05ebcddd2fe27fa914c732be64a7c2c6378578d5c8cb6429d73f25d355f5", + "chksum_sha256": "ff5d04e49ad1db4a7020371f3aac554a0f92221b317cd8dba41aade14495a32d", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_folder/tasks/org.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f334b190c923087495214249c3d63199bfdbd9c16b92f3402ab0bbdd4baceee7", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_folder/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f93043b637d08213e98b76e867cf233123d527d183441230d495fd192ebb0ebe", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_folder/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "826699299c1afe13404ece8b0ba37677617244fa6d31479b94ef8fceb86ba40a", "format": 1 }, { @@ -858,7 +1243,7 @@ "name": "tests/integration/targets/grafana_notification_channel/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1abe5b960f170fe88ae3643d49bb82030dec115a22324db1664f6b52dfc874a8", + "chksum_sha256": "d7d3a06f224d1576e39fcc2083e33261f30e3ceac18506f17772d8bcb72315c2", "format": 1 }, { @@ -872,133 +1257,147 @@ "name": "tests/integration/targets/grafana_notification_channel/tasks/dingding.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "20be703a675310b8986c39720a30cbca5697519aeee1036d735af61608a66cc8", + "chksum_sha256": "71fe4171eff7055caae552fd9baf97bf09ddbab7ea9f4d30ad0329fd0ee57ba5", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/discord.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a24f0816c122f278575acf3269028c027d7056350213d5bf79d7e32a87cabb92", + "chksum_sha256": "783aedab3656a22446c4d7d66cb73b9098a0a417501618314e8ee016c7f823e7", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/email.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "70de3340f20f09e20f1a9f6bcb9cdc71133e9d3ca5299a56e29ded8941402c4e", + "chksum_sha256": "bae730db1d3ae0f9c48b620910e22e084fbb2cd78435cded44d08aab03c49017", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a8fc0c11896c5831c6295818e9a2142b51906a1d3b32ca2865f8323be48d8dfe", + "chksum_sha256": "ed076cea03274dc08b91cfe11440f7d9ed56991192ec8e4ce5fc6aead9932d50", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "fbe650e907a7327d069f72b0077cd563cb7016cbf6d51703bf5f463c7e22488b", + "chksum_sha256": "974a0df2c6fbc614f3cb0110a72437744b528d057fcd07065754e1f0d075f572", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/kafka.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "eb784a102669f4f2d087ae644c63f7f2e09e60363b47acea59ccce9299b3ed78", + "chksum_sha256": "d911f680a1dab3c7c6ca24062b07daa650c14cf95d41bb4b3b5f1a03f82bea8e", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/line.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a997b83ad729061f48299a94c609db3eab3f315f4febc86d3e97bc06c0263fec", + "chksum_sha256": "0793dcafac5c6baadd8f807f5738b0f43c1043fcaf5461548608a3f788144900", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "603d4be7ff5a3e2685e3e0c2bc51a526222d29c20f63a4b7d301110566a4f765", + "chksum_sha256": "3928211b941d3ec67d73d44122519b76364772a9ab79c731a8b5e5f22e46078b", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0a3eb750d61a5a3d26434f25b09681a5ac3b27f94bb6d72eb2879e4a5157194d", + "chksum_sha256": "10a080ac4ab19d1c2ab7e2c1e1558acb7e11a3e75ae3c7ed3f90f56ef9425fef", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d781b916d195e780653185c46aae0cf5007dbc9c92920cd380203164c7512d4e", + "chksum_sha256": "5988e630d581c1c2d715e5f617104c8e8ecabb8ecc2c85d18491b3ba16e7ff8c", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "d893bb8c7f3cddacce404325ce903c5f91d1760ebf17b123a9e2ec58b9d262c6", + "chksum_sha256": "abf6e31f7f4485b1b1821e178cd90063572726958c26e724ccc2f0417d8b5999", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/pushover.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1f7313a53d5d3e38de3f79fe1c89be297eac4ffe366cb88872b989354f3beb6a", + "chksum_sha256": "afaf52cddb757eb1a5ad988e5ac07638269d9e8669136db07f6d909d7b9f74f9", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/sensu.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "542998ba92afda7d9e84204f4792be3f094059fe3b3aa85ec4f8cd8ca8e1f7f5", + "chksum_sha256": "cfa23b2fdd7f2aa998f568177dc029a92c10b00492a6dcb9c575d8aaf06af9a4", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5e07153d379c3ed98207b7a9cab4e291962ecf2b493c78e430bb187ad77b3553", + "chksum_sha256": "8666a93b31846da1f4eef2e632a1e9c1d62dc765691733647b828229dd16f2f4", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/teams.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "9cf484e386db1b5f0b39a97e419127c0c23ef016a8cdf307a46f1a43b6d13b50", + "chksum_sha256": "b95ee61e08caf11be5917c421e82feab4e108c5bd05d7eca2804e0bf3dfa69dd", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/telegram.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "881e193e76bc7c63213ccc4d29e9e91831618760edfa33b4d80381799d5684b4", + "chksum_sha256": "5ef4e24ceb5a814dde62620e98f3d47cd748fa0800ff5db398aa4a8726ee3b4f", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/threema.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ab5586f7f1be5494b306bc7db62bd147f76f8d9c2269d3109b4d20ea7ca27e5b", + "chksum_sha256": "dc35cc7b06bd8f2c4194ccad905bb3aa66053c8f30b2fa6c67626a798710b096", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/victorops.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "6676ea8941ac8758a8b7b68c0d620a02fba4aa46a3208a6143dac3c23a4359c0", + "chksum_sha256": "2da1bc74d6e1dc2ecfbf2625c9228a13e72015a37a646baa65343fe0dd4f57b1", "format": 1 }, { "name": "tests/integration/targets/grafana_notification_channel/tasks/webhook.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "7c9625e9e31301fc7ffa49c384b211f2d68fba895f8358eafd403403de023acc", + "chksum_sha256": "ab506c8c1fc656f62b8bfa34d5ac877086ce6918b7282e22f5d2c0d88cd97475", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_notification_channel/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9aa6c8dadef0b4ce9cb08a05d0cf6c40f882d04f9e698bff795b26896e8853c", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_notification_channel/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "6b38ec20b7346f0bd7ad39edb98487a6c123d3e0c5a86671ab5f81bc6f77d30c", "format": 1 }, { @@ -1019,7 +1418,7 @@ "name": "tests/integration/targets/grafana_organization/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5a300746bed6ecd05371181b97507a46472e5379002f23590c6d3a38b576be68", + "chksum_sha256": "3080ff0153745941649566c421bdd85715c4174dc651656a241aef27be424ae7", "format": 1 }, { @@ -1033,7 +1432,70 @@ "name": "tests/integration/targets/grafana_organization/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "49205778d4eacf15c53b6b0e69f3f78c02cb758d235a64636523c82fec9a26e0", + "chksum_sha256": "5fe8af2ef9401defb8f328e0a24b4184bf1e3309aa8be51c983b34edaa85f234", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f93043b637d08213e98b76e867cf233123d527d183441230d495fd192ebb0ebe", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b29bfe7832b74990a2f2a34f268d16946dca7a1fce852b4ef6c29106ffdc510f", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user/defaults", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user/defaults/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "3080ff0153745941649566c421bdd85715c4174dc651656a241aef27be424ae7", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user/tasks", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user/tasks/main.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "4886c04060faeb0617ab4637269d4ba52279e36169cc508e6231ea63773a38b9", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f93043b637d08213e98b76e867cf233123d527d183441230d495fd192ebb0ebe", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_organization_user/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "ff14af15f86f7b705bbc2505639b15edbd8124af4653a8ae12681ea05ac766fd", "format": 1 }, { @@ -1054,7 +1516,7 @@ "name": "tests/integration/targets/grafana_team/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1abe5b960f170fe88ae3643d49bb82030dec115a22324db1664f6b52dfc874a8", + "chksum_sha256": "d7d3a06f224d1576e39fcc2083e33261f30e3ceac18506f17772d8bcb72315c2", "format": 1 }, { @@ -1068,14 +1530,28 @@ "name": "tests/integration/targets/grafana_team/tasks/create_user.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "1c95f96aaaf709c403a815f6bee8ffe0e1a0acf4bf860b04b84fdeb1e2640d70", + "chksum_sha256": "4d64425f6cc9da06d28a9ca5332b1056b01d65396a5a59f8f0d7c592f6f028b3", "format": 1 }, { "name": "tests/integration/targets/grafana_team/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "849f63851576fe7d459f0e69380fbf6915606bbdebf3836d36301d594ddde9e7", + "chksum_sha256": "3a251b9f1794ca36ff3d9025145ad0f639082d3185772115fdac6eeda2fab8b2", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_team/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "b9aa6c8dadef0b4ce9cb08a05d0cf6c40f882d04f9e698bff795b26896e8853c", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_team/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e729b0be1c515ad93b175d7f024bae9232365faaa68a4506b47967447dfa7e65", "format": 1 }, { @@ -1096,7 +1572,7 @@ "name": "tests/integration/targets/grafana_user/defaults/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5a300746bed6ecd05371181b97507a46472e5379002f23590c6d3a38b576be68", + "chksum_sha256": "3080ff0153745941649566c421bdd85715c4174dc651656a241aef27be424ae7", "format": 1 }, { @@ -1110,7 +1586,21 @@ "name": "tests/integration/targets/grafana_user/tasks/main.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "dee06bc10461e6607b56babd7767b70336a3469ba2fcbc687a3f430b4b89e42c", + "chksum_sha256": "b87f7be9298d38992197ddba80c4a9049a314604867772780040e53f6a1a2368", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_user/runme.sh", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "f93043b637d08213e98b76e867cf233123d527d183441230d495fd192ebb0ebe", + "format": 1 + }, + { + "name": "tests/integration/targets/grafana_user/site.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e19f63916db02291f7340bcfbaadde68e2063c98ebd570151e51b2590ba55006", "format": 1 }, { @@ -1163,6 +1653,20 @@ "format": 1 }, { + "name": "tests/sanity/ignore-2.16.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0908af1f4e86722688b3f158cfa9cc81c931ef40c0a166c398f47a4af69a93f9", + "format": 1 + }, + { + "name": "tests/sanity/ignore-2.17.txt", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "0908af1f4e86722688b3f158cfa9cc81c931ef40c0a166c398f47a4af69a93f9", + "format": 1 + }, + { "name": "tests/sanity/ignore-2.9.txt", "ftype": "file", "chksum_type": "sha256", @@ -1201,7 +1705,7 @@ "name": "tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "a2e07e9ebe4ff689cf3a7a6f17fe7a15bb498787d3f34b2b04e8c6a60faec054", + "chksum_sha256": "f7163648b75242c0acbfe2b3cd911ae3119107bfe9578019443fe887b0f6dbf9", "format": 1 }, { @@ -1215,7 +1719,7 @@ "name": "tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "c7b7015b793f75de5c19cd95772a8aaa170eca6e508a305755f61ae11495e4f1", + "chksum_sha256": "ada20cb8aef9852aee7b79afddd78f144f813c9df55baf3b8adc1b3863a8e9db", "format": 1 }, { @@ -1229,7 +1733,7 @@ "name": "tests/unit/modules/grafana/grafana_team/test_grafana_team.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "96116b92002f89708a978cf54ba9209234bc65622b82ea37f67b942a8d19b849", + "chksum_sha256": "53ca8d47d277ebe320b112ccc59a91e6cd0eae6194eecc1991b8e08977fa2c96", "format": 1 }, { @@ -1243,7 +1747,7 @@ "name": "tests/unit/modules/grafana/grafana_user/test_grafana_user.py", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "ebe4eca4179be5a9780ef606dbd01da1e8512df914b9d576a256a9f0b72c66bb", + "chksum_sha256": "02409b123e54c08c9c5e3ce8bdd0213464c00659490873e07cbfbfdc2cec7c7c", "format": 1 }, { @@ -1254,24 +1758,31 @@ "format": 1 }, { + "name": "tests/requirements.yml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "697e4332a08c9c12c77188df7cf1e687151c9d77e61a3b08f879a825d869f6b4", + "format": 1 + }, + { "name": ".all-contributorsrc", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "2ef91cc790e39c200094615d52b2d5a7efcddf6ec2f95d1324a5664b8ed5f35b", + "chksum_sha256": "00fabf54b7d76fb7d88baea1a20fcfb7e5f3b3a11532d9b8bc474a063bc98a8c", "format": 1 }, { "name": ".gitignore", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "0a44bd2b8e7c35cfad385b8f440c3ec6ebcf8bd10248cf5925d2ceacc92f8078", + "chksum_sha256": "b42461ab9570bb951c6614a223aab90b258464234d72e53bfb3853ec69006c5e", "format": 1 }, { "name": "CHANGELOG.rst", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4840bdb66ca2d031a29df53e3a2e7f9bf3da209fb1fb6ef9a4c6284e6791c56b", + "chksum_sha256": "e20df0ad2408ce9c311cf3bf18c99b2362c6eee388db48286b13be28ecedd8db", "format": 1 }, { @@ -1285,14 +1796,21 @@ "name": "README.md", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "5b3523fbf8ffe4c3f7f503f6e63c294c33d0d074588d9e1f278b035676c34336", + "chksum_sha256": "8e93c6a3ce656865252643666a94835e98d904107d7a678f0a74774445f91125", "format": 1 }, { "name": "codecov.yml", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "4b37d1d2f2d4ddf5ad85e350ea6e0b6c18b1d09de82807847a230883a19abde4", + "chksum_sha256": "187229d57b799f65a9da46cafe34a880c14240022bdbb9b8d447a85de31aa02c", + "format": 1 + }, + { + "name": "ruff.toml", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "9b50c21301c083a2c248259c891bed4403e917cc268ffc236f652e73e2869c15", "format": 1 } ], diff --git a/ansible_collections/community/grafana/MANIFEST.json b/ansible_collections/community/grafana/MANIFEST.json index e1c676340..71f9afdc1 100644 --- a/ansible_collections/community/grafana/MANIFEST.json +++ b/ansible_collections/community/grafana/MANIFEST.json @@ -2,7 +2,7 @@ "collection_info": { "namespace": "community", "name": "grafana", - "version": "1.5.4", + "version": "1.8.0", "authors": [ "R\u00e9mi REY (@rrey)", "Thierry Sall\u00e9 (@seuf)" @@ -25,7 +25,7 @@ "name": "FILES.json", "ftype": "file", "chksum_type": "sha256", - "chksum_sha256": "035021ce8ce32b6ab7857a4f7acf48a7911b14c718c7f9625ead4d097e51788d", + "chksum_sha256": "98a10c2378e1ad46d7031d895c557d33af10809314832b9e3af5c2e607099e0b", "format": 1 }, "format": 1 diff --git a/ansible_collections/community/grafana/README.md b/ansible_collections/community/grafana/README.md index bf416426d..6d23f1b96 100644 --- a/ansible_collections/community/grafana/README.md +++ b/ansible_collections/community/grafana/README.md @@ -3,7 +3,7 @@ ![](https://github.com/ansible-collections/grafana/workflows/CI/badge.svg?branch=master) [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.grafana)](https://codecov.io/gh/ansible-collections/community.grafana) <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> -[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors-) <!-- ALL-CONTRIBUTORS-BADGE:END --> This repo hosts the `community.grafana` Ansible Collection. @@ -27,6 +27,7 @@ Click on the name of a plugin or module to view that content's documentation: - [grafana_folder](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_folder_module.html) - [grafana_notification_channel](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_notification_channel_module.html) - [grafana_organization](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_organization_module.html) + - [grafana_organization_user](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_organization_user_module.html) - [grafana_plugin](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_plugin_module.html) - [grafana_team](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_team_module.html) - [grafana_user](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_user_module.html) @@ -36,7 +37,7 @@ Click on the name of a plugin or module to view that content's documentation: We aim at keeping the last 3 Major versions of Grafana tested. This collection is currently testing the modules against following versions of Grafana: ``` -grafana_version: ["9.2.6", "8.5.15", "7.5.16"] +grafana_version: ["9.5.14", "8.5.27", "10.2.2"] ``` ## Installation and Usage @@ -66,12 +67,9 @@ You can either call modules by their Fully Qualified Collection Namespace (FQCN) gather_facts: false connection: local - collections: - - community.grafana - tasks: - name: Ensure Influxdb datasource exists. - grafana_datasource: + community.grafana.grafana_datasource: name: "datasource-influxdb" grafana_url: "https://grafana.company.com" grafana_user: "admin" @@ -92,13 +90,11 @@ In your playbooks, you can set [module defaults](https://github.com/ansible/ansi ```yaml +--- - hosts: localhost gather_facts: false connection: local - collections: - - community.grafana - module_defaults: group/community.grafana.grafana: grafana_url: "https://grafana.company.com" @@ -107,7 +103,7 @@ In your playbooks, you can set [module defaults](https://github.com/ansible/ansi tasks: - name: Ensure Influxdb datasource exists. - grafana_datasource: + community.grafana.grafana_datasource: name: "datasource-influxdb" org_id: "1" ds_type: "influxdb" @@ -117,7 +113,7 @@ In your playbooks, you can set [module defaults](https://github.com/ansible/ansi tls_ca_cert: "/etc/ssl/certs/ca.pem" - name: Create or update a Grafana user - grafana_user: + community.grafana.grafana_user: name: "Bruce Wayne" email: "batman@gotham.city" login: "batman" @@ -184,6 +180,7 @@ Any contribution is welcome and we only ask contributors to: * Provide *at least* integration tests for any contribution. * The Pull Request *MUST* contain a changelog fragment. See [Ansible documentation](https://docs.ansible.com/ansible/latest/community/development_process.html#creating-a-changelog-fragment) about fragments. * Create an issue for any significant contribution that would change a large portion of the code base. +* Use [ruff](https://github.com/astral-sh/ruff) to lint and [black](https://github.com/psf/black) to format your changes on python code. ## Contributors ✨ @@ -195,28 +192,30 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d <table> <tbody> <tr> - <td align="center"><a href="https://github.com/gundalow"><img src="https://avatars1.githubusercontent.com/u/940557?v=4?s=100" width="100px;" alt="John R Barker"/><br /><sub><b>John R Barker</b></sub></a><br /><a href="#infra-gundalow" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=gundalow" title="Tests">⚠️</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=gundalow" title="Code">💻</a></td> - <td align="center"><a href="https://github.com/rrey"><img src="https://avatars1.githubusercontent.com/u/2752379?v=4?s=100" width="100px;" alt="Rémi REY"/><br /><sub><b>Rémi REY</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=rrey" title="Tests">⚠️</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=rrey" title="Documentation">📖</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=rrey" title="Code">💻</a></td> - <td align="center"><a href="https://aperogeek.fr"><img src="https://avatars1.githubusercontent.com/u/1336359?v=4?s=100" width="100px;" alt="Thierry Sallé"/><br /><sub><b>Thierry Sallé</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=seuf" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=seuf" title="Tests">⚠️</a></td> - <td align="center"><a href="http://antoine.tanzil.li"><img src="https://avatars0.githubusercontent.com/u/1068018?v=4?s=100" width="100px;" alt="Antoine"/><br /><sub><b>Antoine</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=Tailzip" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=Tailzip" title="Tests">⚠️</a></td> - <td align="center"><a href="https://github.com/pomverte"><img src="https://avatars0.githubusercontent.com/u/695230?v=4?s=100" width="100px;" alt="hvle"/><br /><sub><b>hvle</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=pomverte" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=pomverte" title="Tests">⚠️</a></td> - <td align="center"><a href="https://github.com/jual"><img src="https://avatars2.githubusercontent.com/u/4416541?v=4?s=100" width="100px;" alt="jual"/><br /><sub><b>jual</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=jual" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=jual" title="Tests">⚠️</a></td> - <td align="center"><a href="https://github.com/MCyprien"><img src="https://avatars2.githubusercontent.com/u/11160859?v=4?s=100" width="100px;" alt="MCyprien"/><br /><sub><b>MCyprien</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=MCyprien" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=MCyprien" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/gundalow"><img src="https://avatars1.githubusercontent.com/u/940557?v=4?s=100" width="100px;" alt="John R Barker"/><br /><sub><b>John R Barker</b></sub></a><br /><a href="#infra-gundalow" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=gundalow" title="Tests">⚠️</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=gundalow" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/rrey"><img src="https://avatars1.githubusercontent.com/u/2752379?v=4?s=100" width="100px;" alt="Rémi REY"/><br /><sub><b>Rémi REY</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=rrey" title="Tests">⚠️</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=rrey" title="Documentation">📖</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=rrey" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://aperogeek.fr"><img src="https://avatars1.githubusercontent.com/u/1336359?v=4?s=100" width="100px;" alt="Thierry Sallé"/><br /><sub><b>Thierry Sallé</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=seuf" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=seuf" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="http://antoine.tanzil.li"><img src="https://avatars0.githubusercontent.com/u/1068018?v=4?s=100" width="100px;" alt="Antoine"/><br /><sub><b>Antoine</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=Tailzip" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=Tailzip" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/pomverte"><img src="https://avatars0.githubusercontent.com/u/695230?v=4?s=100" width="100px;" alt="hvle"/><br /><sub><b>hvle</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=pomverte" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=pomverte" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/jual"><img src="https://avatars2.githubusercontent.com/u/4416541?v=4?s=100" width="100px;" alt="jual"/><br /><sub><b>jual</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=jual" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=jual" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/MCyprien"><img src="https://avatars2.githubusercontent.com/u/11160859?v=4?s=100" width="100px;" alt="MCyprien"/><br /><sub><b>MCyprien</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=MCyprien" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=MCyprien" title="Tests">⚠️</a></td> </tr> <tr> - <td align="center"><a href="https://twitter.com/RealRockaut"><img src="https://avatars0.githubusercontent.com/u/453368?v=4?s=100" width="100px;" alt="Markus Fischbacher"/><br /><sub><b>Markus Fischbacher</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=rockaut" title="Code">💻</a></td> - <td align="center"><a href="https://github.com/rverchere"><img src="https://avatars3.githubusercontent.com/u/232433?v=4?s=100" width="100px;" alt="Remi Verchere"/><br /><sub><b>Remi Verchere</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=rverchere" title="Code">💻</a></td> - <td align="center"><a href="http://akasurde.github.io"><img src="https://avatars1.githubusercontent.com/u/633765?v=4?s=100" width="100px;" alt="Abhijeet Kasurde"/><br /><sub><b>Abhijeet Kasurde</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=Akasurde" title="Documentation">📖</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=Akasurde" title="Tests">⚠️</a></td> - <td align="center"><a href="https://github.com/martinwangjian"><img src="https://avatars2.githubusercontent.com/u/1770277?v=4?s=100" width="100px;" alt="martinwangjian"/><br /><sub><b>martinwangjian</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=martinwangjian" title="Code">💻</a></td> - <td align="center"><a href="https://github.com/CWollinger"><img src="https://avatars2.githubusercontent.com/u/11299733?v=4?s=100" width="100px;" alt="cwollinger"/><br /><sub><b>cwollinger</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=cwollinger" title="Code">💻</a></td> - <td align="center"><a href="https://github.com/Andersson007"><img src="https://avatars3.githubusercontent.com/u/34477873?v=4?s=100" width="100px;" alt="Andrew Klychkov"/><br /><sub><b>Andrew Klychkov</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=Andersson007" title="Code">💻</a></td> - <td align="center"><a href="https://github.com/vnea"><img src="https://avatars.githubusercontent.com/u/10775422?v=4?s=100" width="100px;" alt="Victor"/><br /><sub><b>Victor</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=vnea" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://twitter.com/RealRockaut"><img src="https://avatars0.githubusercontent.com/u/453368?v=4?s=100" width="100px;" alt="Markus Fischbacher"/><br /><sub><b>Markus Fischbacher</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=rockaut" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/rverchere"><img src="https://avatars3.githubusercontent.com/u/232433?v=4?s=100" width="100px;" alt="Remi Verchere"/><br /><sub><b>Remi Verchere</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=rverchere" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="http://akasurde.github.io"><img src="https://avatars1.githubusercontent.com/u/633765?v=4?s=100" width="100px;" alt="Abhijeet Kasurde"/><br /><sub><b>Abhijeet Kasurde</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=Akasurde" title="Documentation">📖</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=Akasurde" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/martinwangjian"><img src="https://avatars2.githubusercontent.com/u/1770277?v=4?s=100" width="100px;" alt="martinwangjian"/><br /><sub><b>martinwangjian</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=martinwangjian" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/CWollinger"><img src="https://avatars2.githubusercontent.com/u/11299733?v=4?s=100" width="100px;" alt="cwollinger"/><br /><sub><b>cwollinger</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=cwollinger" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/Andersson007"><img src="https://avatars3.githubusercontent.com/u/34477873?v=4?s=100" width="100px;" alt="Andrew Klychkov"/><br /><sub><b>Andrew Klychkov</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=Andersson007" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/vnea"><img src="https://avatars.githubusercontent.com/u/10775422?v=4?s=100" width="100px;" alt="Victor"/><br /><sub><b>Victor</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=vnea" title="Code">💻</a></td> </tr> <tr> - <td align="center"><a href="https://github.com/paytroff"><img src="https://avatars.githubusercontent.com/u/93038288?v=4?s=100" width="100px;" alt="paytroff"/><br /><sub><b>paytroff</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=paytroff" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=paytroff" title="Tests">⚠️</a></td> - <td align="center"><a href="https://github.com/jseiser"><img src="https://avatars.githubusercontent.com/u/4855527?v=4?s=100" width="100px;" alt="Justin Seiser"/><br /><sub><b>Justin Seiser</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=jseiser" title="Code">💻</a></td> - <td align="center"><a href="https://github.com/prndrbr"><img src="https://avatars.githubusercontent.com/u/96344856?v=4?s=100" width="100px;" alt="Pierre"/><br /><sub><b>Pierre</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/issues?q=author%3Aprndrbr" title="Bug reports">🐛</a></td> - <td align="center"><a href="https://github.com/miksonx"><img src="https://avatars.githubusercontent.com/u/5308184?v=4?s=100" width="100px;" alt="MiksonX"/><br /><sub><b>MiksonX</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/issues?q=author%3Amiksonx" title="Bug reports">🐛</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/paytroff"><img src="https://avatars.githubusercontent.com/u/93038288?v=4?s=100" width="100px;" alt="paytroff"/><br /><sub><b>paytroff</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=paytroff" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=paytroff" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/jseiser"><img src="https://avatars.githubusercontent.com/u/4855527?v=4?s=100" width="100px;" alt="Justin Seiser"/><br /><sub><b>Justin Seiser</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=jseiser" title="Code">💻</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/prndrbr"><img src="https://avatars.githubusercontent.com/u/96344856?v=4?s=100" width="100px;" alt="Pierre"/><br /><sub><b>Pierre</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/issues?q=author%3Aprndrbr" title="Bug reports">🐛</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/miksonx"><img src="https://avatars.githubusercontent.com/u/5308184?v=4?s=100" width="100px;" alt="MiksonX"/><br /><sub><b>MiksonX</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/issues?q=author%3Amiksonx" title="Bug reports">🐛</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/amenzhinsky"><img src="https://avatars.githubusercontent.com/u/1308953?v=4?s=100" width="100px;" alt="Aliaksandr Mianzhynski"/><br /><sub><b>Aliaksandr Mianzhynski</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/commits?author=amenzhinsky" title="Code">💻</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=amenzhinsky" title="Tests">⚠️</a></td> + <td align="center" valign="top" width="14.28%"><a href="https://nemental.de"><img src="https://avatars.githubusercontent.com/u/15136847?v=4?s=100" width="100px;" alt="Moritz"/><br /><sub><b>Moritz</b></sub></a><br /><a href="https://github.com/ansible-collections/community.grafana/issues?q=author%3ANemental" title="Bug reports">🐛</a> <a href="https://github.com/ansible-collections/community.grafana/commits?author=Nemental" title="Code">💻</a></td> </tr> </tbody> </table> diff --git a/ansible_collections/community/grafana/changelogs/changelog.yaml b/ansible_collections/community/grafana/changelogs/changelog.yaml index 6166fe7a3..465b42bc3 100644 --- a/ansible_collections/community/grafana/changelogs/changelog.yaml +++ b/ansible_collections/community/grafana/changelogs/changelog.yaml @@ -207,3 +207,75 @@ releases: - 277-gha-ansible-test-versions.yml - 288_get_actual_org_encode.yml release_date: '2023-02-07' + 1.6.0: + changes: + minor_changes: + - Add `grafana_organization_user` module + fragments: + - 242_add_grafana_organization_user_module.yml + modules: + - description: Manage Grafana Organization Users. + name: grafana_organization_user + namespace: '' + release_date: '2023-02-19' + 1.6.1: + changes: + bugfixes: + - Fix error with datasources configured without basicAuth + - grafana_folder, fix an issue during delete (starting Grafana 9.3) + minor_changes: + - Bump version of Python used in tests to 3.10 + - Enable datasource option `time_interval` for prometheus + - Fix documentation url for Ansible doc website + - Now testing against Grafana 9.5.13, 8.5.27, 10.2.0 + fragments: + - 285_fix_doc.yml + - 294-bump-grafana-version.yml + - 300_datasource_prometheus_time_interval.yml + - fix-316.yml + release_date: '2023-11-05' + 1.7.0: + changes: + bugfixes: + - Add `grafana_organiazion_user` to `action_groups.grafana` + - Fixed orgId handling in diff comparison for `grafana_datasource` if using + org_name + minor_changes: + - Add Quickwit search engine datasource (https://quickwit.io). + - Add parameter `org_name` to `grafana_dashboard` + - Add parameter `org_name` to `grafana_datasource` + - Add parameter `org_name` to `grafana_organization_user` + - Add support for Grafana Tempo datasource type (https://grafana.com/docs/grafana/latest/datasources/tempo/) + - default to true/false in docs and code + fragments: + - 238_checkmode.yml + - 308_datasource_quickwit.yml + - 318-org_users_by_org_name.yml + - 321-action-groups-org-users.yml + - 324_formatting.yml + - 325_linting.yml + - 325_true_false.yml + - 331-dashboard-by-org-name.yml + - 332-datasource-by-org-name.yml + - 335-add-datasource-type-tempo.yml + - 339-lint-black.yml + - 341-lint-ruff.yml + - 342-ruff-findings.yml + - 345-datasource-compare-diff-orgid.yml + release_date: '2024-01-17' + 1.8.0: + changes: + bugfixes: + - 'test: replace deprecated `TestCase.assertEquals` to support Python 3.12' + minor_changes: + - Manage `grafana_folder` for organizations + - Merged ansible role telekom-mms/ansible-role-grafana into ansible-collections/community.grafana + - added `community.grafana.notification_channel` to role + - grafana_dashboard - add check_mode support + fragments: + - 311_dashboard_check_mode.yml + - 343-telekom-mms-role.yml + - 347-folder-for-orgs.yml + - 349-role-notification-channel.yml + - 350-python3.12.yml + release_date: '2024-02-21' diff --git a/ansible_collections/community/grafana/changelogs/fragments/238_checkmode.yml b/ansible_collections/community/grafana/changelogs/fragments/238_checkmode.yml new file mode 100644 index 000000000..81785b52d --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/238_checkmode.yml @@ -0,0 +1,2 @@ +trivial: + - run integration tests in check-mode. diff --git a/ansible_collections/community/grafana/changelogs/fragments/242_add_grafana_organization_user_module.yml b/ansible_collections/community/grafana/changelogs/fragments/242_add_grafana_organization_user_module.yml new file mode 100644 index 000000000..2296df462 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/242_add_grafana_organization_user_module.yml @@ -0,0 +1,2 @@ +minor_changes: + - Add `grafana_organization_user` module diff --git a/ansible_collections/community/grafana/changelogs/fragments/285_fix_doc.yml b/ansible_collections/community/grafana/changelogs/fragments/285_fix_doc.yml new file mode 100644 index 000000000..74db5e2c7 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/285_fix_doc.yml @@ -0,0 +1,6 @@ +--- + +minor_changes: + - Fix documentation url for Ansible doc website + +... diff --git a/ansible_collections/community/grafana/changelogs/fragments/294-bump-grafana-version.yml b/ansible_collections/community/grafana/changelogs/fragments/294-bump-grafana-version.yml new file mode 100644 index 000000000..7149cfe0f --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/294-bump-grafana-version.yml @@ -0,0 +1,5 @@ +minor_changes: + - Bump version of Python used in tests to 3.10 + - Now testing against Grafana 9.5.13, 8.5.27, 10.2.0 +bugfixes: + - grafana_folder, fix an issue during delete (starting Grafana 9.3) diff --git a/ansible_collections/community/grafana/changelogs/fragments/300_datasource_prometheus_time_interval.yml b/ansible_collections/community/grafana/changelogs/fragments/300_datasource_prometheus_time_interval.yml new file mode 100644 index 000000000..970797f76 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/300_datasource_prometheus_time_interval.yml @@ -0,0 +1,2 @@ +minor_changes: + - Enable datasource option `time_interval` for prometheus diff --git a/ansible_collections/community/grafana/changelogs/fragments/308_datasource_quickwit.yml b/ansible_collections/community/grafana/changelogs/fragments/308_datasource_quickwit.yml new file mode 100644 index 000000000..d91a13082 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/308_datasource_quickwit.yml @@ -0,0 +1,2 @@ +minor_changes: + - Add Quickwit search engine datasource (https://quickwit.io). diff --git a/ansible_collections/community/grafana/changelogs/fragments/311_dashboard_check_mode.yml b/ansible_collections/community/grafana/changelogs/fragments/311_dashboard_check_mode.yml new file mode 100644 index 000000000..394edad9b --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/311_dashboard_check_mode.yml @@ -0,0 +1,5 @@ +minor_changes: + - grafana_dashboard - add check_mode support + +trivial: + - Refactor tests for `grafana_dashboard` diff --git a/ansible_collections/community/grafana/changelogs/fragments/318-org_users_by_org_name.yml b/ansible_collections/community/grafana/changelogs/fragments/318-org_users_by_org_name.yml new file mode 100644 index 000000000..ef8d4959e --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/318-org_users_by_org_name.yml @@ -0,0 +1,7 @@ +--- + +minor_changes: + - Add parameter `org_name` to `grafana_organization_user` + +trivial: + - Add tests for new `grafana_organization_user`-parameter `org_name` diff --git a/ansible_collections/community/grafana/changelogs/fragments/321-action-groups-org-users.yml b/ansible_collections/community/grafana/changelogs/fragments/321-action-groups-org-users.yml new file mode 100644 index 000000000..d62f076bf --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/321-action-groups-org-users.yml @@ -0,0 +1,4 @@ +--- + +bugfixes: + - Add `grafana_organiazion_user` to `action_groups.grafana` diff --git a/ansible_collections/community/grafana/changelogs/fragments/324_formatting.yml b/ansible_collections/community/grafana/changelogs/fragments/324_formatting.yml new file mode 100644 index 000000000..db7b9609d --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/324_formatting.yml @@ -0,0 +1,2 @@ +trivial: + - Format with black, remove unused variables diff --git a/ansible_collections/community/grafana/changelogs/fragments/325_linting.yml b/ansible_collections/community/grafana/changelogs/fragments/325_linting.yml new file mode 100644 index 000000000..cfeff8a52 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/325_linting.yml @@ -0,0 +1,2 @@ +trivial: + - fix linting with ansible-lint write. diff --git a/ansible_collections/community/grafana/changelogs/fragments/325_true_false.yml b/ansible_collections/community/grafana/changelogs/fragments/325_true_false.yml new file mode 100644 index 000000000..c192d086f --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/325_true_false.yml @@ -0,0 +1,2 @@ +minor_changes: +- default to true/false in docs and code diff --git a/ansible_collections/community/grafana/changelogs/fragments/331-dashboard-by-org-name.yml b/ansible_collections/community/grafana/changelogs/fragments/331-dashboard-by-org-name.yml new file mode 100644 index 000000000..576c8c143 --- /dev/null +++ b/ansible_collections/community/grafana/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/ansible_collections/community/grafana/changelogs/fragments/332-datasource-by-org-name.yml b/ansible_collections/community/grafana/changelogs/fragments/332-datasource-by-org-name.yml new file mode 100644 index 000000000..0c8e265df --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/332-datasource-by-org-name.yml @@ -0,0 +1,4 @@ +--- + +minor_changes: + - Add parameter `org_name` to `grafana_datasource` diff --git a/ansible_collections/community/grafana/changelogs/fragments/335-add-datasource-type-tempo.yml b/ansible_collections/community/grafana/changelogs/fragments/335-add-datasource-type-tempo.yml new file mode 100644 index 000000000..18d34a0aa --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/335-add-datasource-type-tempo.yml @@ -0,0 +1,2 @@ +minor_changes: + - Add support for Grafana Tempo datasource type (https://grafana.com/docs/grafana/latest/datasources/tempo/)
\ No newline at end of file diff --git a/ansible_collections/community/grafana/changelogs/fragments/339-lint-black.yml b/ansible_collections/community/grafana/changelogs/fragments/339-lint-black.yml new file mode 100644 index 000000000..2a37ca851 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/339-lint-black.yml @@ -0,0 +1,3 @@ +--- +trivial: + - added python black linting action diff --git a/ansible_collections/community/grafana/changelogs/fragments/341-lint-ruff.yml b/ansible_collections/community/grafana/changelogs/fragments/341-lint-ruff.yml new file mode 100644 index 000000000..e5cfd5b49 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/341-lint-ruff.yml @@ -0,0 +1,3 @@ +--- +trivial: + - added python ruff linting action diff --git a/ansible_collections/community/grafana/changelogs/fragments/342-ruff-findings.yml b/ansible_collections/community/grafana/changelogs/fragments/342-ruff-findings.yml new file mode 100644 index 000000000..79c7ab220 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/342-ruff-findings.yml @@ -0,0 +1,2 @@ +trivial: + - fixed ruff python formatter findings diff --git a/ansible_collections/community/grafana/changelogs/fragments/343-telekom-mms-role.yml b/ansible_collections/community/grafana/changelogs/fragments/343-telekom-mms-role.yml new file mode 100644 index 000000000..07da942ca --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/343-telekom-mms-role.yml @@ -0,0 +1,2 @@ +minor_changes: + - Merged ansible role telekom-mms/ansible-role-grafana into ansible-collections/community.grafana diff --git a/ansible_collections/community/grafana/changelogs/fragments/345-datasource-compare-diff-orgid.yml b/ansible_collections/community/grafana/changelogs/fragments/345-datasource-compare-diff-orgid.yml new file mode 100644 index 000000000..11c211417 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/345-datasource-compare-diff-orgid.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - Fixed orgId handling in diff comparison for `grafana_datasource` if using org_name diff --git a/ansible_collections/community/grafana/changelogs/fragments/347-folder-for-orgs.yml b/ansible_collections/community/grafana/changelogs/fragments/347-folder-for-orgs.yml new file mode 100644 index 000000000..c37990eb4 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/347-folder-for-orgs.yml @@ -0,0 +1,7 @@ +--- + +minor_changes: + - Manage `grafana_folder` for organizations + +trivial: + - Fixed syntax for code in some docs diff --git a/ansible_collections/community/grafana/changelogs/fragments/349-role-notification-channel.yml b/ansible_collections/community/grafana/changelogs/fragments/349-role-notification-channel.yml new file mode 100644 index 000000000..f2428c3a6 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/349-role-notification-channel.yml @@ -0,0 +1,4 @@ +--- + +minor_changes: + - added `community.grafana.notification_channel` to role diff --git a/ansible_collections/community/grafana/changelogs/fragments/350-python3.12.yml b/ansible_collections/community/grafana/changelogs/fragments/350-python3.12.yml new file mode 100644 index 000000000..21b7cc760 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/350-python3.12.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - "test: replace deprecated `TestCase.assertEquals` to support Python 3.12" diff --git a/ansible_collections/community/grafana/changelogs/fragments/fix-316.yml b/ansible_collections/community/grafana/changelogs/fragments/fix-316.yml new file mode 100644 index 000000000..4569d4456 --- /dev/null +++ b/ansible_collections/community/grafana/changelogs/fragments/fix-316.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fix error with datasources configured without basicAuth
\ No newline at end of file diff --git a/ansible_collections/community/grafana/codecov.yml b/ansible_collections/community/grafana/codecov.yml index c01a21d4a..d376ab571 100644 --- a/ansible_collections/community/grafana/codecov.yml +++ b/ansible_collections/community/grafana/codecov.yml @@ -3,4 +3,4 @@ coverage: round: down range: "70...100" fixes: - - "/ansible_collections/community/grafana/::" + - "/ansible_collections/community/grafana/::" diff --git a/ansible_collections/community/grafana/hacking/find_grafana_versions.py b/ansible_collections/community/grafana/hacking/find_grafana_versions.py index 06625a2d5..b31fc530a 100644 --- a/ansible_collections/community/grafana/hacking/find_grafana_versions.py +++ b/ansible_collections/community/grafana/hacking/find_grafana_versions.py @@ -7,11 +7,14 @@ import requests def get_by_major(version): if version.startswith("v"): version = version[1:] - return (version[0], version, int(version.replace('.', ''))) + return (version[0], version, int(version.replace(".", ""))) def get_grafana_releases(): - r = requests.get('https://api.github.com/repos/grafana/grafana/releases?per_page=50', headers={"Accept": "application/vnd.github.v3+json"}) + r = requests.get( + "https://api.github.com/repos/grafana/grafana/releases?per_page=50", + headers={"Accept": "application/vnd.github.v3+json"}, + ) if r.status_code != 200: raise Exception("Failed to get releases from GitHub") return r.json() diff --git a/ansible_collections/community/grafana/hacking/requirements.txt b/ansible_collections/community/grafana/hacking/requirements.txt index ced51d094..2c24336eb 100644 --- a/ansible_collections/community/grafana/hacking/requirements.txt +++ b/ansible_collections/community/grafana/hacking/requirements.txt @@ -1 +1 @@ -requests==2.28.0 +requests==2.31.0 diff --git a/ansible_collections/community/grafana/meta/runtime.yml b/ansible_collections/community/grafana/meta/runtime.yml index 8d9b13a42..15f5554fc 100644 --- a/ansible_collections/community/grafana/meta/runtime.yml +++ b/ansible_collections/community/grafana/meta/runtime.yml @@ -1,12 +1,13 @@ --- -requires_ansible: '>=2.9.0' +requires_ansible: ">=2.14.0" action_groups: grafana: - - grafana_dashboard - - grafana_datasource - - grafana_folder - - grafana_notification_channel - - grafana_organization - - grafana_plugin - - grafana_team - - grafana_user + - grafana_dashboard + - grafana_datasource + - grafana_folder + - grafana_notification_channel + - grafana_organization + - grafana_organization_user + - grafana_plugin + - grafana_team + - grafana_user diff --git a/ansible_collections/community/grafana/molecule/default/converge.yml b/ansible_collections/community/grafana/molecule/default/converge.yml new file mode 100644 index 000000000..b1e1cf20b --- /dev/null +++ b/ansible_collections/community/grafana/molecule/default/converge.yml @@ -0,0 +1,48 @@ +--- +- name: Converge + hosts: localhost + environment: + http_proxy: "{{ lookup('env', 'HTTP_PROXY') | default(omit) }}" + https_proxy: "{{ lookup('env', 'HTTPS_PROXY') | default(omit) }}" + no_proxy: "{{ lookup('env', 'NO_PROXY') | default(omit) }}" + + vars: + grafana_url: http://localhost:3000 + grafana_username: admin + grafana_password: admin + + grafana_organizations: + - name: my_org + + grafana_datasources: + - name: Loki + ds_type: loki + ds_url: http://127.0.0.1:3100 + tls_skip_verify: true + + grafana_folders: + - name: my_service + - name: other_service + + grafana_teams: + - name: my_team + email: myteam@example.de + + grafana_users: + - name: Test User + login: testuser + password: supersecure!123 + email: testuser@example.de + + grafana_organization_users: + - login: testuser + org_id: 1 + - login: testuser + org_name: my_org + + grafana_dashboards: + - folder: my_service + path: test_dashboard.json + overwrite: true + + roles: [{role: community.grafana.grafana}] diff --git a/ansible_collections/community/grafana/molecule/default/molecule.yml b/ansible_collections/community/grafana/molecule/default/molecule.yml new file mode 100644 index 000000000..ab4613bcb --- /dev/null +++ b/ansible_collections/community/grafana/molecule/default/molecule.yml @@ -0,0 +1,21 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: instance + image: rndmh3ro/docker-debian12-ansible:latest + command: ${MOLECULE_DOCKER_COMMAND:-/lib/systemd/systemd} + env: + container: docker + pre_build_image: true + platform: amd64 +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + callback_whitelist: profile_tasks, timer, yaml +verifier: + name: ansible diff --git a/ansible_collections/community/grafana/molecule/default/test_dashboard.json b/ansible_collections/community/grafana/molecule/default/test_dashboard.json new file mode 100644 index 000000000..b95184512 --- /dev/null +++ b/ansible_collections/community/grafana/molecule/default/test_dashboard.json @@ -0,0 +1,126 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "loki" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "datasource": { + "type": "loki" + }, + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 33, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "New dashboard", + "uid": "ES5apb27k", + "version": 1, + "weekStart": "" +} diff --git a/ansible_collections/community/grafana/plugins/callback/grafana_annotations.py b/ansible_collections/community/grafana/plugins/callback/grafana_annotations.py index 04555eae0..6030b0c8b 100644 --- a/ansible_collections/community/grafana/plugins/callback/grafana_annotations.py +++ b/ansible_collections/community/grafana/plugins/callback/grafana_annotations.py @@ -14,10 +14,11 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see <http://www.gnu.org/licenses/>. -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ name: grafana_annotations type: notification short_description: send ansible events as annotations on charts to grafana over http api. @@ -29,7 +30,7 @@ DOCUMENTATION = ''' options: grafana_url: description: Grafana annotations api URL - required: True + required: true env: - name: GRAFANA_URL ini: @@ -45,7 +46,7 @@ DOCUMENTATION = ''' key: validate_grafana_certs - section: callback_grafana_annotations key: validate_certs - default: True + default: true type: bool aliases: [ validate_grafana_certs ] http_agent: @@ -104,7 +105,7 @@ DOCUMENTATION = ''' default: [] type: list elements: integer -''' +""" import json import socket @@ -148,7 +149,7 @@ Result: def to_millis(dt): - return int(dt.strftime('%s')) * 1000 + return int(dt.strftime("%s")) * 1000 class CallbackModule(CallbackBase): @@ -161,15 +162,14 @@ class CallbackModule(CallbackBase): """ CALLBACK_VERSION = 2.0 - CALLBACK_TYPE = 'aggregate' - CALLBACK_NAME = 'community.grafana.grafana_annotations' + CALLBACK_TYPE = "aggregate" + CALLBACK_NAME = "community.grafana.grafana_annotations" CALLBACK_NEEDS_WHITELIST = True def __init__(self, display=None): - super(CallbackModule, self).__init__(display=display) - self.headers = {'Content-Type': 'application/json'} + self.headers = {"Content-Type": "application/json"} self.force_basic_auth = False self.hostname = socket.gethostname() self.username = getpass.getuser() @@ -177,38 +177,42 @@ class CallbackModule(CallbackBase): self.errors = 0 def set_options(self, task_keys=None, var_options=None, direct=None): - - super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct) - - self.grafana_api_key = self.get_option('grafana_api_key') - self.grafana_url = self.get_option('grafana_url') - self.validate_grafana_certs = self.get_option('validate_certs') - self.http_agent = self.get_option('http_agent') - self.grafana_user = self.get_option('grafana_user') - self.grafana_password = self.get_option('grafana_password') - self.dashboard_id = self.get_option('grafana_dashboard_id') - self.panel_ids = self.get_option('grafana_panel_ids') + super(CallbackModule, self).set_options( + task_keys=task_keys, var_options=var_options, direct=direct + ) + + self.grafana_api_key = self.get_option("grafana_api_key") + self.grafana_url = self.get_option("grafana_url") + self.validate_grafana_certs = self.get_option("validate_certs") + self.http_agent = self.get_option("http_agent") + self.grafana_user = self.get_option("grafana_user") + self.grafana_password = self.get_option("grafana_password") + self.dashboard_id = self.get_option("grafana_dashboard_id") + self.panel_ids = self.get_option("grafana_panel_ids") if self.grafana_api_key: - self.headers['Authorization'] = "Bearer %s" % self.grafana_api_key + self.headers["Authorization"] = "Bearer %s" % self.grafana_api_key else: self.force_basic_auth = True if self.grafana_url is None: self.disabled = True - self._display.warning('Grafana URL was not provided. The ' - 'Grafana URL can be provided using ' - 'the `GRAFANA_URL` environment variable.') - self._display.debug('Grafana URL: %s' % self.grafana_url) + self._display.warning( + "Grafana URL was not provided. The " + "Grafana URL can be provided using " + "the `GRAFANA_URL` environment variable." + ) + self._display.debug("Grafana URL: %s" % self.grafana_url) def v2_playbook_on_start(self, playbook): self.playbook = playbook._file_name - text = PLAYBOOK_START_TXT.format(playbook=self.playbook, hostname=self.hostname, - username=self.username) + text = PLAYBOOK_START_TXT.format( + playbook=self.playbook, hostname=self.hostname, username=self.username + ) data = { - 'time': to_millis(self.start_time), - 'text': text, - 'tags': ['ansible', 'ansible_event_start', self.playbook, self.hostname] + "time": to_millis(self.start_time), + "text": text, + "tags": ["ansible", "ansible_event_start", self.playbook, self.hostname], } self._send_annotation(data) @@ -223,30 +227,39 @@ class CallbackModule(CallbackBase): if self.errors == 0: status = "OK" - text = PLAYBOOK_STATS_TXT.format(playbook=self.playbook, hostname=self.hostname, - duration=duration.total_seconds(), - status=status, username=self.username, - summary=json.dumps(summarize_stat)) + text = PLAYBOOK_STATS_TXT.format( + playbook=self.playbook, + hostname=self.hostname, + duration=duration.total_seconds(), + status=status, + username=self.username, + summary=json.dumps(summarize_stat), + ) data = { - 'time': to_millis(self.start_time), - 'timeEnd': to_millis(end_time), - 'isRegion': True, - 'text': text, - 'tags': ['ansible', 'ansible_report', self.playbook, self.hostname] + "time": to_millis(self.start_time), + "timeEnd": to_millis(end_time), + "isRegion": True, + "text": text, + "tags": ["ansible", "ansible_report", self.playbook, self.hostname], } self._send_annotations(data) def v2_runner_on_failed(self, result, ignore_errors=False, **kwargs): - text = PLAYBOOK_ERROR_TXT.format(playbook=self.playbook, hostname=self.hostname, - username=self.username, task=result._task, - host=result._host.name, result=self._dump_results(result._result)) + text = PLAYBOOK_ERROR_TXT.format( + playbook=self.playbook, + hostname=self.hostname, + username=self.username, + task=result._task, + host=result._host.name, + result=self._dump_results(result._result), + ) if ignore_errors: return data = { - 'time': to_millis(datetime.now()), - 'text': text, - 'tags': ['ansible', 'ansible_event_failure', self.playbook, self.hostname] + "time": to_millis(datetime.now()), + "text": text, + "tags": ["ansible", "ansible_event_failure", self.playbook, self.hostname], } self.errors += 1 self._send_annotations(data) @@ -263,10 +276,16 @@ class CallbackModule(CallbackBase): def _send_annotation(self, annotation): try: - open_url(self.grafana_url, data=json.dumps(annotation), headers=self.headers, - method="POST", - validate_certs=self.validate_grafana_certs, - url_username=self.grafana_user, url_password=self.grafana_password, - http_agent=self.http_agent, force_basic_auth=self.force_basic_auth) + open_url( + self.grafana_url, + data=json.dumps(annotation), + headers=self.headers, + method="POST", + validate_certs=self.validate_grafana_certs, + url_username=self.grafana_user, + url_password=self.grafana_password, + http_agent=self.http_agent, + force_basic_auth=self.force_basic_auth, + ) except Exception as e: - self._display.error(u'Could not submit message to Grafana: %s' % to_text(e)) + self._display.error("Could not submit message to Grafana: %s" % to_text(e)) diff --git a/ansible_collections/community/grafana/plugins/doc_fragments/api_key.py b/ansible_collections/community/grafana/plugins/doc_fragments/api_key.py index ffea714e5..94fdb57a2 100644 --- a/ansible_collections/community/grafana/plugins/doc_fragments/api_key.py +++ b/ansible_collections/community/grafana/plugins/doc_fragments/api_key.py @@ -2,17 +2,16 @@ # Copyright: (c) 2019, Rémi REY (@rrey) # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function __metaclass__ = type class ModuleDocFragment(object): - - DOCUMENTATION = r'''options: + DOCUMENTATION = r"""options: grafana_api_key: description: - The Grafana API key. - If set, C(url_username) and C(url_password) will be ignored. type: str - ''' + """ diff --git a/ansible_collections/community/grafana/plugins/doc_fragments/basic_auth.py b/ansible_collections/community/grafana/plugins/doc_fragments/basic_auth.py index 8c41acdbe..5a8aec541 100644 --- a/ansible_collections/community/grafana/plugins/doc_fragments/basic_auth.py +++ b/ansible_collections/community/grafana/plugins/doc_fragments/basic_auth.py @@ -2,14 +2,13 @@ # Copyright: (c) 2019, Rémi REY (@rrey) # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function __metaclass__ = type class ModuleDocFragment(object): - - DOCUMENTATION = r'''options: + DOCUMENTATION = r"""options: url: description: - The Grafana URL. @@ -30,9 +29,9 @@ class ModuleDocFragment(object): aliases: [ grafana_password ] use_proxy: description: - - If C(no), it will not use a proxy, even if one is defined in an environment variable on the target hosts. + - If C(false), it will not use a proxy, even if one is defined in an environment variable on the target hosts. type: bool - default: yes + default: true client_cert: description: - PEM formatted certificate chain file to be used for SSL client authentication. @@ -45,8 +44,8 @@ class ModuleDocFragment(object): type: path validate_certs: description: - - If C(no), SSL certificates will not be validated. - - This should only set to C(no) used on personally controlled sites using self-signed certificates. + - If C(false), SSL certificates will not be validated. + - This should only set to C(false) used on personally controlled sites using self-signed certificates. type: bool - default: yes - ''' + default: true + """ diff --git a/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py b/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py index ff288a1f3..c7fa31574 100644 --- a/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py +++ b/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py @@ -1,9 +1,10 @@ # (c) 2018 Ansible Project # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ name: grafana_dashboard author: Thierry Salle (@seuf) short_description: list or search grafana dashboards @@ -40,7 +41,7 @@ options: description: optional filter for dashboard search. env: - name: GRAFANA_DASHBOARD_SEARCH -''' +""" EXAMPLES = """ - name: get project foo grafana dashboards @@ -64,30 +65,30 @@ from ansible.utils.display import Display display = Display() -ANSIBLE_GRAFANA_URL = 'http://127.0.0.1:3000' +ANSIBLE_GRAFANA_URL = "http://127.0.0.1:3000" ANSIBLE_GRAFANA_API_KEY = None -ANSIBLE_GRAFANA_USER = 'admin' -ANSIBLE_GRAFANA_PASSWORD = 'admin' +ANSIBLE_GRAFANA_USER = "admin" +ANSIBLE_GRAFANA_PASSWORD = "admin" ANSIBLE_GRAFANA_ORG_ID = 1 ANSIBLE_GRAFANA_DASHBOARD_SEARCH = None -if os.getenv('GRAFANA_URL') is not None: - ANSIBLE_GRAFANA_URL = os.environ['GRAFANA_URL'] +if os.getenv("GRAFANA_URL") is not None: + ANSIBLE_GRAFANA_URL = os.environ["GRAFANA_URL"] -if os.getenv('GRAFANA_API_KEY') is not None: - ANSIBLE_GRAFANA_API_KEY = os.environ['GRAFANA_API_KEY'] +if os.getenv("GRAFANA_API_KEY") is not None: + ANSIBLE_GRAFANA_API_KEY = os.environ["GRAFANA_API_KEY"] -if os.getenv('GRAFANA_USER') is not None: - ANSIBLE_GRAFANA_USER = os.environ['GRAFANA_USER'] +if os.getenv("GRAFANA_USER") is not None: + ANSIBLE_GRAFANA_USER = os.environ["GRAFANA_USER"] -if os.getenv('GRAFANA_PASSWORD') is not None: - ANSIBLE_GRAFANA_PASSWORD = os.environ['GRAFANA_PASSWORD'] +if os.getenv("GRAFANA_PASSWORD") is not None: + ANSIBLE_GRAFANA_PASSWORD = os.environ["GRAFANA_PASSWORD"] -if os.getenv('GRAFANA_ORG_ID') is not None: - ANSIBLE_GRAFANA_ORG_ID = os.environ['GRAFANA_ORG_ID'] +if os.getenv("GRAFANA_ORG_ID") is not None: + ANSIBLE_GRAFANA_ORG_ID = os.environ["GRAFANA_ORG_ID"] -if os.getenv('GRAFANA_DASHBOARD_SEARCH') is not None: - ANSIBLE_GRAFANA_DASHBOARD_SEARCH = os.environ['GRAFANA_DASHBOARD_SEARCH'] +if os.getenv("GRAFANA_DASHBOARD_SEARCH") is not None: + ANSIBLE_GRAFANA_DASHBOARD_SEARCH = os.environ["GRAFANA_DASHBOARD_SEARCH"] class GrafanaAPIException(Exception): @@ -96,35 +97,47 @@ class GrafanaAPIException(Exception): class GrafanaAPI: def __init__(self, **kwargs): - self.grafana_url = kwargs.get('grafana_url', ANSIBLE_GRAFANA_URL) - self.grafana_api_key = kwargs.get('grafana_api_key', ANSIBLE_GRAFANA_API_KEY) - self.grafana_user = kwargs.get('grafana_user', ANSIBLE_GRAFANA_USER) - self.grafana_password = kwargs.get('grafana_password', ANSIBLE_GRAFANA_PASSWORD) - self.grafana_org_id = kwargs.get('grafana_org_id', ANSIBLE_GRAFANA_ORG_ID) - self.search = kwargs.get('search', ANSIBLE_GRAFANA_DASHBOARD_SEARCH) + self.grafana_url = kwargs.get("grafana_url", ANSIBLE_GRAFANA_URL) + self.grafana_api_key = kwargs.get("grafana_api_key", ANSIBLE_GRAFANA_API_KEY) + self.grafana_user = kwargs.get("grafana_user", ANSIBLE_GRAFANA_USER) + self.grafana_password = kwargs.get("grafana_password", ANSIBLE_GRAFANA_PASSWORD) + self.grafana_org_id = kwargs.get("grafana_org_id", ANSIBLE_GRAFANA_ORG_ID) + self.search = kwargs.get("search", ANSIBLE_GRAFANA_DASHBOARD_SEARCH) def grafana_switch_organisation(self, headers): try: - r = open_url('%s/api/user/using/%s' % (self.grafana_url, self.grafana_org_id), headers=headers, method='POST') + r = open_url( + "%s/api/user/using/%s" % (self.grafana_url, self.grafana_org_id), + headers=headers, + method="POST", + ) except HTTPError as e: - raise GrafanaAPIException('Unable to switch to organization %s : %s' % (self.grafana_org_id, to_native(e))) + raise GrafanaAPIException( + "Unable to switch to organization %s : %s" + % (self.grafana_org_id, to_native(e)) + ) if r.getcode() != 200: - raise GrafanaAPIException('Unable to switch to organization %s : %s' % (self.grafana_org_id, str(r.getcode()))) + raise GrafanaAPIException( + "Unable to switch to organization %s : %s" + % (self.grafana_org_id, str(r.getcode())) + ) def grafana_headers(self): - headers = {'content-type': 'application/json; charset=utf8'} + headers = {"content-type": "application/json; charset=utf8"} if self.grafana_api_key: api_key = self.grafana_api_key if len(api_key) % 4 == 2: display.deprecated( "Passing a mangled version of the API key to the grafana_dashboard lookup is no longer necessary and should not be done.", "2.0.0", - collection_name='community.grafana', + collection_name="community.grafana", ) - api_key += '==' - headers['Authorization'] = "Bearer %s" % api_key + api_key += "==" + headers["Authorization"] = "Bearer %s" % api_key else: - headers['Authorization'] = basic_auth_header(self.grafana_user, self.grafana_password) + headers["Authorization"] = basic_auth_header( + self.grafana_user, self.grafana_password + ) self.grafana_switch_organisation(headers) return headers @@ -136,35 +149,44 @@ class GrafanaAPI: dashboard_list = [] try: if self.search: - r = open_url('%s/api/search?query=%s' % (self.grafana_url, self.search), headers=headers, method='GET') + r = open_url( + "%s/api/search?query=%s" % (self.grafana_url, self.search), + headers=headers, + method="GET", + ) else: - r = open_url('%s/api/search/' % self.grafana_url, headers=headers, method='GET') + r = open_url( + "%s/api/search/" % self.grafana_url, headers=headers, method="GET" + ) except HTTPError as e: - raise GrafanaAPIException('Unable to search dashboards : %s' % to_native(e)) + raise GrafanaAPIException("Unable to search dashboards : %s" % to_native(e)) if r.getcode() == 200: try: dashboard_list = json.loads(r.read()) except Exception as e: - raise GrafanaAPIException('Unable to parse json list %s' % to_native(e)) + raise GrafanaAPIException("Unable to parse json list %s" % to_native(e)) else: - raise GrafanaAPIException('Unable to list grafana dashboards : %s' % str(r.getcode())) + raise GrafanaAPIException( + "Unable to list grafana dashboards : %s" % str(r.getcode()) + ) return dashboard_list class LookupModule(LookupBase): - def run(self, terms, variables=None, **kwargs): - - grafana_args = terms[0].split(' ') + grafana_args = terms[0].split(" ") grafana_dict = {} ret = [] for param in grafana_args: try: - key, value = param.split('=', 1) + key, value = param.split("=", 1) except ValueError: - raise AnsibleError("grafana_dashboard lookup plugin needs key=value pairs, but received %s" % terms) + raise AnsibleError( + "grafana_dashboard lookup plugin needs key=value pairs, but received %s" + % terms + ) grafana_dict[key] = value grafana = GrafanaAPI(**grafana_dict) diff --git a/ansible_collections/community/grafana/plugins/module_utils/base.py b/ansible_collections/community/grafana/plugins/module_utils/base.py index 3a0174bbd..7d51601d8 100644 --- a/ansible_collections/community/grafana/plugins/module_utils/base.py +++ b/ansible_collections/community/grafana/plugins/module_utils/base.py @@ -16,7 +16,7 @@ # # Copyright: (c) 2019, Rémi REY (@rrey) -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from ansible.module_utils.urls import url_argument_spec __metaclass__ = type @@ -29,26 +29,26 @@ def clean_url(url): def grafana_argument_spec(): argument_spec = url_argument_spec() - del argument_spec['force'] - del argument_spec['force_basic_auth'] - del argument_spec['http_agent'] + del argument_spec["force"] + del argument_spec["force_basic_auth"] + del argument_spec["http_agent"] # Avoid sanity error with devel if "use_gssapi" in argument_spec: - del argument_spec['use_gssapi'] + del argument_spec["use_gssapi"] argument_spec.update( - state=dict(choices=['present', 'absent'], default='present'), - url=dict(aliases=['grafana_url'], type='str', required=True), - grafana_api_key=dict(type='str', no_log=True), - url_username=dict(aliases=['grafana_user'], default='admin'), - url_password=dict(aliases=['grafana_password'], default='admin', no_log=True), + state=dict(choices=["present", "absent"], default="present"), + url=dict(aliases=["grafana_url"], type="str", required=True), + grafana_api_key=dict(type="str", no_log=True), + url_username=dict(aliases=["grafana_user"], default="admin"), + url_password=dict(aliases=["grafana_password"], default="admin", no_log=True), ) return argument_spec def grafana_required_together(): - return [['url_username', 'url_password']] + return [["url_username", "url_password"]] def grafana_mutually_exclusive(): - return [['url_username', 'grafana_api_key']] + return [["url_username", "grafana_api_key"]] diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_dashboard.py b/ansible_collections/community/grafana/plugins/modules/grafana_dashboard.py index 99801d494..77a5a7565 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_dashboard.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_dashboard.py @@ -6,7 +6,7 @@ from __future__ import absolute_import, division, print_function -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: grafana_dashboard author: @@ -18,10 +18,17 @@ description: 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 C(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 C(org_id). + type: str folder: description: - The Grafana folder where this dashboard will be imported to. @@ -58,7 +65,7 @@ options: description: - Override existing dashboard when state is present. type: bool - default: 'no' + default: false dashboard_id: description: - Public Grafana.com dashboard id to import @@ -80,55 +87,52 @@ options: extends_documentation_fragment: - community.grafana.basic_auth - community.grafana.api_key -''' - -EXAMPLES = ''' -- hosts: localhost - connection: local - tasks: - - name: Import Grafana dashboard foo - community.grafana.grafana_dashboard: - grafana_url: http://grafana.company.com - grafana_api_key: "{{ grafana_api_key }}" - state: present - commit_message: Updated by ansible - overwrite: yes - path: /path/to/dashboards/foo.json - - - name: Import Grafana dashboard Zabbix - community.grafana.grafana_dashboard: - grafana_url: http://grafana.company.com - grafana_api_key: "{{ grafana_api_key }}" - folder: zabbix - dashboard_id: 6098 - dashbord_revision: 1 - - - name: Import Grafana dashboard zabbix - community.grafana.grafana_dashboard: - grafana_url: http://grafana.company.com - grafana_api_key: "{{ grafana_api_key }}" - folder: public - dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download - - - name: Export dashboard - community.grafana.grafana_dashboard: - grafana_url: http://grafana.company.com - grafana_user: "admin" - grafana_password: "{{ grafana_password }}" - org_id: 1 - state: export - uid: "000000653" - path: "/path/to/dashboards/000000653.json" -''' - -RETURN = ''' +""" + +EXAMPLES = """ +- name: Import Grafana dashboard foo + community.grafana.grafana_dashboard: + grafana_url: http://grafana.company.com + grafana_api_key: "{{ grafana_api_key }}" + state: present + commit_message: Updated by ansible + overwrite: true + path: /path/to/dashboards/foo.json + +- name: Import Grafana dashboard Zabbix + community.grafana.grafana_dashboard: + grafana_url: http://grafana.company.com + grafana_api_key: "{{ grafana_api_key }}" + folder: zabbix + dashboard_id: 6098 + dashboard_revision: 1 + +- name: Import Grafana dashboard zabbix + community.grafana.grafana_dashboard: + grafana_url: http://grafana.company.com + grafana_api_key: "{{ grafana_api_key }}" + folder: public + dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download + +- name: Export dashboard + community.grafana.grafana_dashboard: + grafana_url: http://grafana.company.com + grafana_user: "admin" + grafana_password: "{{ grafana_password }}" + org_id: 1 + state: export + uid: "000000653" + path: "/path/to/dashboards/000000653.json" +""" + +RETURN = """ --- uid: description: uid or slug of the created / deleted / exported dashboard. returned: success type: str sample: 000000063 -''' +""" import json from ansible.module_utils.basic import AnsibleModule @@ -136,7 +140,10 @@ from ansible.module_utils.urls import fetch_url from ansible.module_utils.six.moves.urllib.parse import urlencode from ansible.module_utils._text import to_native from ansible.module_utils._text import to_text -from ansible_collections.community.grafana.plugins.module_utils.base import grafana_argument_spec, clean_url +from ansible_collections.community.grafana.plugins.module_utils.base import ( + grafana_argument_spec, + clean_url, +) __metaclass__ = type @@ -157,56 +164,91 @@ class GrafanaDeleteException(Exception): pass -def grafana_switch_organisation(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)) +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) + ) def grafana_headers(module, data): - headers = {'content-type': 'application/json; charset=utf8'} - if 'grafana_api_key' in data and data['grafana_api_key']: - headers['Authorization'] = "Bearer %s" % data['grafana_api_key'] + headers = {"content-type": "application/json; charset=utf8"} + if "grafana_api_key" in data and data["grafana_api_key"]: + 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) + module.params["force_basic_auth"] = True + 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 def get_grafana_version(module, grafana_url, headers): grafana_version = None - r, info = fetch_url(module, '%s/api/frontend/settings' % grafana_url, headers=headers, method='GET') - if info['status'] == 200: + r, info = fetch_url( + module, "%s/api/frontend/settings" % grafana_url, headers=headers, method="GET" + ) + if info["status"] == 200: try: settings = json.loads(to_text(r.read())) - grafana_version = settings['buildInfo']['version'].split('.')[0] - except UnicodeError as e: - raise GrafanaAPIException('Unable to decode version string to Unicode') + grafana_version = settings["buildInfo"]["version"].split(".")[0] + except UnicodeError: + raise GrafanaAPIException("Unable to decode version string to Unicode") 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) def grafana_folder_exists(module, grafana_url, folder_name, headers): # the 'General' folder is a special case, it's ID is always '0' - if folder_name == 'General': + if folder_name == "General": return True, 0 try: - r, info = fetch_url(module, '%s/api/folders' % grafana_url, headers=headers, method='GET') + r, info = fetch_url( + module, "%s/api/folders" % grafana_url, headers=headers, method="GET" + ) - if info['status'] != 200: - raise GrafanaAPIException("Unable to query Grafana API for folders (name: %s): %d" % (folder_name, info['status'])) + if info["status"] != 200: + raise GrafanaAPIException( + "Unable to query Grafana API for folders (name: %s): %d" + % (folder_name, info["status"]) + ) folders = json.loads(r.read()) for folder in folders: - if folder['title'] == folder_name: - return True, folder['id'] + if folder["title"] == folder_name: + return True, folder["id"] except Exception as e: raise GrafanaAPIException(e) @@ -219,73 +261,73 @@ def grafana_dashboard_exists(module, grafana_url, uid, headers): grafana_version = get_grafana_version(module, grafana_url, headers) if grafana_version >= 5: - uri = '%s/api/dashboards/uid/%s' % (grafana_url, uid) + uri = "%s/api/dashboards/uid/%s" % (grafana_url, uid) else: - uri = '%s/api/dashboards/db/%s' % (grafana_url, uid) + uri = "%s/api/dashboards/db/%s" % (grafana_url, uid) - r, info = fetch_url(module, uri, headers=headers, method='GET') + r, info = fetch_url(module, uri, headers=headers, method="GET") - if info['status'] == 200: + if info["status"] == 200: dashboard_exists = True try: dashboard = json.loads(r.read()) except Exception as e: raise GrafanaAPIException(e) - elif info['status'] == 404: + elif info["status"] == 404: dashboard_exists = False else: - raise GrafanaAPIException('Unable to get dashboard %s : %s' % (uid, info)) + raise GrafanaAPIException("Unable to get dashboard %s : %s" % (uid, info)) return dashboard_exists, dashboard def grafana_dashboard_search(module, grafana_url, folder_id, title, headers): - # search by title - uri = '%s/api/search?%s' % (grafana_url, urlencode({ - 'folderIds': folder_id, - 'query': title, - 'type': 'dash-db' - })) - r, info = fetch_url(module, uri, headers=headers, method='GET') - - if info['status'] == 200: + uri = "%s/api/search?%s" % ( + grafana_url, + urlencode({"folderIds": folder_id, "query": title, "type": "dash-db"}), + ) + r, info = fetch_url(module, uri, headers=headers, method="GET") + + if info["status"] == 200: try: dashboards = json.loads(r.read()) for d in dashboards: - if d['title'] == title: - return grafana_dashboard_exists(module, grafana_url, d['uid'], headers) + if d["title"] == title: + return grafana_dashboard_exists( + module, grafana_url, d["uid"], headers + ) except Exception as e: raise GrafanaAPIException(e) else: - raise GrafanaAPIException('Unable to search dashboard %s : %s' % (title, info)) + raise GrafanaAPIException("Unable to search dashboard %s : %s" % (title, info)) return False, None # for comparison, we sometimes need to ignore a few keys -def grafana_dashboard_changed(payload, dashboard): +def is_grafana_dashboard_changed(payload, dashboard): # you don't need to set the version, but '0' is incremented to '1' by Grafana's API - if 'version' in payload['dashboard']: - del payload['dashboard']['version'] - if 'version' in dashboard['dashboard']: - del dashboard['dashboard']['version'] + if "version" in payload["dashboard"]: + del payload["dashboard"]["version"] + if "version" in dashboard["dashboard"]: + del dashboard["dashboard"]["version"] # remove meta key if exists for compare - if 'meta' in dashboard: - del dashboard['meta'] - if 'meta' in payload: - del payload['meta'] + if "meta" in dashboard: + del dashboard["meta"] + if "meta" in payload: + del payload["meta"] # if folderId is not provided in dashboard, set default folderId - if 'folderId' not in dashboard: - dashboard['folderId'] = 0 + if "folderId" not in dashboard: + dashboard["folderId"] = 0 # Ignore dashboard ids since real identifier is uuid - if 'id' in dashboard['dashboard']: - del dashboard['dashboard']['id'] - if 'id' in payload['dashboard']: - del payload['dashboard']['id'] + if "id" in dashboard["dashboard"]: + del dashboard["dashboard"]["id"] + if "id" in payload["dashboard"]: + del payload["dashboard"]["id"] if payload == dashboard: return False @@ -293,43 +335,48 @@ def grafana_dashboard_changed(payload, dashboard): def grafana_create_dashboard(module, data): - # define data payload for grafana API payload = {} - if data.get('dashboard_id'): - data['path'] = "https://grafana.com/api/dashboards/%s/revisions/%s/download" % (data['dashboard_id'], data['dashboard_revision']) - if data['path'].startswith('http'): - r, info = fetch_url(module, data['path']) - if info['status'] != 200: - raise GrafanaAPIException('Unable to download grafana dashboard from url %s : %s' % (data['path'], info)) + if data.get("dashboard_id"): + data["path"] = "https://grafana.com/api/dashboards/%s/revisions/%s/download" % ( + data["dashboard_id"], + data["dashboard_revision"], + ) + if data["path"].startswith("http"): + r, info = fetch_url(module, data["path"]) + if info["status"] != 200: + raise GrafanaAPIException( + "Unable to download grafana dashboard from url %s : %s" + % (data["path"], info) + ) payload = json.loads(r.read()) else: try: - with open(data['path'], 'r', encoding="utf-8") as json_file: + with open(data["path"], "r", encoding="utf-8") as json_file: payload = json.load(json_file) except Exception as e: raise GrafanaAPIException("Can't load json file %s" % to_native(e)) # Check that the dashboard JSON is nested under the 'dashboard' key - if 'dashboard' not in payload: - payload = {'dashboard': payload} + if "dashboard" not in payload: + payload = {"dashboard": payload} # define http header headers = grafana_headers(module, data) - grafana_version = get_grafana_version(module, data['url'], headers) + grafana_version = get_grafana_version(module, data["url"], headers) if grafana_version < 5: - if data.get('slug'): - uid = data['slug'] - elif 'meta' in payload and 'slug' in payload['meta']: - uid = payload['meta']['slug'] + if data.get("slug"): + uid = data["slug"] + elif "meta" in payload and "slug" in payload["meta"]: + uid = payload["meta"]["slug"] else: - raise GrafanaMalformedJson('No slug found in json. Needed with grafana < 5') + raise GrafanaMalformedJson("No slug found in json. Needed with grafana < 5") else: - if data.get('uid'): - uid = data['uid'] - elif 'uid' in payload['dashboard']: - uid = payload['dashboard']['uid'] + if data.get("uid"): + uid = data["uid"] + elif "uid" in payload["dashboard"]: + uid = payload["dashboard"]["uid"] else: uid = None @@ -338,148 +385,223 @@ def grafana_create_dashboard(module, data): # test if the folder exists folder_exists = False if grafana_version >= 5: - folder_exists, folder_id = grafana_folder_exists(module, data['url'], data['folder'], headers) + folder_exists, folder_id = grafana_folder_exists( + module, data["url"], data["folder"], headers + ) if folder_exists is False: - raise GrafanaAPIException("Dashboard folder '%s' does not exist." % data['folder']) + raise GrafanaAPIException( + "Dashboard folder '%s' does not exist." % data["folder"] + ) - payload['folderId'] = folder_id + payload["folderId"] = folder_id # test if dashboard already exists if uid: dashboard_exists, dashboard = grafana_dashboard_exists( - module, data['url'], uid, headers=headers) + module, data["url"], uid, headers=headers + ) else: dashboard_exists, dashboard = grafana_dashboard_search( - module, data['url'], folder_id, payload['dashboard']['title'], headers=headers) + module, + data["url"], + folder_id, + payload["dashboard"]["title"], + headers=headers, + ) if dashboard_exists is True: - if grafana_dashboard_changed(payload, dashboard): + grafana_dashboard_changed = is_grafana_dashboard_changed(payload, dashboard) + + if grafana_dashboard_changed: + if module.check_mode: + module.exit_json( + uid=uid, + failed=False, + changed=True, + msg="Dashboard %s will be updated" % payload["dashboard"]["title"], + ) # update - if 'overwrite' in data and data['overwrite']: - payload['overwrite'] = True - if 'commit_message' in data and data['commit_message']: - payload['message'] = data['commit_message'] - - r, info = fetch_url(module, '%s/api/dashboards/db' % data['url'], - data=json.dumps(payload), headers=headers, method='POST') - if info['status'] == 200: + if "overwrite" in data and data["overwrite"]: + payload["overwrite"] = True + if "commit_message" in data and data["commit_message"]: + payload["message"] = data["commit_message"] + + r, info = fetch_url( + module, + "%s/api/dashboards/db" % data["url"], + data=json.dumps(payload), + headers=headers, + method="POST", + ) + if info["status"] == 200: if grafana_version >= 5: try: dashboard = json.loads(r.read()) - uid = dashboard['uid'] + uid = dashboard["uid"] except Exception as e: raise GrafanaAPIException(e) - result['uid'] = uid - result['msg'] = "Dashboard %s updated" % payload['dashboard']['title'] - result['changed'] = True + result["uid"] = uid + result["msg"] = "Dashboard %s updated" % payload["dashboard"]["title"] + result["changed"] = True else: - body = json.loads(info['body']) - raise GrafanaAPIException('Unable to update the dashboard %s : %s (HTTP: %d)' % - (uid, body['message'], info['status'])) + body = json.loads(info["body"]) + raise GrafanaAPIException( + "Unable to update the dashboard %s : %s (HTTP: %d)" + % (uid, body["message"], info["status"]) + ) else: # unchanged - result['uid'] = uid - result['msg'] = "Dashboard %s unchanged." % payload['dashboard']['title'] - result['changed'] = False + result["uid"] = uid + result["msg"] = "Dashboard %s unchanged." % payload["dashboard"]["title"] + result["changed"] = False else: + if module.check_mode: + module.exit_json( + failed=False, + changed=True, + msg="Dashboard %s will be created" % payload["dashboard"]["title"], + ) + # Ensure there is no id in payload - if 'id' in payload['dashboard']: - del payload['dashboard']['id'] - - r, info = fetch_url(module, '%s/api/dashboards/db' % data['url'], - data=json.dumps(payload), headers=headers, method='POST') - if info['status'] == 200: - result['msg'] = "Dashboard %s created" % payload['dashboard']['title'] - result['changed'] = True + if "id" in payload["dashboard"]: + del payload["dashboard"]["id"] + + r, info = fetch_url( + module, + "%s/api/dashboards/db" % data["url"], + data=json.dumps(payload), + headers=headers, + method="POST", + ) + if info["status"] == 200: + result["msg"] = "Dashboard %s created" % payload["dashboard"]["title"] + result["changed"] = True if grafana_version >= 5: try: dashboard = json.loads(r.read()) - uid = dashboard['uid'] + uid = dashboard["uid"] except Exception as e: raise GrafanaAPIException(e) - result['uid'] = uid + result["uid"] = uid else: - raise GrafanaAPIException('Unable to create the new dashboard %s : %s - %s. (headers : %s)' % - (payload['dashboard']['title'], info['status'], info, headers)) + raise GrafanaAPIException( + "Unable to create the new dashboard %s : %s - %s. (headers : %s)" + % (payload["dashboard"]["title"], info["status"], info, headers) + ) return result def grafana_delete_dashboard(module, data): - # define http headers headers = grafana_headers(module, data) - grafana_version = get_grafana_version(module, data['url'], headers) + grafana_version = get_grafana_version(module, data["url"], headers) if grafana_version < 5: - if data.get('slug'): - uid = data['slug'] + if data.get("slug"): + uid = data["slug"] else: - raise GrafanaMalformedJson('No slug parameter. Needed with grafana < 5') + raise GrafanaMalformedJson("No slug parameter. Needed with grafana < 5") else: - if data.get('uid'): - uid = data['uid'] + if data.get("uid"): + uid = data["uid"] else: - raise GrafanaDeleteException('No uid specified %s') + raise GrafanaDeleteException("No uid specified %s") # test if dashboard already exists - dashboard_exists, dashboard = grafana_dashboard_exists(module, data['url'], uid, headers=headers) + dashboard_exists, dashboard = grafana_dashboard_exists( + module, data["url"], uid, headers=headers + ) result = {} if dashboard_exists is True: + if module.check_mode: + module.exit_json( + uid=uid, + failed=False, + changed=True, + msg="Dashboard %s will be deleted" % uid, + ) + # delete if grafana_version < 5: - r, info = fetch_url(module, '%s/api/dashboards/db/%s' % (data['url'], uid), headers=headers, method='DELETE') + r, info = fetch_url( + module, + "%s/api/dashboards/db/%s" % (data["url"], uid), + headers=headers, + method="DELETE", + ) else: - r, info = fetch_url(module, '%s/api/dashboards/uid/%s' % (data['url'], uid), headers=headers, method='DELETE') - if info['status'] == 200: - result['msg'] = "Dashboard %s deleted" % uid - result['changed'] = True - result['uid'] = uid + r, info = fetch_url( + module, + "%s/api/dashboards/uid/%s" % (data["url"], uid), + headers=headers, + method="DELETE", + ) + if info["status"] == 200: + result["msg"] = "Dashboard %s deleted" % uid + result["changed"] = True + result["uid"] = uid else: - raise GrafanaAPIException('Unable to update the dashboard %s : %s' % (uid, info)) + raise GrafanaAPIException( + "Unable to update the dashboard %s : %s" % (uid, info) + ) else: # dashboard does not exist, do nothing - result = {'msg': "Dashboard %s does not exist." % uid, - 'changed': False, - 'uid': uid} + result = { + "msg": "Dashboard %s does not exist." % uid, + "changed": False, + "uid": uid, + } return result def grafana_export_dashboard(module, data): - # define http headers headers = grafana_headers(module, data) - grafana_version = get_grafana_version(module, data['url'], headers) + grafana_version = get_grafana_version(module, data["url"], headers) if grafana_version < 5: - if data.get('slug'): - uid = data['slug'] + if data.get("slug"): + uid = data["slug"] else: - raise GrafanaMalformedJson('No slug parameter. Needed with grafana < 5') + raise GrafanaMalformedJson("No slug parameter. Needed with grafana < 5") else: - if data.get('uid'): - uid = data['uid'] + if data.get("uid"): + uid = data["uid"] else: - raise GrafanaExportException('No uid specified') + raise GrafanaExportException("No uid specified") # test if dashboard already exists - dashboard_exists, dashboard = grafana_dashboard_exists(module, data['url'], uid, headers=headers) + dashboard_exists, dashboard = grafana_dashboard_exists( + module, data["url"], uid, headers=headers + ) if dashboard_exists is True: + if module.check_mode: + module.exit_json( + uid=uid, + failed=False, + changed=True, + msg="Dashboard %s will be exported to %s" % (uid, data["path"]), + ) try: - with open(data['path'], 'w', encoding="utf-8") as f: + with open(data["path"], "w", encoding="utf-8") as f: f.write(json.dumps(dashboard, indent=2)) except Exception as e: raise GrafanaExportException("Can't write json file : %s" % to_native(e)) - result = {'msg': "Dashboard %s exported to %s" % (uid, data['path']), - 'uid': uid, - 'changed': True} + result = { + "msg": "Dashboard %s exported to %s" % (uid, data["path"]), + "uid": uid, + "changed": True, + } else: - result = {'msg': "Dashboard %s does not exist." % uid, - 'uid': uid, - 'changed': False} + result = { + "msg": "Dashboard %s does not exist." % uid, + "uid": uid, + "changed": False, + } return result @@ -488,72 +610,75 @@ def main(): # use the predefined argument spec for url argument_spec = grafana_argument_spec() argument_spec.update( - state=dict(choices=['present', 'absent', 'export'], default='present'), - org_id=dict(default=1, type='int'), - folder=dict(type='str', default='General'), - uid=dict(type='str'), - slug=dict(type='str'), - path=dict(aliases=['dashboard_url'], type='str'), - dashboard_id=dict(type='str'), - dashboard_revision=dict(type='str', default='1'), - overwrite=dict(type='bool', default=False), - commit_message=dict(type='str', aliases=['message'], - deprecated_aliases=[dict(name='message', - version='2.0.0', collection_name="community.grafana")]), + 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"), + path=dict(aliases=["dashboard_url"], type="str"), + dashboard_id=dict(type="str"), + dashboard_revision=dict(type="str", default="1"), + overwrite=dict(type="bool", default=False), + commit_message=dict( + type="str", + aliases=["message"], + deprecated_aliases=[ + dict( + name="message", version="2.0.0", collection_name="community.grafana" + ) + ], + ), ) module = AnsibleModule( argument_spec=argument_spec, - supports_check_mode=False, + supports_check_mode=True, required_if=[ - ['state', 'export', ['path']], + ["state", "export", ["path"]], + ], + required_together=[["url_username", "url_password", "org_id"]], + mutually_exclusive=[ + ["url_username", "grafana_api_key"], + ["uid", "slug"], + ["path", "dashboard_id"], + ["org_id", "org_name"], ], - required_together=[['url_username', 'url_password', 'org_id']], - mutually_exclusive=[['url_username', 'grafana_api_key'], ['uid', 'slug'], ['path', 'dashboard_id']], ) module.params["url"] = clean_url(module.params["url"]) - if 'message' in module.params: - module.fail_json(msg="'message' is reserved keyword, please change this parameter to 'commit_message'") + if "message" in module.params: + module.fail_json( + msg="'message' is reserved keyword, please change this parameter to 'commit_message'" + ) try: - if module.params['state'] == 'present': + if module.params["state"] == "present": result = grafana_create_dashboard(module, module.params) - elif module.params['state'] == 'absent': + elif module.params["state"] == "absent": result = grafana_delete_dashboard(module, module.params) else: result = grafana_export_dashboard(module, module.params) except GrafanaAPIException as e: - module.fail_json( - failed=True, - msg="error : %s" % to_native(e) - ) + module.fail_json(failed=True, msg="error : %s" % to_native(e)) return except GrafanaMalformedJson as e: - module.fail_json( - failed=True, - msg="error : %s" % to_native(e) - ) + module.fail_json(failed=True, msg="error : %s" % to_native(e)) return except GrafanaDeleteException as e: module.fail_json( - failed=True, - msg="error : Can't delete dashboard : %s" % to_native(e) + failed=True, msg="error : Can't delete dashboard : %s" % to_native(e) ) return except GrafanaExportException as e: module.fail_json( - failed=True, - msg="error : Can't export dashboard : %s" % to_native(e) + failed=True, msg="error : Can't export dashboard : %s" % to_native(e) ) return - module.exit_json( - failed=False, - **result - ) + module.exit_json(failed=False, **result) return -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py b/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py index 6346038f4..29cdbea7c 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py @@ -5,9 +5,10 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ module: grafana_datasource author: - Thierry Sallé (@seuf) @@ -46,6 +47,8 @@ options: - camptocamp-prometheus-alertmanager-datasource - loki - redis-datasource + - tempo + - quickwit-quickwit-datasource type: str ds_url: description: @@ -67,15 +70,18 @@ options: (index name), C(mysql) or C(postgres). required: false type: str + default: '' user: description: - The datasource login user for influxdb datasources. type: str + default: '' password: description: - The datasource password. - Stored as secure data, see C(enforce_secure_data) and notes! type: str + default: '' basic_auth_user: description: - The datasource basic auth user. @@ -83,7 +89,7 @@ options: type: str basic_auth_password: description: - - The datasource basic auth password, when C(basic auth) is C(yes). + - The datasource basic auth password, when C(basic auth) is C(true). - Stored as secure data, see C(enforce_secure_data) and notes! type: str with_credentials: @@ -91,7 +97,7 @@ options: - Whether credentials such as cookies or auth headers should be sent with cross-site requests. type: bool - default: 'no' + default: false tls_client_cert: description: - The client TLS certificate. @@ -120,14 +126,22 @@ options: description: - Make this datasource the default one. type: bool - default: 'no' + default: false org_id: description: - - Grafana Organisation ID in which the datasource should be created. + - Grafana organization ID in which the datasource should be created. - Not used when C(grafana_api_key) is set, because the C(grafana_api_key) only - belong to one organisation. + belongs to one organization. + - Mutually exclusive with C(org_name). default: 1 type: int + org_name: + description: + - Grafana organization name in which the datasource should be created. + - Not used when C(grafana_api_key) is set, because the C(grafana_api_key) only + belongs to one organization. + - Mutually exclusive with C(org_id). + type: str state: description: - Status of the datasource @@ -166,7 +180,7 @@ options: default: '@timestamp' time_interval: description: - - Minimum group by interval for C(influxdb) or C(elasticsearch) datasources. + - Minimum group by interval for C(influxdb), C(elasticsearch) or C(prometheus) datasources. - for example C(>10s). type: str interval: @@ -180,6 +194,7 @@ options: - Monthly - Yearly type: str + default: '' tsdb_version: description: - The opentsdb version. @@ -213,7 +228,7 @@ options: description: - Use trends or not for zabbix datasource type. type: bool - default: False + default: false aws_auth_type: description: - Type for AWS authentication for CloudWatch datasource type (authType of grafana @@ -280,7 +295,6 @@ options: aws_custom_metrics_namespaces: description: - Namespaces of Custom Metrics for CloudWatch datasource type - default: '' required: false type: str azure_cloud: @@ -350,9 +364,9 @@ notes: data will not be updated after initial creation! To force the secure data update you have to set I(enforce_secure_data=True). - Hint, with the C(enforce_secure_data) always reporting changed=True, you might just do one Task updating the datasource without any secure data and make a separate playbook/task also changing the secure data. This way it will not break any workflow. -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- - name: Create elasticsearch datasource community.grafana.grafana_datasource: @@ -460,9 +474,9 @@ EXAMPLES = ''' additional_secure_json_data: httpHeaderValue1: "Bearer ihavenogroot" enforce_secure_data: true -''' +""" -RETURN = ''' +RETURN = """ --- datasource: description: datasource created/updated by module @@ -488,13 +502,13 @@ datasource: "user": "", "password": "", "withCredentials": false } -''' +""" import json from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves.urllib.parse import quote -from ansible.module_utils.urls import fetch_url, url_argument_spec, basic_auth_header +from ansible.module_utils.urls import fetch_url, basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base @@ -506,175 +520,191 @@ ES_VERSION_MAPPING = { def compare_datasources(new, current, compareSecureData=True): - if new['uid'] is None: - del current['uid'] - del new['uid'] - del current['typeLogoUrl'] - del current['id'] - if 'version' in current: - del current['version'] - if 'readOnly' in current: - del current['readOnly'] - if current['basicAuth'] is False: - del current['basicAuthUser'] - if 'password' in current: - del current['password'] - if 'basicAuthPassword' in current: - del current['basicAuthPassword'] + if new["uid"] is None: + del current["uid"] + del new["uid"] + del current["typeLogoUrl"] + del current["id"] + if "version" in current: + del current["version"] + if "readOnly" in current: + del current["readOnly"] + if current["basicAuth"] is False: + if "basicAuthUser" in current: + del current["basicAuthUser"] + if "password" in current: + del current["password"] + if "basicAuthPassword" in current: + del current["basicAuthPassword"] # check if secureJsonData should be compared if not compareSecureData: # if we should ignore it just drop alltogether - new.pop('secureJsonData', None) - new.pop('secureJsonFields', None) - current.pop('secureJsonData', None) - current.pop('secureJsonFields', None) + new.pop("secureJsonData", None) + new.pop("secureJsonFields", None) + current.pop("secureJsonData", None) + current.pop("secureJsonFields", None) else: # handle secureJsonData/secureJsonFields, some current facts: # - secureJsonFields is reporting each field set as true # - secureJsonFields once set cant be removed (DS has to be deleted) - if not new.get('secureJsonData'): + if not new.get("secureJsonData"): # secureJsonData is not provided so just remove both for comparision - new.pop('secureJsonData', None) - current.pop('secureJsonFields', None) + new.pop("secureJsonData", None) + current.pop("secureJsonFields", None) else: # we have some secure data so just "rename" secureJsonFields for comparison as it will change anyhow everytime - current['secureJsonData'] = current.pop('secureJsonFields') + current["secureJsonData"] = current.pop("secureJsonFields") return dict(before=current, after=new) -def get_datasource_payload(data): +def get_datasource_payload(data, org_id=None): payload = { - 'orgId': data['org_id'], - 'name': data['name'], - 'uid': data['uid'], - 'type': data['ds_type'], - 'access': data['access'], - 'url': data['ds_url'], - 'database': data['database'], - 'withCredentials': data['with_credentials'], - 'isDefault': data['is_default'], - 'user': data['user'], - 'jsonData': data['additional_json_data'], - 'secureJsonData': data['additional_secure_json_data'] + "orgId": data["org_id"] if org_id is None else org_id, + "name": data["name"], + "uid": data["uid"], + "type": data["ds_type"], + "access": data["access"], + "url": data["ds_url"], + "database": data["database"], + "withCredentials": data["with_credentials"], + "isDefault": data["is_default"], + "user": data["user"], + "jsonData": data["additional_json_data"], + "secureJsonData": data["additional_secure_json_data"], } - json_data = payload['jsonData'] - secure_json_data = payload['secureJsonData'] + json_data = payload["jsonData"] + secure_json_data = payload["secureJsonData"] # define password - if data.get('password'): - secure_json_data['password'] = data['password'] + if data.get("password"): + secure_json_data["password"] = data["password"] # define basic auth - if 'basic_auth_user' in data and data['basic_auth_user'] and 'basic_auth_password' in data and data['basic_auth_password']: - payload['basicAuth'] = True - payload['basicAuthUser'] = data['basic_auth_user'] - secure_json_data['basicAuthPassword'] = data['basic_auth_password'] + if ( + "basic_auth_user" in data + and data["basic_auth_user"] + and "basic_auth_password" in data + and data["basic_auth_password"] + ): + payload["basicAuth"] = True + payload["basicAuthUser"] = data["basic_auth_user"] + secure_json_data["basicAuthPassword"] = data["basic_auth_password"] else: - payload['basicAuth'] = False + payload["basicAuth"] = False # define tls auth - if data.get('tls_client_cert') and data.get('tls_client_key'): - json_data['tlsAuth'] = True - if data.get('tls_ca_cert'): - secure_json_data['tlsCACert'] = data['tls_ca_cert'] - secure_json_data['tlsClientCert'] = data['tls_client_cert'] - secure_json_data['tlsClientKey'] = data['tls_client_key'] - json_data['tlsAuthWithCACert'] = True + if data.get("tls_client_cert") and data.get("tls_client_key"): + json_data["tlsAuth"] = True + if data.get("tls_ca_cert"): + secure_json_data["tlsCACert"] = data["tls_ca_cert"] + secure_json_data["tlsClientCert"] = data["tls_client_cert"] + secure_json_data["tlsClientKey"] = data["tls_client_key"] + json_data["tlsAuthWithCACert"] = True else: - secure_json_data['tlsClientCert'] = data['tls_client_cert'] - secure_json_data['tlsClientKey'] = data['tls_client_key'] + secure_json_data["tlsClientCert"] = data["tls_client_cert"] + secure_json_data["tlsClientKey"] = data["tls_client_key"] else: - json_data['tlsAuth'] = False - json_data['tlsAuthWithCACert'] = False - if data.get('tls_ca_cert'): - json_data['tlsAuthWithCACert'] = True - secure_json_data['tlsCACert'] = data['tls_ca_cert'] + json_data["tlsAuth"] = False + json_data["tlsAuthWithCACert"] = False + if data.get("tls_ca_cert"): + json_data["tlsAuthWithCACert"] = True + secure_json_data["tlsCACert"] = data["tls_ca_cert"] - if data.get('tls_skip_verify'): - json_data['tlsSkipVerify'] = True + if data.get("tls_skip_verify"): + json_data["tlsSkipVerify"] = True # datasource type related parameters - if data['ds_type'] == 'elasticsearch': - - json_data['maxConcurrentShardRequests'] = data['max_concurrent_shard_requests'] - json_data['timeField'] = data['time_field'] - if data.get('interval'): - json_data['interval'] = data['interval'] + if data["ds_type"] == "elasticsearch": + json_data["maxConcurrentShardRequests"] = data["max_concurrent_shard_requests"] + json_data["timeField"] = data["time_field"] + if data.get("interval"): + json_data["interval"] = data["interval"] # Handle changes in es_version format in Grafana < 8.x which used to # be integers and is now semver format try: - es_version = int(data['es_version']) + es_version = int(data["es_version"]) if es_version < 56: - json_data.pop('maxConcurrentShardRequests') + json_data.pop("maxConcurrentShardRequests") except ValueError: # Retrieve the Semver format expected by API - es_version = ES_VERSION_MAPPING.get(data['es_version']) - json_data['esVersion'] = es_version + es_version = ES_VERSION_MAPPING.get(data["es_version"]) + json_data["esVersion"] = es_version - if data['ds_type'] == 'elasticsearch' or data['ds_type'] == 'influxdb': - if data.get('time_interval'): - json_data['timeInterval'] = data['time_interval'] + if data["ds_type"] in ["elasticsearch", "influxdb", "prometheus"]: + if data.get("time_interval"): + json_data["timeInterval"] = data["time_interval"] - if data['ds_type'] == 'opentsdb': - json_data['tsdbVersion'] = data['tsdb_version'] - if data['tsdb_resolution'] == 'second': - json_data['tsdbResolution'] = 1 + if data["ds_type"] == "opentsdb": + json_data["tsdbVersion"] = data["tsdb_version"] + if data["tsdb_resolution"] == "second": + json_data["tsdbResolution"] = 1 else: - json_data['tsdbResolution'] = 2 - - if data['ds_type'] == 'postgres': - json_data['sslmode'] = data['sslmode'] - - if data['ds_type'] == 'alexanderzobnin-zabbix-datasource': - if data.get('trends'): - json_data['trends'] = True - json_data['username'] = data['zabbix_user'] - json_data['password'] = data['zabbix_password'] - - if data['ds_type'] == 'grafana-azure-monitor-datasource': - json_data['tenantId'] = data['azure_tenant'] - json_data['clientId'] = data['azure_client'] - json_data['cloudName'] = data['azure_cloud'] - json_data['clientsecret'] = 'clientsecret' - if data.get('azure_secret'): - secure_json_data['clientSecret'] = data['azure_secret'] - - if data['ds_type'] == 'cloudwatch': - if data.get('aws_credentials_profile'): - payload['database'] = data.get('aws_credentials_profile') - - json_data['authType'] = data['aws_auth_type'] - json_data['defaultRegion'] = data['aws_default_region'] - - if data.get('aws_custom_metrics_namespaces'): - json_data['customMetricsNamespaces'] = data.get('aws_custom_metrics_namespaces') - if data.get('aws_assume_role_arn'): - json_data['assumeRoleArn'] = data.get('aws_assume_role_arn') - if data.get('aws_access_key') and data.get('aws_secret_key'): - secure_json_data['accessKey'] = data.get('aws_access_key') - secure_json_data['secretKey'] = data.get('aws_secret_key') - - payload['jsonData'] = json_data - payload['secureJsonData'] = secure_json_data + json_data["tsdbResolution"] = 2 + + if data["ds_type"] == "postgres": + json_data["sslmode"] = data["sslmode"] + + if data["ds_type"] == "alexanderzobnin-zabbix-datasource": + if data.get("trends"): + json_data["trends"] = True + json_data["username"] = data["zabbix_user"] + json_data["password"] = data["zabbix_password"] + + if data["ds_type"] == "grafana-azure-monitor-datasource": + json_data["tenantId"] = data["azure_tenant"] + json_data["clientId"] = data["azure_client"] + json_data["cloudName"] = data["azure_cloud"] + json_data["clientsecret"] = "clientsecret" + if data.get("azure_secret"): + secure_json_data["clientSecret"] = data["azure_secret"] + + if data["ds_type"] == "cloudwatch": + if data.get("aws_credentials_profile"): + payload["database"] = data.get("aws_credentials_profile") + + json_data["authType"] = data["aws_auth_type"] + json_data["defaultRegion"] = data["aws_default_region"] + + if data.get("aws_custom_metrics_namespaces"): + json_data["customMetricsNamespaces"] = data.get( + "aws_custom_metrics_namespaces" + ) + if data.get("aws_assume_role_arn"): + json_data["assumeRoleArn"] = data.get("aws_assume_role_arn") + if data.get("aws_access_key") and data.get("aws_secret_key"): + secure_json_data["accessKey"] = data.get("aws_access_key") + secure_json_data["secretKey"] = data.get("aws_secret_key") + + payload["jsonData"] = json_data + payload["secureJsonData"] = secure_json_data return payload class GrafanaInterface(object): - def __init__(self, module): self._module = module self.grafana_url = base.clean_url(module.params.get("url")) + self.org_id = None # {{{ Authentication header self.headers = {"Content-Type": "application/json"} - if module.params.get('grafana_api_key', None): - self.headers["Authorization"] = "Bearer %s" % module.params['grafana_api_key'] + if module.params.get("grafana_api_key", None): + self.headers["Authorization"] = ( + "Bearer %s" % module.params["grafana_api_key"] + ) else: - self.headers["Authorization"] = basic_auth_header(module.params['url_username'], module.params['url_password']) - self.switch_organisation(module.params['org_id']) + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) + self.org_id = ( + self.organization_by_name(module.params["org_name"]) + if module.params["org_name"] + else module.params["org_id"] + ) + self.switch_organization(self.org_id) # }}} def _send_request(self, url, data=None, headers=None, method="GET"): @@ -684,125 +714,200 @@ class GrafanaInterface(object): headers = [] full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url(self._module, full_url, data=data, headers=headers, method=method) + resp, info = fetch_url( + self._module, full_url, data=data, headers=headers, method=method + ) status_code = info["status"] if status_code == 404: return None elif status_code == 401: - self._module.fail_json(failed=True, msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url)) + self._module.fail_json( + failed=True, + msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), + ) elif status_code == 403: self._module.fail_json(failed=True, msg="Permission Denied") elif status_code == 200: return self._module.from_json(resp.read()) - self._module.fail_json(failed=True, msg="Grafana API answered with HTTP %d for url %s and data %s" % (status_code, url, data)) + self._module.fail_json( + failed=True, + msg="Grafana API answered with HTTP %d for url %s and data %s" + % (status_code, url, data), + ) - def switch_organisation(self, org_id): + def switch_organization(self, org_id): url = "/api/user/using/%d" % org_id - response = self._send_request(url, headers=self.headers, method='POST') + self._send_request(url, headers=self.headers, method="POST") + + def organization_by_name(self, org_name): + url = "/api/user/orgs" + organizations = self._send_request(url, headers=self.headers, method="GET") + orga = next((org for org in organizations if org["name"] == org_name)) + if orga: + return orga["orgId"] + + return self._module.fail_json( + failed=True, msg="Current user isn't member of organization: %s" % org_name + ) def datasource_by_name(self, name): - datasource_exists = False - ds = {} - url = "/api/datasources/name/%s" % quote(name, safe='') - return self._send_request(url, headers=self.headers, method='GET') + url = "/api/datasources/name/%s" % quote(name, safe="") + return self._send_request(url, headers=self.headers, method="GET") def delete_datasource(self, name): - url = "/api/datasources/name/%s" % quote(name, safe='') - self._send_request(url, headers=self.headers, method='DELETE') + url = "/api/datasources/name/%s" % quote(name, safe="") + self._send_request(url, headers=self.headers, method="DELETE") def update_datasource(self, ds_id, data): url = "/api/datasources/%d" % ds_id - self._send_request(url, data=data, headers=self.headers, method='PUT') + self._send_request(url, data=data, headers=self.headers, method="PUT") def create_datasource(self, data): url = "/api/datasources" - self._send_request(url, data=data, headers=self.headers, method='POST') + self._send_request(url, data=data, headers=self.headers, method="POST") def setup_module_object(): argument_spec = base.grafana_argument_spec() argument_spec.update( - name=dict(required=True, type='str'), - uid=dict(type='str'), - ds_type=dict(choices=['graphite', - 'prometheus', - 'elasticsearch', - 'influxdb', - 'opentsdb', - 'mysql', - 'postgres', - 'cloudwatch', - 'alexanderzobnin-zabbix-datasource', - 'grafana-azure-monitor-datasource', - 'camptocamp-prometheus-alertmanager-datasource', - 'sni-thruk-datasource', - 'redis-datasource', - 'loki']), - ds_url=dict(type='str'), - access=dict(default='proxy', choices=['proxy', 'direct']), - database=dict(type='str', default=""), - user=dict(default='', type='str'), - password=dict(default='', no_log=True, type='str'), - basic_auth_user=dict(type='str'), - basic_auth_password=dict(type='str', no_log=True), - with_credentials=dict(default=False, type='bool'), - tls_client_cert=dict(type='str', no_log=True), - tls_client_key=dict(type='str', no_log=True), - tls_ca_cert=dict(type='str', no_log=True), - tls_skip_verify=dict(type='bool', default=False), - is_default=dict(default=False, type='bool'), - org_id=dict(default=1, type='int'), - es_version=dict(type='str', default="7.10+", choices=["2", "5", "56", "60", - "70", "7.7+", "7.10+", - "8.0+"]), - max_concurrent_shard_requests=dict(type='int', default=256), - time_field=dict(default='@timestamp', type='str'), - time_interval=dict(type='str'), - interval=dict(type='str', choices=['', 'Hourly', 'Daily', 'Weekly', 'Monthly', 'Yearly'], default=''), - tsdb_version=dict(type='int', default=1, choices=[1, 2, 3]), - tsdb_resolution=dict(type='str', default='second', choices=['second', 'millisecond']), - sslmode=dict(default='disable', choices=['disable', 'require', 'verify-ca', 'verify-full']), - trends=dict(default=False, type='bool'), - aws_auth_type=dict(default='keys', choices=['keys', 'credentials', 'arn', 'default']), - aws_default_region=dict(default='us-east-1', choices=['ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-south-1', - 'ca-central-1', - 'cn-north-1', 'cn-northwest-1', - 'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-west-3', - 'sa-east-1', - 'us-east-1', 'us-east-2', 'us-gov-west-1', 'us-west-1', 'us-west-2']), - aws_access_key=dict(default='', no_log=True, type='str'), - aws_secret_key=dict(default='', no_log=True, type='str'), - aws_credentials_profile=dict(default='', type='str'), - aws_assume_role_arn=dict(default='', type='str'), - aws_custom_metrics_namespaces=dict(type='str'), - azure_cloud=dict(type='str', default='azuremonitor', choices=['azuremonitor', 'chinaazuremonitor', 'govazuremonitor', 'germanyazuremonitor']), - azure_tenant=dict(type='str'), - azure_client=dict(type='str'), - azure_secret=dict(type='str', no_log=True), - zabbix_user=dict(type='str'), - zabbix_password=dict(type='str', no_log=True), - additional_json_data=dict(type='dict', default={}, required=False), - additional_secure_json_data=dict(type='dict', default={}, required=False), - enforce_secure_data=dict(type='bool', default=False, required=False) + name=dict(required=True, type="str"), + uid=dict(type="str"), + ds_type=dict( + choices=[ + "graphite", + "prometheus", + "elasticsearch", + "influxdb", + "opentsdb", + "mysql", + "postgres", + "cloudwatch", + "alexanderzobnin-zabbix-datasource", + "grafana-azure-monitor-datasource", + "camptocamp-prometheus-alertmanager-datasource", + "sni-thruk-datasource", + "redis-datasource", + "loki", + "tempo", + "quickwit-quickwit-datasource", + ] + ), + ds_url=dict(type="str"), + access=dict(default="proxy", choices=["proxy", "direct"]), + database=dict(type="str", default=""), + user=dict(default="", type="str"), + password=dict(default="", no_log=True, type="str"), + basic_auth_user=dict(type="str"), + basic_auth_password=dict(type="str", no_log=True), + with_credentials=dict(default=False, type="bool"), + tls_client_cert=dict(type="str", no_log=True), + tls_client_key=dict(type="str", no_log=True), + tls_ca_cert=dict(type="str", no_log=True), + tls_skip_verify=dict(type="bool", default=False), + is_default=dict(default=False, type="bool"), + org_id=dict(default=1, type="int"), + org_name=dict(type="str"), + es_version=dict( + type="str", + default="7.10+", + choices=["2", "5", "56", "60", "70", "7.7+", "7.10+", "8.0+"], + ), + max_concurrent_shard_requests=dict(type="int", default=256), + time_field=dict(default="@timestamp", type="str"), + time_interval=dict(type="str"), + interval=dict( + type="str", + choices=["", "Hourly", "Daily", "Weekly", "Monthly", "Yearly"], + default="", + ), + tsdb_version=dict(type="int", default=1, choices=[1, 2, 3]), + tsdb_resolution=dict( + type="str", default="second", choices=["second", "millisecond"] + ), + sslmode=dict( + default="disable", + choices=["disable", "require", "verify-ca", "verify-full"], + ), + trends=dict(default=False, type="bool"), + aws_auth_type=dict( + default="keys", choices=["keys", "credentials", "arn", "default"] + ), + aws_default_region=dict( + default="us-east-1", + choices=[ + "ap-northeast-1", + "ap-northeast-2", + "ap-southeast-1", + "ap-southeast-2", + "ap-south-1", + "ca-central-1", + "cn-north-1", + "cn-northwest-1", + "eu-central-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-gov-west-1", + "us-west-1", + "us-west-2", + ], + ), + aws_access_key=dict(default="", no_log=True, type="str"), + aws_secret_key=dict(default="", no_log=True, type="str"), + aws_credentials_profile=dict(default="", type="str"), + aws_assume_role_arn=dict(default="", type="str"), + aws_custom_metrics_namespaces=dict(type="str"), + azure_cloud=dict( + type="str", + default="azuremonitor", + choices=[ + "azuremonitor", + "chinaazuremonitor", + "govazuremonitor", + "germanyazuremonitor", + ], + ), + azure_tenant=dict(type="str"), + azure_client=dict(type="str"), + azure_secret=dict(type="str", no_log=True), + zabbix_user=dict(type="str"), + zabbix_password=dict(type="str", no_log=True), + additional_json_data=dict(type="dict", default={}, required=False), + additional_secure_json_data=dict(type="dict", default={}, required=False), + enforce_secure_data=dict(type="bool", default=False, required=False), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=False, - required_together=[['url_username', 'url_password', 'org_id'], ['tls_client_cert', 'tls_client_key']], - mutually_exclusive=[['url_username', 'grafana_api_key'], ['tls_ca_cert', 'tls_skip_verify']], + required_together=[ + ["url_username", "url_password", "org_id"], + ["tls_client_cert", "tls_client_key"], + ], + mutually_exclusive=[ + ["url_username", "grafana_api_key"], + ["tls_ca_cert", "tls_skip_verify"], + ["org_id", "org_name"], + ], required_if=[ - ['state', 'present', ['ds_type', 'ds_url']], - ['ds_type', 'opentsdb', ['tsdb_version', 'tsdb_resolution']], - ['ds_type', 'influxdb', ['database']], - ['ds_type', 'elasticsearch', ['database', 'es_version', 'time_field', 'interval']], - ['ds_type', 'mysql', ['database']], - ['ds_type', 'postgres', ['database', 'sslmode']], - ['ds_type', 'cloudwatch', ['aws_auth_type', 'aws_default_region']], - ['es_version', "56", ['max_concurrent_shard_requests']], - ['es_version', "60", ['max_concurrent_shard_requests']], - ['es_version', "70", ['max_concurrent_shard_requests']] + ["state", "present", ["ds_type", "ds_url"]], + ["ds_type", "opentsdb", ["tsdb_version", "tsdb_resolution"]], + ["ds_type", "influxdb", ["database"]], + [ + "ds_type", + "elasticsearch", + ["database", "es_version", "time_field", "interval"], + ], + ["ds_type", "mysql", ["database"]], + ["ds_type", "postgres", ["database", "sslmode"]], + ["ds_type", "cloudwatch", ["aws_auth_type", "aws_default_region"]], + ["es_version", "56", ["max_concurrent_shard_requests"]], + ["es_version", "60", ["max_concurrent_shard_requests"]], + ["es_version", "70", ["max_concurrent_shard_requests"]], ], ) return module @@ -811,35 +916,52 @@ def setup_module_object(): def main(): module = setup_module_object() - state = module.params['state'] - name = module.params['name'] - enforce_secure_data = module.params['enforce_secure_data'] + state = module.params["state"] + name = module.params["name"] + enforce_secure_data = module.params["enforce_secure_data"] grafana_iface = GrafanaInterface(module) ds = grafana_iface.datasource_by_name(name) - if state == 'present': - payload = get_datasource_payload(module.params) + if state == "present": + payload = get_datasource_payload(module.params, grafana_iface.org_id) if ds is None: grafana_iface.create_datasource(payload) ds = grafana_iface.datasource_by_name(name) - module.exit_json(changed=True, datasource=ds, msg='Datasource %s created' % name) + module.exit_json( + changed=True, datasource=ds, msg="Datasource %s created" % name + ) else: diff = compare_datasources(payload.copy(), ds.copy(), enforce_secure_data) - if diff.get('before') == diff.get('after'): - module.exit_json(changed=False, datasource=ds, msg='Datasource %s unchanged' % name) - grafana_iface.update_datasource(ds.get('id'), payload) + if diff.get("before") == diff.get("after"): + module.exit_json( + changed=False, datasource=ds, msg="Datasource %s unchanged" % name + ) + grafana_iface.update_datasource(ds.get("id"), payload) ds = grafana_iface.datasource_by_name(name) - if diff.get('before') == diff.get('after'): - module.exit_json(changed=False, datasource=ds, msg='Datasource %s unchanged' % name) - - module.exit_json(changed=True, diff=diff, datasource=ds, msg='Datasource %s updated' % name) + if diff.get("before") == diff.get("after"): + module.exit_json( + changed=False, datasource=ds, msg="Datasource %s unchanged" % name + ) + + module.exit_json( + changed=True, + diff=diff, + datasource=ds, + msg="Datasource %s updated" % name, + ) else: if ds is None: - module.exit_json(changed=False, datasource=None, msg='Datasource %s does not exist.' % name) + module.exit_json( + changed=False, + datasource=None, + msg="Datasource %s does not exist." % name, + ) grafana_iface.delete_datasource(name) - module.exit_json(changed=True, datasource=None, msg='Datasource %s deleted.' % name) + module.exit_json( + changed=True, datasource=None, msg="Datasource %s deleted." % name + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_folder.py b/ansible_collections/community/grafana/plugins/modules/grafana_folder.py index d39e56e41..73c437dbb 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_folder.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_folder.py @@ -19,7 +19,7 @@ from __future__ import absolute_import, division, print_function -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: grafana_folder author: @@ -44,20 +44,35 @@ options: default: present type: str choices: ["present", "absent"] + org_id: + description: + - Grafana organization ID in which the datasource should be created. + - Not used when C(grafana_api_key) is set, because the C(grafana_api_key) only + belongs to one organization. + - Mutually exclusive with C(org_name). + default: 1 + type: int + org_name: + description: + - Grafana organization name in which the datasource should be created. + - Not used when C(grafana_api_key) is set, because the C(grafana_api_key) only + belongs to one organization. + - Mutually exclusive with C(org_id). + type: str skip_version_check: description: - Skip Grafana version check and try to reach api endpoint anyway. - - This parameter can be useful if you enabled `hide_version` in grafana.ini + - This parameter can be useful if you enabled C(hide_version) in grafana.ini required: False type: bool - default: False + default: false version_added: "1.2.0" extends_documentation_fragment: - community.grafana.basic_auth - community.grafana.api_key -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- - name: Create a folder community.grafana.grafana_folder: @@ -72,9 +87,9 @@ EXAMPLES = ''' grafana_api_key: "{{ some_api_token_value }}" title: "grafana_working_group" state: absent -''' +""" -RETURN = ''' +RETURN = """ --- folder: description: Information about the Folder @@ -159,7 +174,7 @@ folder: type: int sample: - 1 -''' +""" import json @@ -177,24 +192,36 @@ class GrafanaError(Exception): class GrafanaFolderInterface(object): - def __init__(self, module): self._module = module + self.grafana_url = base.clean_url(module.params.get("url")) + self.org_id = None # {{{ Authentication header self.headers = {"Content-Type": "application/json"} - if module.params.get('grafana_api_key', None): - self.headers["Authorization"] = "Bearer %s" % module.params['grafana_api_key'] + if module.params.get("grafana_api_key", None): + self.headers["Authorization"] = ( + "Bearer %s" % module.params["grafana_api_key"] + ) else: - self.headers["Authorization"] = basic_auth_header(module.params['url_username'], module.params['url_password']) + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) + self.org_id = ( + self.organization_by_name(module.params["org_name"]) + if module.params["org_name"] + else module.params["org_id"] + ) + self.switch_organization(self.org_id) # }}} - self.grafana_url = base.clean_url(module.params.get("url")) if module.params.get("skip_version_check") is False: try: grafana_version = self.get_version() except GrafanaError as e: 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") + self._module.fail_json( + failed=True, msg="Folders API is available starting Grafana v5" + ) def _send_request(self, url, data=None, headers=None, method="GET"): if data is not None: @@ -203,24 +230,51 @@ class GrafanaFolderInterface(object): headers = [] full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url(self._module, full_url, data=data, headers=headers, method=method) + resp, info = fetch_url( + self._module, full_url, data=data, headers=headers, method=method + ) status_code = info["status"] if status_code == 404: return None elif status_code == 401: - self._module.fail_json(failed=True, msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url)) + self._module.fail_json( + failed=True, + msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), + ) elif status_code == 403: self._module.fail_json(failed=True, msg="Permission Denied") elif status_code == 412: - error_msg = resp.read()['message'] + error_msg = resp.read()["message"] self._module.fail_json(failed=True, msg=error_msg) elif status_code == 200: - return self._module.from_json(resp.read()) - self._module.fail_json(failed=True, msg="Grafana Folders API answered with HTTP %d" % status_code) + # XXX: Grafana folders endpoint stopped sending back json in response for delete operations + # see https://github.com/grafana/grafana/issues/77673 + 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 + ) + + def switch_organization(self, org_id): + url = "/api/user/using/%d" % org_id + self._send_request(url, headers=self.headers, method="POST") + + def organization_by_name(self, org_name): + url = "/api/user/orgs" + organizations = self._send_request(url, headers=self.headers, method="GET") + orga = next((org for org in organizations if org["name"] == org_name)) + if orga: + return orga["orgId"] + + self._module.fail_json( + failed=True, msg="Current user isn't member of organization: %s" % org_name + ) def get_version(self): url = "/api/health" - response = self._send_request(url, data=None, headers=self.headers, method="GET") + response = self._send_request( + url, data=None, headers=self.headers, method="GET" + ) version = response.get("version") if version is not None: major, minor, rev = version.split(".") @@ -230,7 +284,9 @@ class GrafanaFolderInterface(object): def create_folder(self, title): url = "/api/folders" folder = dict(title=title) - response = self._send_request(url, data=folder, headers=self.headers, method="POST") + response = self._send_request( + url, data=folder, headers=self.headers, method="POST" + ) return response def get_folder(self, title): @@ -247,34 +303,33 @@ class GrafanaFolderInterface(object): return response -def setup_module_object(): +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"), + ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=False, - required_together=base.grafana_required_together(), - mutually_exclusive=base.grafana_mutually_exclusive(), + required_together=base.grafana_required_together() + + [["url_username", "url_password", "org_id"]], + mutually_exclusive=base.grafana_mutually_exclusive() + + [ + ["org_id", "org_name"], + ], ) - return module - - -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), -) - - -def main(): - - module = setup_module_object() - state = module.params['state'] - title = module.params['name'] + state = module.params["state"] + title = module.params["name"] + module.params["url"] = base.clean_url(module.params["url"]) grafana_iface = GrafanaFolderInterface(module) changed = False - if state == 'present': + if state == "present": folder = grafana_iface.get_folder(title) if folder is None: grafana_iface.create_folder(title) @@ -282,7 +337,7 @@ def main(): changed = True folder = grafana_iface.get_folder(title) module.exit_json(changed=changed, folder=folder) - elif state == 'absent': + elif state == "absent": folder = grafana_iface.get_folder(title) if folder is None: module.exit_json(changed=False, message="No folder found") @@ -290,5 +345,5 @@ def main(): module.exit_json(changed=True, message=result) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_notification_channel.py b/ansible_collections/community/grafana/plugins/modules/grafana_notification_channel.py index eb808fa1b..30b5b1124 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_notification_channel.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_notification_channel.py @@ -16,9 +16,10 @@ # along with Ansible. If not, see <http://www.gnu.org/licenses/>. from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: grafana_notification_channel notes: @@ -82,17 +83,17 @@ options: - Required when I(state) is C(present). is_default: type: bool - default: 'no' + default: false description: - Use this channel for all alerts. include_image: type: bool - default: 'no' + default: false description: - Capture a visualization image and attach it to notifications. disable_resolve_message: type: bool - default: 'no' + default: false description: - Disable the resolve message. reminder_frequency: @@ -368,10 +369,10 @@ options: extends_documentation_fragment: - community.grafana.basic_auth - community.grafana.api_key -''' +""" -EXAMPLES = ''' +EXAMPLES = """ - name: Create slack notification channel register: result grafana_notification_channel: @@ -391,9 +392,9 @@ EXAMPLES = ''' grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password}}" -''' +""" -RETURN = ''' +RETURN = """ notification_channel: description: Notification channel created or updated by the module. returned: changed @@ -416,14 +417,17 @@ notification_channel: "uid": "slack-oops", "updated": "2020-11-10T21:10:19.675308112+03:00" } -''' +""" import json from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.urls import fetch_url from ansible.module_utils._text import to_text -from ansible_collections.community.grafana.plugins.module_utils.base import grafana_argument_spec, clean_url +from ansible_collections.community.grafana.plugins.module_utils.base import ( + grafana_argument_spec, + clean_url, +) from ansible.module_utils.urls import basic_auth_header @@ -432,412 +436,448 @@ class GrafanaAPIException(Exception): def dingding_channel_payload(data, payload): - payload['settings']['url'] = data['dingding_url'] - if data.get('dingding_message_type'): - payload['settings']['msgType'] = { - 'link': 'link', - 'action_card': 'actionCard', - }[data['dingding_message_type']] + payload["settings"]["url"] = data["dingding_url"] + if data.get("dingding_message_type"): + payload["settings"]["msgType"] = { + "link": "link", + "action_card": "actionCard", + }[data["dingding_message_type"]] def discord_channel_payload(data, payload): - payload['settings']['url'] = data['discord_url'] - if data.get('discord_message_content'): - payload['settings']['content'] = data['discord_message_content'] + payload["settings"]["url"] = data["discord_url"] + if data.get("discord_message_content"): + payload["settings"]["content"] = data["discord_message_content"] def email_channel_payload(data, payload): - payload['settings']['addresses'] = ';'.join(data['email_addresses']) - if data.get('email_single'): - payload['settings']['singleEmail'] = data['email_single'] + payload["settings"]["addresses"] = ";".join(data["email_addresses"]) + if data.get("email_single"): + payload["settings"]["singleEmail"] = data["email_single"] def hipchat_channel_payload(data, payload): - payload['settings']['url'] = data['hipchat_url'] - if data.get('hipchat_api_key'): - payload['settings']['apiKey'] = data['hipchat_api_key'] - if data.get('hipchat_room_id'): - payload['settings']['roomid'] = data['hipchat_room_id'] + payload["settings"]["url"] = data["hipchat_url"] + if data.get("hipchat_api_key"): + payload["settings"]["apiKey"] = data["hipchat_api_key"] + if data.get("hipchat_room_id"): + payload["settings"]["roomid"] = data["hipchat_room_id"] def pagerduty_channel_payload(data, payload): - payload['settings']['integrationKey'] = data['pagerduty_integration_key'] - if data.get('pagerduty_severity'): - payload['settings']['severity'] = data['pagerduty_severity'] - if data.get('pagerduty_auto_resolve'): - payload['settings']['autoResolve'] = data['pagerduty_auto_resolve'] - if data.get('pagerduty_message_in_details'): - payload['settings']['messageInDetails'] = data['pagerduty_message_in_details'] + payload["settings"]["integrationKey"] = data["pagerduty_integration_key"] + if data.get("pagerduty_severity"): + payload["settings"]["severity"] = data["pagerduty_severity"] + if data.get("pagerduty_auto_resolve"): + payload["settings"]["autoResolve"] = data["pagerduty_auto_resolve"] + if data.get("pagerduty_message_in_details"): + payload["settings"]["messageInDetails"] = data["pagerduty_message_in_details"] def prometheus_channel_payload(data, payload): - payload['type'] = 'prometheus-alertmanager' - payload['settings']['url'] = data['prometheus_url'] - if data.get('prometheus_username'): - payload['settings']['basicAuthUser'] = data['prometheus_username'] - if data.get('prometheus_password'): - payload['settings']['basicAuthPassword'] = data['prometheus_password'] + payload["type"] = "prometheus-alertmanager" + payload["settings"]["url"] = data["prometheus_url"] + if data.get("prometheus_username"): + payload["settings"]["basicAuthUser"] = data["prometheus_username"] + if data.get("prometheus_password"): + payload["settings"]["basicAuthPassword"] = data["prometheus_password"] def pushover_channel_payload(data, payload): - payload['settings']['apiToken'] = data['pushover_api_token'] - payload['settings']['userKey'] = data['pushover_user_key'] - if data.get('pushover_devices'): - payload['settings']['device'] = ';'.join(data['pushover_devices']) - if data.get('pushover_priority'): - payload['settings']['priority'] = { - 'emergency': '2', - 'high': '1', - 'normal': '0', - 'low': '-1', - 'lowest': '-2' - }[data['pushover_priority']] - if data.get('pushover_retry'): - payload['settings']['retry'] = str(data['pushover_retry']) - if data.get('pushover_expire'): - payload['settings']['expire'] = str(data['pushover_expire']) - if data.get('pushover_alert_sound'): - payload['settings']['sound'] = data['pushover_alert_sound'] - if data.get('pushover_ok_sound'): - payload['settings']['okSound'] = data['pushover_ok_sound'] + payload["settings"]["apiToken"] = data["pushover_api_token"] + payload["settings"]["userKey"] = data["pushover_user_key"] + if data.get("pushover_devices"): + payload["settings"]["device"] = ";".join(data["pushover_devices"]) + if data.get("pushover_priority"): + payload["settings"]["priority"] = { + "emergency": "2", + "high": "1", + "normal": "0", + "low": "-1", + "lowest": "-2", + }[data["pushover_priority"]] + if data.get("pushover_retry"): + payload["settings"]["retry"] = str(data["pushover_retry"]) + if data.get("pushover_expire"): + payload["settings"]["expire"] = str(data["pushover_expire"]) + if data.get("pushover_alert_sound"): + payload["settings"]["sound"] = data["pushover_alert_sound"] + if data.get("pushover_ok_sound"): + payload["settings"]["okSound"] = data["pushover_ok_sound"] def sensu_channel_payload(data, payload): - payload['settings']['url'] = data['sensu_url'] - if data.get('sensu_source'): - payload['settings']['source'] = data['sensu_source'] - if data.get('sensu_handler'): - payload['settings']['handler'] = data['sensu_handler'] - if data.get('sensu_username'): - payload['settings']['username'] = data['sensu_username'] - if data.get('sensu_password'): - payload['settings']['password'] = data['sensu_password'] + payload["settings"]["url"] = data["sensu_url"] + if data.get("sensu_source"): + payload["settings"]["source"] = data["sensu_source"] + if data.get("sensu_handler"): + payload["settings"]["handler"] = data["sensu_handler"] + if data.get("sensu_username"): + payload["settings"]["username"] = data["sensu_username"] + if data.get("sensu_password"): + payload["settings"]["password"] = data["sensu_password"] def slack_channel_payload(data, payload): - payload['settings']['url'] = data['slack_url'] - if data.get('slack_recipient'): - payload['settings']['recipient'] = data['slack_recipient'] - if data.get('slack_username'): - payload['settings']['username'] = data['slack_username'] - if data.get('slack_icon_emoji'): - payload['settings']['iconEmoji'] = data['slack_icon_emoji'] - if data.get('slack_icon_url'): - payload['settings']['iconUrl'] = data['slack_icon_url'] - if data.get('slack_mention_users'): - payload['settings']['mentionUsers'] = ','.join(data['slack_mention_users']) - if data.get('slack_mention_groups'): - payload['settings']['mentionGroups'] = ','.join(data['slack_mention_groups']) - if data.get('slack_mention_channel'): - payload['settings']['mentionChannel'] = data['slack_mention_channel'] - if data.get('slack_token'): - payload['settings']['token'] = data['slack_token'] + payload["settings"]["url"] = data["slack_url"] + if data.get("slack_recipient"): + payload["settings"]["recipient"] = data["slack_recipient"] + if data.get("slack_username"): + payload["settings"]["username"] = data["slack_username"] + if data.get("slack_icon_emoji"): + payload["settings"]["iconEmoji"] = data["slack_icon_emoji"] + if data.get("slack_icon_url"): + payload["settings"]["iconUrl"] = data["slack_icon_url"] + if data.get("slack_mention_users"): + payload["settings"]["mentionUsers"] = ",".join(data["slack_mention_users"]) + if data.get("slack_mention_groups"): + payload["settings"]["mentionGroups"] = ",".join(data["slack_mention_groups"]) + if data.get("slack_mention_channel"): + payload["settings"]["mentionChannel"] = data["slack_mention_channel"] + if data.get("slack_token"): + payload["settings"]["token"] = data["slack_token"] def webhook_channel_payload(data, payload): - payload['settings']['url'] = data['webhook_url'] - if data.get('webhook_http_method'): - payload['settings']['httpMethod'] = data['webhook_http_method'] - if data.get('webhook_username'): - payload['settings']['username'] = data['webhook_username'] - if data.get('webhook_password'): - payload['settings']['password'] = data['webhook_password'] + payload["settings"]["url"] = data["webhook_url"] + if data.get("webhook_http_method"): + payload["settings"]["httpMethod"] = data["webhook_http_method"] + if data.get("webhook_username"): + payload["settings"]["username"] = data["webhook_username"] + if data.get("webhook_password"): + payload["settings"]["password"] = data["webhook_password"] def grafana_notification_channel_payload(data): payload = { - 'uid': data['uid'], - 'name': data['name'], - 'type': data['type'], - 'isDefault': data['is_default'], - 'disableResolveMessage': data['disable_resolve_message'], - 'settings': { - 'uploadImage': data['include_image'] - } + "uid": data["uid"], + "name": data["name"], + "type": data["type"], + "isDefault": data["is_default"], + "disableResolveMessage": data["disable_resolve_message"], + "settings": {"uploadImage": data["include_image"]}, } - if data.get('reminder_frequency'): - payload['sendReminder'] = True - payload['frequency'] = data['reminder_frequency'] + if data.get("reminder_frequency"): + payload["sendReminder"] = True + payload["frequency"] = data["reminder_frequency"] - if data['type'] == 'dingding': + if data["type"] == "dingding": dingding_channel_payload(data, payload) - elif data['type'] == 'discord': + elif data["type"] == "discord": discord_channel_payload(data, payload) - elif data['type'] == 'email': + elif data["type"] == "email": email_channel_payload(data, payload) - elif data['type'] == 'googlechat': - payload['settings']['url'] = data['googlechat_url'] - elif data['type'] == 'hipchat': + elif data["type"] == "googlechat": + payload["settings"]["url"] = data["googlechat_url"] + elif data["type"] == "hipchat": hipchat_channel_payload(data, payload) - elif data['type'] == 'kafka': - payload['settings']['kafkaRestProxy'] = data['kafka_url'] - payload['settings']['kafkaTopic'] = data['kafka_topic'] - elif data['type'] == 'line': - payload['settings']['token'] = data['line_token'] - elif data['type'] == 'teams': - payload['settings']['url'] = data['teams_url'] - elif data['type'] == 'opsgenie': - payload['settings']['apiUrl'] = data['opsgenie_url'] - payload['settings']['apiKey'] = data['opsgenie_api_key'] - elif data['type'] == 'pagerduty': + elif data["type"] == "kafka": + payload["settings"]["kafkaRestProxy"] = data["kafka_url"] + payload["settings"]["kafkaTopic"] = data["kafka_topic"] + elif data["type"] == "line": + payload["settings"]["token"] = data["line_token"] + elif data["type"] == "teams": + payload["settings"]["url"] = data["teams_url"] + elif data["type"] == "opsgenie": + payload["settings"]["apiUrl"] = data["opsgenie_url"] + payload["settings"]["apiKey"] = data["opsgenie_api_key"] + elif data["type"] == "pagerduty": pagerduty_channel_payload(data, payload) - elif data['type'] == 'prometheus': + elif data["type"] == "prometheus": prometheus_channel_payload(data, payload) - elif data['type'] == 'pushover': + elif data["type"] == "pushover": pushover_channel_payload(data, payload) - elif data['type'] == 'sensu': + elif data["type"] == "sensu": sensu_channel_payload(data, payload) - elif data['type'] == 'slack': + elif data["type"] == "slack": slack_channel_payload(data, payload) - elif data['type'] == 'telegram': - payload['settings']['bottoken'] = data['telegram_bot_token'] - payload['settings']['chatid'] = data['telegram_chat_id'] - elif data['type'] == 'threema': - payload['settings']['gateway_id'] = data['threema_gateway_id'] - payload['settings']['recipient_id'] = data['threema_recipient_id'] - payload['settings']['api_secret'] = data['threema_api_secret'] - elif data['type'] == 'victorops': - payload['settings']['url'] = data['victorops_url'] - if data.get('victorops_auto_resolve'): - payload['settings']['autoResolve'] = data['victorops_auto_resolve'] - elif data['type'] == 'webhook': + elif data["type"] == "telegram": + payload["settings"]["bottoken"] = data["telegram_bot_token"] + payload["settings"]["chatid"] = data["telegram_chat_id"] + elif data["type"] == "threema": + payload["settings"]["gateway_id"] = data["threema_gateway_id"] + payload["settings"]["recipient_id"] = data["threema_recipient_id"] + payload["settings"]["api_secret"] = data["threema_api_secret"] + elif data["type"] == "victorops": + payload["settings"]["url"] = data["victorops_url"] + if data.get("victorops_auto_resolve"): + payload["settings"]["autoResolve"] = data["victorops_auto_resolve"] + elif data["type"] == "webhook": webhook_channel_payload(data, payload) return payload class GrafanaNotificationChannelInterface(object): - def __init__(self, module): self._module = module # {{{ Authentication header self.headers = {"Content-Type": "application/json"} - if module.params.get('grafana_api_key', None): - self.headers["Authorization"] = "Bearer %s" % module.params['grafana_api_key'] + if module.params.get("grafana_api_key", None): + self.headers["Authorization"] = ( + "Bearer %s" % module.params["grafana_api_key"] + ) else: - self.headers["Authorization"] = basic_auth_header(module.params['url_username'], module.params['url_password']) + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) # }}} self.grafana_url = clean_url(module.params.get("url")) def grafana_switch_organisation(self, grafana_url, org_id): - r, info = fetch_url(self._module, '%s/api/user/using/%s' % (grafana_url, org_id), - headers=self.headers, method='POST') - if info['status'] != 200: - raise GrafanaAPIException('Unable to switch to organization %s : %s' % - (org_id, info)) + r, info = fetch_url( + self._module, + "%s/api/user/using/%s" % (grafana_url, org_id), + headers=self.headers, + method="POST", + ) + if info["status"] != 200: + raise GrafanaAPIException( + "Unable to switch to organization %s : %s" % (org_id, info) + ) def grafana_create_notification_channel(self, data, payload): - r, info = fetch_url(self._module, '%s/api/alert-notifications' % data['url'], - data=json.dumps(payload), headers=self.headers, method='POST') - if info['status'] == 200: + r, info = fetch_url( + self._module, + "%s/api/alert-notifications" % data["url"], + data=json.dumps(payload), + headers=self.headers, + method="POST", + ) + if info["status"] == 200: return { - 'state': 'present', - 'changed': True, - 'channel': json.loads(to_text(r.read())), + "state": "present", + "changed": True, + "channel": json.loads(to_text(r.read())), } else: - raise GrafanaAPIException("Unable to create notification channel: %s" % info) + raise GrafanaAPIException( + "Unable to create notification channel: %s" % info + ) def grafana_update_notification_channel(self, data, payload, before): - r, info = fetch_url(self._module, '%s/api/alert-notifications/uid/%s' % - (data['url'], data['uid']), - data=json.dumps(payload), headers=self.headers, method='PUT') - if info['status'] == 200: - del before['created'] - del before['updated'] + r, info = fetch_url( + self._module, + "%s/api/alert-notifications/uid/%s" % (data["url"], data["uid"]), + data=json.dumps(payload), + headers=self.headers, + method="PUT", + ) + if info["status"] == 200: + del before["created"] + del before["updated"] channel = json.loads(to_text(r.read())) after = channel.copy() - del after['created'] - del after['updated'] + del after["created"] + del after["updated"] if before == after: return { - 'changed': False, - 'channel': channel, + "changed": False, + "channel": channel, } else: return { - 'changed': True, - 'diff': { - 'before': before, - 'after': after, + "changed": True, + "diff": { + "before": before, + "after": after, }, - 'channel': channel, + "channel": channel, } else: - raise GrafanaAPIException("Unable to update notification channel %s : %s" % - (data['uid'], info)) + raise GrafanaAPIException( + "Unable to update notification channel %s : %s" % (data["uid"], info) + ) def grafana_create_or_update_notification_channel(self, data): payload = grafana_notification_channel_payload(data) - r, info = fetch_url(self._module, '%s/api/alert-notifications/uid/%s' % - (data['url'], data['uid']), headers=self.headers) - if info['status'] == 200: + r, info = fetch_url( + self._module, + "%s/api/alert-notifications/uid/%s" % (data["url"], data["uid"]), + headers=self.headers, + ) + if info["status"] == 200: before = json.loads(to_text(r.read())) return self.grafana_update_notification_channel(data, payload, before) - elif info['status'] == 404: + elif info["status"] == 404: return self.grafana_create_notification_channel(data, payload) else: - raise GrafanaAPIException("Unable to get notification channel %s : %s" % - (data['uid'], info)) + raise GrafanaAPIException( + "Unable to get notification channel %s : %s" % (data["uid"], info) + ) def grafana_delete_notification_channel(self, data): - r, info = fetch_url(self._module, '%s/api/alert-notifications/uid/%s' % - (data['url'], data['uid']), - headers=self.headers, method='DELETE') - if info['status'] == 200: - return { - 'state': 'absent', - 'changed': True - } - elif info['status'] == 404: - return { - 'changed': False - } + r, info = fetch_url( + self._module, + "%s/api/alert-notifications/uid/%s" % (data["url"], data["uid"]), + headers=self.headers, + method="DELETE", + ) + if info["status"] == 200: + return {"state": "absent", "changed": True} + elif info["status"] == 404: + return {"changed": False} else: - raise GrafanaAPIException("Unable to delete notification channel %s : %s" % - (data['uid'], info)) + raise GrafanaAPIException( + "Unable to delete notification channel %s : %s" % (data["uid"], info) + ) def main(): argument_spec = grafana_argument_spec() argument_spec.update( - org_id=dict(type='int', default=1), - uid=dict(type='str'), - name=dict(type='str'), - type=dict(type='str', - choices=['dingding', 'discord', 'email', 'googlechat', 'hipchat', - 'kafka', 'line', 'teams', 'opsgenie', 'pagerduty', - 'prometheus', 'pushover', 'sensu', 'slack', 'telegram', - 'threema', 'victorops', 'webhook']), - is_default=dict(type='bool', default=False), - include_image=dict(type='bool', default=False), - disable_resolve_message=dict(type='bool', default=False), - reminder_frequency=dict(type='str'), - - dingding_url=dict(type='str'), - dingding_message_type=dict(type='list', elements='str', - choices=['link', 'action_card']), - - discord_url=dict(type='str'), - discord_message_content=dict(type='str'), - - email_addresses=dict(type='list', elements='str'), - email_single=dict(type='bool'), - - googlechat_url=dict(type='str'), - - hipchat_url=dict(type='str'), - hipchat_api_key=dict(type='str', no_log=True), - hipchat_room_id=dict(type='str'), - - kafka_url=dict(type='str'), - kafka_topic=dict(type='str'), - - line_token=dict(type='str', no_log=True), - - teams_url=dict(type='str'), - - opsgenie_url=dict(type='str'), - opsgenie_api_key=dict(type='str', no_log=True), - opsgenie_auto_close=dict(type='bool'), - opsgenie_override_priority=dict(type='bool'), - - pagerduty_integration_key=dict(type='str', no_log=True), - pagerduty_severity=dict(type='list', elements='str', - choices=['critical', 'error', 'warning', 'info']), - pagerduty_auto_resolve=dict(type='bool'), - pagerduty_message_in_details=dict(type='bool'), - - prometheus_url=dict(type='str'), - prometheus_username=dict(type='str'), - prometheus_password=dict(type='str', no_log=True), - - pushover_api_token=dict(type='str', no_log=True), - pushover_user_key=dict(type='str', no_log=True), - pushover_devices=dict(type='list', elements='str'), - pushover_priority=dict(type='list', elements='str', - choices=['emergency', 'high', 'normal', 'low', 'lowest']), - pushover_retry=dict(type='int'), # TODO: only when priority==emergency - pushover_expire=dict(type='int'), # TODO: only when priority==emergency - pushover_alert_sound=dict(type='str'), # TODO: add sound choices - pushover_ok_sound=dict(type='str'), # TODO: add sound choices - - sensu_url=dict(type='str'), - sensu_source=dict(type='str'), - sensu_handler=dict(type='str'), - sensu_username=dict(type='str'), - sensu_password=dict(type='str', no_log=True), - - slack_url=dict(type='str', no_log=True), - slack_recipient=dict(type='str'), - slack_username=dict(type='str'), - slack_icon_emoji=dict(type='str'), - slack_icon_url=dict(type='str'), - slack_mention_users=dict(type='list', elements='str'), - slack_mention_groups=dict(type='list', elements='str'), - slack_mention_channel=dict(type='list', elements='str', - choices=['here', 'channel']), - slack_token=dict(type='str', no_log=True), - - telegram_bot_token=dict(type='str', no_log=True), - telegram_chat_id=dict(type='str'), - - threema_gateway_id=dict(type='str'), - threema_recipient_id=dict(type='str'), - threema_api_secret=dict(type='str', no_log=True), - - victorops_url=dict(type='str'), - victorops_auto_resolve=dict(type='bool'), - - webhook_url=dict(type='str'), - webhook_username=dict(type='str'), - webhook_password=dict(type='str', no_log=True), - webhook_http_method=dict(type='list', elements='str', choices=['POST', 'PUT']) + org_id=dict(type="int", default=1), + uid=dict(type="str"), + name=dict(type="str"), + type=dict( + type="str", + choices=[ + "dingding", + "discord", + "email", + "googlechat", + "hipchat", + "kafka", + "line", + "teams", + "opsgenie", + "pagerduty", + "prometheus", + "pushover", + "sensu", + "slack", + "telegram", + "threema", + "victorops", + "webhook", + ], + ), + is_default=dict(type="bool", default=False), + include_image=dict(type="bool", default=False), + disable_resolve_message=dict(type="bool", default=False), + reminder_frequency=dict(type="str"), + dingding_url=dict(type="str"), + dingding_message_type=dict( + type="list", elements="str", choices=["link", "action_card"] + ), + discord_url=dict(type="str"), + discord_message_content=dict(type="str"), + email_addresses=dict(type="list", elements="str"), + email_single=dict(type="bool"), + googlechat_url=dict(type="str"), + hipchat_url=dict(type="str"), + hipchat_api_key=dict(type="str", no_log=True), + hipchat_room_id=dict(type="str"), + kafka_url=dict(type="str"), + kafka_topic=dict(type="str"), + line_token=dict(type="str", no_log=True), + teams_url=dict(type="str"), + opsgenie_url=dict(type="str"), + opsgenie_api_key=dict(type="str", no_log=True), + opsgenie_auto_close=dict(type="bool"), + opsgenie_override_priority=dict(type="bool"), + pagerduty_integration_key=dict(type="str", no_log=True), + pagerduty_severity=dict( + type="list", + elements="str", + choices=["critical", "error", "warning", "info"], + ), + pagerduty_auto_resolve=dict(type="bool"), + pagerduty_message_in_details=dict(type="bool"), + prometheus_url=dict(type="str"), + prometheus_username=dict(type="str"), + prometheus_password=dict(type="str", no_log=True), + pushover_api_token=dict(type="str", no_log=True), + pushover_user_key=dict(type="str", no_log=True), + pushover_devices=dict(type="list", elements="str"), + pushover_priority=dict( + type="list", + elements="str", + choices=["emergency", "high", "normal", "low", "lowest"], + ), + pushover_retry=dict(type="int"), # TODO: only when priority==emergency + pushover_expire=dict(type="int"), # TODO: only when priority==emergency + pushover_alert_sound=dict(type="str"), # TODO: add sound choices + pushover_ok_sound=dict(type="str"), # TODO: add sound choices + sensu_url=dict(type="str"), + sensu_source=dict(type="str"), + sensu_handler=dict(type="str"), + sensu_username=dict(type="str"), + sensu_password=dict(type="str", no_log=True), + slack_url=dict(type="str", no_log=True), + slack_recipient=dict(type="str"), + slack_username=dict(type="str"), + slack_icon_emoji=dict(type="str"), + slack_icon_url=dict(type="str"), + slack_mention_users=dict(type="list", elements="str"), + slack_mention_groups=dict(type="list", elements="str"), + slack_mention_channel=dict( + type="list", elements="str", choices=["here", "channel"] + ), + slack_token=dict(type="str", no_log=True), + telegram_bot_token=dict(type="str", no_log=True), + telegram_chat_id=dict(type="str"), + threema_gateway_id=dict(type="str"), + threema_recipient_id=dict(type="str"), + threema_api_secret=dict(type="str", no_log=True), + victorops_url=dict(type="str"), + victorops_auto_resolve=dict(type="bool"), + webhook_url=dict(type="str"), + webhook_username=dict(type="str"), + webhook_password=dict(type="str", no_log=True), + webhook_http_method=dict(type="list", elements="str", choices=["POST", "PUT"]), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=False, - required_together=[['url_username', 'url_password', 'org_id'], - ['prometheus_username', 'prometheus_password'], - ['sensu_username', 'sensu_password']], - mutually_exclusive=[['url_username', 'grafana_api_key']], + required_together=[ + ["url_username", "url_password", "org_id"], + ["prometheus_username", "prometheus_password"], + ["sensu_username", "sensu_password"], + ], + mutually_exclusive=[["url_username", "grafana_api_key"]], required_if=[ - ['state', 'present', ['name', 'type']], - ['type', 'dingding', ['dingding_url']], - ['type', 'discord', ['discord_url']], - ['type', 'email', ['email_addresses']], - ['type', 'googlechat', ['googlechat_url']], - ['type', 'hipchat', ['hipchat_url']], - ['type', 'kafka', ['kafka_url', 'kafka_topic']], - ['type', 'line', ['line_token']], - ['type', 'teams', ['teams_url']], - ['type', 'opsgenie', ['opsgenie_url', 'opsgenie_api_key']], - ['type', 'pagerduty', ['pagerduty_integration_key']], - ['type', 'prometheus', ['prometheus_url']], - ['type', 'pushover', ['pushover_api_token', 'pushover_user_key']], - ['type', 'sensu', ['sensu_url']], - ['type', 'slack', ['slack_url']], - ['type', 'telegram', ['telegram_bot_token', 'telegram_chat_id']], - ['type', 'threema', ['threema_gateway_id', 'threema_recipient_id', - 'threema_api_secret']], - ['type', 'victorops', ['victorops_url']], - ['type', 'webhook', ['webhook_url']] - ] + ["state", "present", ["name", "type"]], + ["type", "dingding", ["dingding_url"]], + ["type", "discord", ["discord_url"]], + ["type", "email", ["email_addresses"]], + ["type", "googlechat", ["googlechat_url"]], + ["type", "hipchat", ["hipchat_url"]], + ["type", "kafka", ["kafka_url", "kafka_topic"]], + ["type", "line", ["line_token"]], + ["type", "teams", ["teams_url"]], + ["type", "opsgenie", ["opsgenie_url", "opsgenie_api_key"]], + ["type", "pagerduty", ["pagerduty_integration_key"]], + ["type", "prometheus", ["prometheus_url"]], + ["type", "pushover", ["pushover_api_token", "pushover_user_key"]], + ["type", "sensu", ["sensu_url"]], + ["type", "slack", ["slack_url"]], + ["type", "telegram", ["telegram_bot_token", "telegram_chat_id"]], + [ + "type", + "threema", + ["threema_gateway_id", "threema_recipient_id", "threema_api_secret"], + ], + ["type", "victorops", ["victorops_url"]], + ["type", "webhook", ["webhook_url"]], + ], ) module.params["url"] = clean_url(module.params["url"]) alert_channel_iface = GrafanaNotificationChannelInterface(module) - if module.params['state'] == 'present': - result = alert_channel_iface.grafana_create_or_update_notification_channel(module.params) + if module.params["state"] == "present": + result = alert_channel_iface.grafana_create_or_update_notification_channel( + module.params + ) module.exit_json(failed=False, **result) else: result = alert_channel_iface.grafana_delete_notification_channel(module.params) module.exit_json(failed=False, **result) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_organization.py b/ansible_collections/community/grafana/plugins/modules/grafana_organization.py index 7fad4c876..7bddcb3f4 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_organization.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_organization.py @@ -19,7 +19,7 @@ from __future__ import absolute_import, division, print_function -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: grafana_organization author: @@ -43,9 +43,9 @@ options: choices: ["present", "absent"] extends_documentation_fragment: - community.grafana.basic_auth -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- - name: Create a Grafana organization community.grafana.grafana_organization: @@ -62,9 +62,9 @@ EXAMPLES = ''' url_password: changeme name: orgtest state: absent -''' +""" -RETURN = ''' +RETURN = """ --- org: description: Information about the organization @@ -94,7 +94,7 @@ org: country: "" state: "" zipCode: "" -''' +""" import json @@ -107,12 +107,13 @@ __metaclass__ = type class GrafanaOrgInterface(object): - def __init__(self, module): self._module = module # {{{ Authentication header self.headers = {"Content-Type": "application/json"} - self.headers["Authorization"] = basic_auth_header(module.params['url_username'], module.params['url_password']) + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) # }}} self.grafana_url = base.clean_url(module.params.get("url")) @@ -123,20 +124,35 @@ class GrafanaOrgInterface(object): headers = [] full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url(self._module, full_url, data=data, headers=headers, method=method) + resp, info = fetch_url( + self._module, full_url, data=data, headers=headers, method=method + ) status_code = info["status"] if status_code == 404: return None elif status_code == 401: - self._module.fail_json(failed=True, msg="Unauthorized to perform action '%s' on '%s' header: %s" % (method, full_url, self.headers)) + self._module.fail_json( + failed=True, + msg="Unauthorized to perform action '%s' on '%s' header: %s" + % (method, full_url, self.headers), + ) elif status_code == 403: self._module.fail_json(failed=True, msg="Permission Denied") elif status_code == 200: return self._module.from_json(resp.read()) if resp is None: - self._module.fail_json(failed=True, msg="Cannot connect to API Grafana %s" % info['msg'], status=status_code, url=info['url']) + self._module.fail_json( + failed=True, + msg="Cannot connect to API Grafana %s" % info["msg"], + status=status_code, + url=info["url"], + ) else: - self._module.fail_json(failed=True, msg="Grafana Org API answered with HTTP %d" % status_code, body=self._module.from_json(resp.read())) + self._module.fail_json( + failed=True, + msg="Grafana Org API answered with HTTP %d" % status_code, + body=self._module.from_json(resp.read()), + ) def get_actual_org(self, name): # https://grafana.com/docs/grafana/latest/http_api/org/#get-organization-by-name @@ -160,40 +176,48 @@ def setup_module_object(): module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=False, - required_together=base.grafana_required_together() + required_together=base.grafana_required_together(), ) return module argument_spec = base.grafana_argument_spec() argument_spec.update( - state=dict(choices=['present', 'absent'], default='present'), - name=dict(type='str', required=True), + state=dict(choices=["present", "absent"], default="present"), + name=dict(type="str", required=True), ) -argument_spec.pop('grafana_api_key') +argument_spec.pop("grafana_api_key") def main(): module = setup_module_object() - state = module.params['state'] - name = module.params['name'] + state = module.params["state"] + name = module.params["name"] grafana_iface = GrafanaOrgInterface(module) # search org by name actual_org = grafana_iface.get_actual_org(name) - if state == 'present': + if state == "present": has_changed = False if actual_org is None: # create new org actual_org = grafana_iface.create_org(name) has_changed = True - module.exit_json(changed=has_changed, msg='Organization %s created.' % name, org=actual_org) + module.exit_json( + changed=has_changed, + msg="Organization %s created." % name, + org=actual_org, + ) else: - module.exit_json(changed=has_changed, msg='Organization %s already created.' % name, org=actual_org) + module.exit_json( + changed=has_changed, + msg="Organization %s already created." % name, + org=actual_org, + ) - elif state == 'absent': + elif state == "absent": if actual_org is None: module.exit_json(msg="No org found, nothing to do") # delete org @@ -201,5 +225,5 @@ def main(): module.exit_json(changed=True, msg=result.get("message")) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_organization_user.py b/ansible_collections/community/grafana/plugins/modules/grafana_organization_user.py new file mode 100644 index 000000000..11171f38f --- /dev/null +++ b/ansible_collections/community/grafana/plugins/modules/grafana_organization_user.py @@ -0,0 +1,294 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright: (c) 2021 + +from __future__ import absolute_import, division, print_function + +DOCUMENTATION = """ +--- +module: grafana_organization_user +author: + - Aliaksandr Mianzhynski (@amenzhinsky) +version_added: "1.6.0" +short_description: Manage Grafana Organization Users. +description: + - Add or remove users or change their roles in Grafana organizations through org API. + - The user has to exist before using this module. See U(https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_user_module.html). +options: + login: + type: str + required: True + description: + - Username or email. + role: + type: str + choices: + - viewer + - editor + - admin + default: viewer + description: + - User's role in the organization. + state: + type: str + default: present + choices: + - present + - absent + description: + - Status of a user's organization membership. + org_id: + type: int + default: 1 + description: + - Organization ID. + - Mutually exclusive with C(org_name). + org_name: + type: str + description: + - Organization name. + - Mutually exclusive with C(org_id). + +extends_documentation_fragment: + - community.grafana.basic_auth +""" + +EXAMPLES = """ +--- +- name: Add user to organization + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: john + role: admin + +- name: Remove user from organization + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: john + state: absent +""" + +RETURN = """ +--- +user: + description: Information about the organization user + returned: when state present + type: complex + contains: + email: + description: The User email address + returned: always + type: str + sample: + - "foo.bar@example.com" + login: + description: The User login + returned: always + type: str + sample: + - "batman" + name: + description: The User name (same as login) + returned: always + type: str + sample: + - "batman" + orgId: + description: The organization id that the team is part of. + returned: always + type: int + sample: + - 1 + role: + description: The user role in the organization + returned: always + type: str + choices: + - Viewer + - Editor + - Admin + sample: + - Viewer +""" + + +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import fetch_url +from ansible.module_utils._text import to_text +from ansible_collections.community.grafana.plugins.module_utils.base import ( + grafana_argument_spec, + clean_url, +) +from ansible.module_utils.urls import basic_auth_header + +__metaclass__ = type + + +class GrafanaAPIException(Exception): + pass + + +class GrafanaOrganizationUserInterface(object): + def __init__(self, module): + self._module = module + # {{{ Authentication header + self.headers = {"Content-Type": "application/json"} + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) + # }}} + self.grafana_url = clean_url(module.params.get("url")) + + def _api_call(self, method, path, payload): + data = None + if payload: + data = json.dumps(payload) + return fetch_url( + self._module, + self.grafana_url + "/api/" + path, + headers=self.headers, + method=method, + data=data, + ) + + def _organization_by_name(self, org_name): + r, info = self._api_call("GET", "orgs/name/%s" % org_name, None) + if info["status"] != 200: + raise GrafanaAPIException("Unable to retrieve organization: %s" % info) + return json.loads(to_text(r.read())) + + def _organization_users(self, org_id): + r, info = self._api_call("GET", "orgs/%d/users" % org_id, None) + if info["status"] != 200: + raise GrafanaAPIException( + "Unable to retrieve organization users: %s" % info + ) + return json.loads(to_text(r.read())) + + def _create_organization_user(self, org_id, login, role): + return self._api_call( + "POST", + "orgs/%d/users" % org_id, + { + "loginOrEmail": login, + "role": role, + }, + ) + + def _update_organization_user_role(self, org_id, user_id, role): + return self._api_call( + "PATCH", + "orgs/%d/users/%s" % (org_id, user_id), + { + "role": role, + }, + ) + + def _remove_organization_user(self, org_id, user_id): + return self._api_call("DELETE", "orgs/%d/users/%s" % (org_id, user_id), None) + + def _organization_user_by_login(self, org_id, login): + for user in self._organization_users(org_id): + if login in (user["login"], user["email"]): + return user + + def create_or_update_user(self, org_id, login, role): + r, info = self._create_organization_user(org_id, login, role) + if info["status"] == 200: + return { + "state": "present", + "changed": True, + "user": self._organization_user_by_login(org_id, login), + } + if info["status"] == 409: # already member + user = self._organization_user_by_login(org_id, login) + if not user: + raise Exception("[BUG] User not found in organization") + + if user["role"] == role: + return {"changed": False} + + r, info = self._update_organization_user_role(org_id, user["userId"], role) + if info["status"] == 200: + return { + "changed": True, + "user": self._organization_user_by_login(org_id, login), + } + else: + raise GrafanaAPIException( + "Unable to update organization user: %s" % info + ) + else: + raise GrafanaAPIException("Unable to add user to organization: %s" % info) + + def remove_user(self, org_id, login): + user = self._organization_user_by_login(org_id, login) + if not user: + return {"changed": False} + + r, info = self._remove_organization_user(org_id, user["userId"]) + if info["status"] == 200: + return {"state": "absent", "changed": True} + else: + raise GrafanaAPIException("Unable to delete organization user: %s" % info) + + +def main(): + argument_spec = grafana_argument_spec() + argument_spec.pop("grafana_api_key") + argument_spec.update( + org_id=dict(type="int", default=1), + org_name=dict(type="str"), + login=dict(type="str", required=True), + role=dict(type="str", choices=["viewer", "editor", "admin"], default="viewer"), + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + mutually_exclusive=[ + ("org_id", "org_name"), + ], + required_if=[ + ["state", "present", ["role"]], + ], + ) + + org_id = module.params["org_id"] + login = module.params["login"] + iface = GrafanaOrganizationUserInterface(module) + if module.params["org_name"]: + org_name = module.params["org_name"] + organization = iface._organization_by_name(org_name) + org_id = organization["id"] + if module.params["state"] == "present": + role = module.params["role"].capitalize() + result = iface.create_or_update_user(org_id, login, role) + module.exit_json(failed=False, **result) + else: + result = iface.remove_user(org_id, login) + module.exit_json(failed=False, **result) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py b/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py index 7fd418760..c510f02ba 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py @@ -6,7 +6,7 @@ from __future__ import absolute_import, division, print_function -DOCUMENTATION = '''module: grafana_plugin +DOCUMENTATION = """module: grafana_plugin author: - Thierry Sallé (@seuf) short_description: Manage Grafana plugins via grafana-cli @@ -50,26 +50,26 @@ options: validate_certs: description: - Boolean variable to include --insecure while installing pluging - default: False + default: false type: bool -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- - name: Install/update Grafana piechart panel plugin community.grafana.grafana_plugin: name: grafana-piechart-panel version: latest state: present -''' +""" -RETURN = ''' +RETURN = """ --- version: description: version of the installed/removed/updated plugin. type: str returned: always -''' +""" import os from ansible.module_utils.basic import AnsibleModule @@ -82,18 +82,18 @@ class GrafanaCliException(Exception): def parse_version(string): - name, version = string.split('@') + name, version = string.split("@") return name.strip(), version.strip() def grafana_cli_bin(params): - ''' + """ Get the grafana-cli binary path with global options. Raise a GrafanaCliException if the grafana-cli is not present or not in PATH :param params: ansible module params. Used to fill grafana-cli global params. - ''' - program = 'grafana-cli' + """ + program = "grafana-cli" grafana_cli = None def is_exe(fpath): @@ -112,50 +112,57 @@ def grafana_cli_bin(params): break if grafana_cli is None: - raise GrafanaCliException('grafana-cli binary is not present or not in PATH') + raise GrafanaCliException("grafana-cli binary is not present or not in PATH") else: - if 'grafana_plugin_url' in params and params['grafana_plugin_url']: - grafana_cli = '{0} {1} {2}'.format(grafana_cli, '--pluginUrl', params['grafana_plugin_url']) - if 'grafana_plugins_dir' in params and params['grafana_plugins_dir']: - grafana_cli = '{0} {1} {2}'.format(grafana_cli, '--pluginsDir', params['grafana_plugins_dir']) - if 'grafana_repo' in params and params['grafana_repo']: - grafana_cli = '{0} {1} {2}'.format(grafana_cli, '--repo', params['grafana_repo']) - if 'validate_certs' in params and params['validate_certs'] is False: - grafana_cli = '{0} {1}'.format(grafana_cli, '--insecure') - - return '{0} {1}'.format(grafana_cli, 'plugins') + if "grafana_plugin_url" in params and params["grafana_plugin_url"]: + grafana_cli = "{0} {1} {2}".format( + grafana_cli, "--pluginUrl", params["grafana_plugin_url"] + ) + if "grafana_plugins_dir" in params and params["grafana_plugins_dir"]: + grafana_cli = "{0} {1} {2}".format( + grafana_cli, "--pluginsDir", params["grafana_plugins_dir"] + ) + if "grafana_repo" in params and params["grafana_repo"]: + grafana_cli = "{0} {1} {2}".format( + grafana_cli, "--repo", params["grafana_repo"] + ) + if "validate_certs" in params and params["validate_certs"] is False: + grafana_cli = "{0} {1}".format(grafana_cli, "--insecure") + + return "{0} {1}".format(grafana_cli, "plugins") def get_grafana_plugin_version(module, params): - ''' + """ Fetch grafana installed plugin version. Return None if plugin is not installed. :param module: ansible module object. used to run system commands. :param params: ansible module params. - ''' + """ grafana_cli = grafana_cli_bin(params) - rc, stdout, stderr = module.run_command('{0} ls'.format(grafana_cli)) + rc, stdout, stderr = module.run_command("{0} ls".format(grafana_cli)) stdout_lines = stdout.split("\n") for line in stdout_lines: - if line.find(' @ ') != -1: + if line.find(" @ ") != -1: line = line.rstrip() plugin_name, plugin_version = parse_version(line) - if plugin_name == params['name']: + if plugin_name == params["name"]: return plugin_version return None def get_grafana_plugin_version_latest(module, params): - ''' + """ Fetch the latest version available from grafana-cli. Return the newest version number or None not found. :param module: ansible module object. used to run system commands. :param params: ansible module params. - ''' + """ grafana_cli = grafana_cli_bin(params) - rc, stdout, stderr = module.run_command('{0} list-versions {1}'.format(grafana_cli, - params['name'])) + rc, stdout, stderr = module.run_command( + "{0} list-versions {1}".format(grafana_cli, params["name"]) + ) stdout_lines = stdout.split("\n") if stdout_lines[0]: return stdout_lines[0].rstrip() @@ -163,108 +170,126 @@ def get_grafana_plugin_version_latest(module, params): def grafana_plugin(module, params): - ''' + """ Install update or remove grafana plugin :param module: ansible module object. used to run system commands. :param params: ansible module params. - ''' + """ grafana_cli = grafana_cli_bin(params) - if params['state'] == 'present': + if params["state"] == "present": grafana_plugin_version = get_grafana_plugin_version(module, params) if grafana_plugin_version is not None: - if 'version' in params and params['version']: - if params['version'] == grafana_plugin_version: - return {'msg': 'Grafana plugin already installed', - 'changed': False, - 'version': grafana_plugin_version} + if "version" in params and params["version"]: + if params["version"] == grafana_plugin_version: + return { + "msg": "Grafana plugin already installed", + "changed": False, + "version": grafana_plugin_version, + } else: - if params['version'] == 'latest' or params['version'] is None: - latest_version = get_grafana_plugin_version_latest(module, params) + if params["version"] == "latest" or params["version"] is None: + latest_version = get_grafana_plugin_version_latest( + module, params + ) if latest_version == grafana_plugin_version: - return {'msg': 'Grafana plugin already installed', - 'changed': False, - 'version': grafana_plugin_version} - cmd = '{0} update {1}'.format(grafana_cli, params['name']) + return { + "msg": "Grafana plugin already installed", + "changed": False, + "version": grafana_plugin_version, + } + cmd = "{0} update {1}".format(grafana_cli, params["name"]) else: - cmd = '{0} install {1} {2}'.format(grafana_cli, params['name'], params['version']) + cmd = "{0} install {1} {2}".format( + grafana_cli, params["name"], params["version"] + ) else: - return {'msg': 'Grafana plugin already installed', - 'changed': False, - 'version': grafana_plugin_version} + return { + "msg": "Grafana plugin already installed", + "changed": False, + "version": grafana_plugin_version, + } else: - if 'version' in params: - if params['version'] == 'latest' or params['version'] is None: - cmd = '{0} install {1}'.format(grafana_cli, params['name']) + if "version" in params: + if params["version"] == "latest" or params["version"] is None: + cmd = "{0} install {1}".format(grafana_cli, params["name"]) else: - cmd = '{0} install {1} {2}'.format(grafana_cli, params['name'], params['version']) + cmd = "{0} install {1} {2}".format( + grafana_cli, params["name"], params["version"] + ) else: - cmd = '{0} install {1}'.format(grafana_cli, params['name']) + cmd = "{0} install {1}".format(grafana_cli, params["name"]) else: - cmd = '{0} uninstall {1}'.format(grafana_cli, params['name']) + cmd = "{0} uninstall {1}".format(grafana_cli, params["name"]) rc, stdout, stderr = module.run_command(cmd) if rc == 0: stdout_lines = stdout.split("\n") for line in stdout_lines: - if line.find(params['name']): - if line.find(' @ ') != -1: + if line.find(params["name"]): + if line.find(" @ ") != -1: line = line.rstrip() plugin_name, plugin_version = parse_version(line) else: plugin_version = None - if params['state'] == 'present': - return {'msg': 'Grafana plugin {0} installed : {1}'.format(params['name'], cmd), - 'changed': True, - 'version': plugin_version} + if params["state"] == "present": + return { + "msg": "Grafana plugin {0} installed : {1}".format( + params["name"], cmd + ), + "changed": True, + "version": plugin_version, + } else: - return {'msg': 'Grafana plugin {0} uninstalled : {1}'.format(params['name'], cmd), - 'changed': True} + return { + "msg": "Grafana plugin {0} uninstalled : {1}".format( + params["name"], cmd + ), + "changed": True, + } else: - if params['state'] == 'absent' and stdout.find("plugin does not exist"): - return {'msg': 'Grafana plugin {0} already uninstalled : {1}'.format(params['name'], cmd), 'changed': False} - raise GrafanaCliException("'{0}' execution returned an error : [{1}] {2} {3}".format(cmd, rc, stdout, stderr)) + if params["state"] == "absent" and stdout.find("plugin does not exist"): + return { + "msg": "Grafana plugin {0} already uninstalled : {1}".format( + params["name"], cmd + ), + "changed": False, + } + raise GrafanaCliException( + "'{0}' execution returned an error : [{1}] {2} {3}".format( + cmd, rc, stdout, stderr + ) + ) def main(): module = AnsibleModule( argument_spec=dict( - name=dict(required=True, - type='str'), - version=dict(type='str'), - grafana_plugins_dir=dict(type='str'), - grafana_repo=dict(type='str'), - grafana_plugin_url=dict(type='str'), - validate_certs=dict(type='bool', default=False), - state=dict(choices=['present', 'absent'], - default='present') + name=dict(required=True, type="str"), + version=dict(type="str"), + grafana_plugins_dir=dict(type="str"), + grafana_repo=dict(type="str"), + grafana_plugin_url=dict(type="str"), + validate_certs=dict(type="bool", default=False), + state=dict(choices=["present", "absent"], default="present"), ), - supports_check_mode=False + supports_check_mode=False, ) try: result = grafana_plugin(module, module.params) except GrafanaCliException as e: - module.fail_json( - failed=True, - msg="{0}".format(e) - ) + module.fail_json(failed=True, msg="{0}".format(e)) return except Exception as e: - module.fail_json( - failed=True, - msg="{0} : {1} ".format(type(e), e) - ) + module.fail_json(failed=True, msg="{0} : {1} ".format(type(e), e)) return - module.exit_json( - failed=False, - **result - ) + module.exit_json(failed=False, **result) return -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_team.py b/ansible_collections/community/grafana/plugins/modules/grafana_team.py index 7f8de8457..76dc70a62 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_team.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_team.py @@ -19,7 +19,7 @@ from __future__ import absolute_import, division, print_function -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: grafana_team author: @@ -59,22 +59,22 @@ options: description: - Delete the members not found in the C(members) parameters from the - list of members found on the Team. - default: False + default: false type: bool skip_version_check: description: - Skip Grafana version check and try to reach api endpoint anyway. - - This parameter can be useful if you enabled `hide_version` in grafana.ini + - This parameter can be useful if you enabled C(hide_version) in grafana.ini required: False type: bool - default: False + default: false version_added: "1.2.0" extends_documentation_fragment: - community.grafana.basic_auth - community.grafana.api_key -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- - name: Create a team community.grafana.grafana_team: @@ -104,7 +104,7 @@ EXAMPLES = ''' members: - john.doe@example.com - jane.doe@example.com - enforce_members: yes + enforce_members: true state: present - name: Delete a team @@ -114,9 +114,9 @@ EXAMPLES = ''' name: "grafana_working_group" email: "foo.bar@example.com" state: absent -''' +""" -RETURN = ''' +RETURN = """ --- team: description: Information about the Team @@ -165,7 +165,7 @@ team: type: int sample: - 1 -''' +""" import json @@ -183,15 +183,18 @@ class GrafanaError(Exception): class GrafanaTeamInterface(object): - def __init__(self, module): self._module = module # {{{ Authentication header self.headers = {"Content-Type": "application/json"} - if module.params.get('grafana_api_key', None): - self.headers["Authorization"] = "Bearer %s" % module.params['grafana_api_key'] + if module.params.get("grafana_api_key", None): + self.headers["Authorization"] = ( + "Bearer %s" % module.params["grafana_api_key"] + ) else: - self.headers["Authorization"] = basic_auth_header(module.params['url_username'], module.params['url_password']) + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) # }}} self.grafana_url = base.clean_url(module.params.get("url")) if module.params.get("skip_version_check") is False: @@ -200,7 +203,9 @@ class GrafanaTeamInterface(object): except GrafanaError as e: self._module.fail_json(failed=True, msg=to_text(e)) if grafana_version["major"] < 5: - self._module.fail_json(failed=True, msg="Teams API is available starting Grafana v5") + self._module.fail_json( + failed=True, msg="Teams API is available starting Grafana v5" + ) def _send_request(self, url, data=None, headers=None, method="GET"): if data is not None: @@ -209,23 +214,32 @@ class GrafanaTeamInterface(object): headers = [] full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url(self._module, full_url, data=data, headers=headers, method=method) + resp, info = fetch_url( + self._module, full_url, data=data, headers=headers, method=method + ) status_code = info["status"] if status_code == 404: return None elif status_code == 401: - self._module.fail_json(failed=True, msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url)) + self._module.fail_json( + failed=True, + msg="Unauthorized to perform action '%s' on '%s'" % (method, full_url), + ) elif status_code == 403: self._module.fail_json(failed=True, msg="Permission Denied") elif status_code == 409: self._module.fail_json(failed=True, msg="Team name is taken") elif status_code == 200: return self._module.from_json(resp.read()) - self._module.fail_json(failed=True, msg="Grafana Teams API answered with HTTP %d" % status_code) + self._module.fail_json( + failed=True, msg="Grafana Teams API answered with HTTP %d" % status_code + ) def get_version(self): url = "/api/health" - response = self._send_request(url, data=None, headers=self.headers, method="GET") + response = self._send_request( + url, data=None, headers=self.headers, method="GET" + ) version = response.get("version") if version is not None: major, minor, rev = version.split(".") @@ -235,7 +249,9 @@ class GrafanaTeamInterface(object): def create_team(self, name, email): url = "/api/teams" team = dict(email=email, name=name) - response = self._send_request(url, data=team, headers=self.headers, method="POST") + response = self._send_request( + url, data=team, headers=self.headers, method="POST" + ) return response def get_team(self, name): @@ -251,7 +267,9 @@ class GrafanaTeamInterface(object): def update_team(self, team_id, name, email): url = "/api/teams/{team_id}".format(team_id=team_id) team = dict(email=email, name=name) - response = self._send_request(url, data=team, headers=self.headers, method="PUT") + response = self._send_request( + url, data=team, headers=self.headers, method="PUT" + ) return response def delete_team(self, team_id): @@ -272,7 +290,9 @@ class GrafanaTeamInterface(object): def delete_team_member(self, team_id, email): user_id = self.get_user_id_from_mail(email) - url = "/api/teams/{team_id}/members/{user_id}".format(team_id=team_id, user_id=user_id) + url = "/api/teams/{team_id}/members/{user_id}".format( + team_id=team_id, user_id=user_id + ) self._send_request(url, headers=self.headers, method="DELETE") def get_user_id_from_mail(self, email): @@ -295,27 +315,26 @@ def setup_module_object(): argument_spec = base.grafana_argument_spec() argument_spec.update( - name=dict(type='str', required=True), - email=dict(type='str', required=True), - members=dict(type='list', elements='str', required=False), - enforce_members=dict(type='bool', default=False), - skip_version_check=dict(type='bool', default=False), + name=dict(type="str", required=True), + email=dict(type="str", required=True), + members=dict(type="list", elements="str", required=False), + enforce_members=dict(type="bool", default=False), + skip_version_check=dict(type="bool", default=False), ) def main(): - module = setup_module_object() - state = module.params['state'] - name = module.params['name'] - email = module.params['email'] - members = module.params['members'] - enforce_members = module.params['enforce_members'] + state = module.params["state"] + name = module.params["name"] + email = module.params["email"] + members = module.params["members"] + enforce_members = module.params["enforce_members"] grafana_iface = GrafanaTeamInterface(module) changed = False - if state == 'present': + if state == "present": team = grafana_iface.get_team(name) if team is None: grafana_iface.create_team(name, email) @@ -332,9 +351,9 @@ def main(): grafana_iface.delete_team_member(team.get("id"), member) changed = True team = grafana_iface.get_team(name) - team['members'] = grafana_iface.get_team_members(team.get("id")) + team["members"] = grafana_iface.get_team_members(team.get("id")) module.exit_json(failed=False, changed=changed, team=team) - elif state == 'absent': + elif state == "absent": team = grafana_iface.get_team(name) if team is None: module.exit_json(failed=False, changed=False, message="No team found") @@ -353,5 +372,5 @@ def diff_members(target, current): return diff -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_user.py b/ansible_collections/community/grafana/plugins/modules/grafana_user.py index 3247b534a..6e99718ac 100644 --- a/ansible_collections/community/grafana/plugins/modules/grafana_user.py +++ b/ansible_collections/community/grafana/plugins/modules/grafana_user.py @@ -19,7 +19,7 @@ from __future__ import absolute_import, division, print_function -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: grafana_user author: @@ -68,13 +68,13 @@ options: type: str choices: ["present", "absent"] notes: -- Unlike other modules from the collection, this module does not support `grafana_api_key` authentication type. The Grafana API endpoint for users management +- Unlike other modules from the collection, this module does not support C(grafana_api_key) authentication type. The Grafana API endpoint for users management requires basic auth and admin privileges. extends_documentation_fragment: - community.grafana.basic_auth -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- - name: Create or update a Grafana user community.grafana.grafana_user: @@ -95,9 +95,9 @@ EXAMPLES = ''' url_password: changeme login: batman state: absent -''' +""" -RETURN = ''' +RETURN = """ --- user: description: Information about the User @@ -152,7 +152,7 @@ user: type: bool sample: - false -''' +""" import json @@ -165,12 +165,13 @@ __metaclass__ = type class GrafanaUserInterface(object): - def __init__(self, module): self._module = module # {{{ Authentication header self.headers = {"Content-Type": "application/json"} - self.headers["Authorization"] = basic_auth_header(module.params['url_username'], module.params['url_password']) + self.headers["Authorization"] = basic_auth_header( + module.params["url_username"], module.params["url_password"] + ) # }}} self.grafana_url = base.clean_url(module.params.get("url")) @@ -181,22 +182,34 @@ class GrafanaUserInterface(object): headers = [] full_url = "{grafana_url}{path}".format(grafana_url=self.grafana_url, path=url) - resp, info = fetch_url(self._module, full_url, data=data, headers=headers, method=method) + resp, info = fetch_url( + self._module, full_url, data=data, headers=headers, method=method + ) status_code = info["status"] if status_code == 404: return None elif status_code == 401: - self._module.fail_json(failed=True, msg="Unauthorized to perform action '%s' on '%s' header: %s" % (method, full_url, self.headers)) + self._module.fail_json( + failed=True, + msg="Unauthorized to perform action '%s' on '%s' header: %s" + % (method, full_url, self.headers), + ) elif status_code == 403: self._module.fail_json(failed=True, msg="Permission Denied") elif status_code == 200: return self._module.from_json(resp.read()) - self._module.fail_json(failed=True, msg="Grafana Users API answered with HTTP %d" % status_code, body=self._module.from_json(resp.read())) + self._module.fail_json( + failed=True, + msg="Grafana Users API answered with HTTP %d" % status_code, + body=self._module.from_json(resp.read()), + ) def create_user(self, name, email, login, password): # https://grafana.com/docs/http_api/admin/#global-users if not password: - self._module.fail_json(failed=True, msg="missing required arguments: password") + self._module.fail_json( + failed=True, msg="missing required arguments: password" + ) url = "/api/admin/users" user = dict(name=name, email=email, login=login, password=password) self._send_request(url, data=user, headers=self.headers, method="POST") @@ -218,7 +231,9 @@ class GrafanaUserInterface(object): # https://grafana.com/docs/http_api/admin/#permissions url = "/api/admin/users/{user_id}/permissions".format(user_id=user_id) permissions = dict(isGrafanaAdmin=is_admin) - return self._send_request(url, data=permissions, headers=self.headers, method="PUT") + return self._send_request( + url, data=permissions, headers=self.headers, method="PUT" + ) def delete_user(self, user_id): # https://grafana.com/docs/http_api/admin/#delete-global-user @@ -232,7 +247,7 @@ def is_user_update_required(target_user, email, name, login, is_admin): email=target_user.get("email"), name=target_user.get("name"), login=target_user.get("login"), - is_admin=target_user.get("isGrafanaAdmin") + is_admin=target_user.get("isGrafanaAdmin"), ) param_dict = dict(email=email, name=name, login=login, is_admin=is_admin) return target_user_dict != param_dict @@ -243,44 +258,46 @@ def setup_module_object(): argument_spec=argument_spec, supports_check_mode=False, required_if=[ - ['state', 'present', ['name', 'email']], + ["state", "present", ["name", "email"]], ], - required_together=base.grafana_required_together() + required_together=base.grafana_required_together(), ) return module argument_spec = base.grafana_argument_spec() argument_spec.update( - state=dict(choices=['present', 'absent'], default='present'), - name=dict(type='str', required=False), - email=dict(type='str', required=False), - login=dict(type='str', required=True), - password=dict(type='str', required=False, no_log=True), - is_admin=dict(type='bool', default=False), + state=dict(choices=["present", "absent"], default="present"), + name=dict(type="str", required=False), + email=dict(type="str", required=False), + login=dict(type="str", required=True), + password=dict(type="str", required=False, no_log=True), + is_admin=dict(type="bool", default=False), ) -argument_spec.pop('grafana_api_key') +argument_spec.pop("grafana_api_key") def main(): module = setup_module_object() - state = module.params['state'] - name = module.params['name'] - email = module.params['email'] - login = module.params['login'] - password = module.params['password'] - is_admin = module.params['is_admin'] + state = module.params["state"] + name = module.params["name"] + email = module.params["email"] + login = module.params["login"] + password = module.params["password"] + is_admin = module.params["is_admin"] grafana_iface = GrafanaUserInterface(module) # search user by login actual_grafana_user = grafana_iface.get_user_from_login(login) - if state == 'present': + if state == "present": has_changed = False if actual_grafana_user is None: # create new user - actual_grafana_user = grafana_iface.create_user(name, email, login, password) + actual_grafana_user = grafana_iface.create_user( + name, email, login, password + ) has_changed = True if is_user_update_required(actual_grafana_user, email, name, login, is_admin): @@ -288,17 +305,19 @@ def main(): actual_grafana_user_id = actual_grafana_user.get("id") if is_admin != actual_grafana_user.get("isGrafanaAdmin"): grafana_iface.update_user_permissions(actual_grafana_user_id, is_admin) - actual_grafana_user = grafana_iface.update_user(actual_grafana_user_id, email, name, login) + actual_grafana_user = grafana_iface.update_user( + actual_grafana_user_id, email, name, login + ) has_changed = True module.exit_json(changed=has_changed, user=actual_grafana_user) - elif state == 'absent': + elif state == "absent": if actual_grafana_user is None: module.exit_json(message="No user found, nothing to do") result = grafana_iface.delete_user(actual_grafana_user.get("id")) module.exit_json(changed=True, message=result.get("message")) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/grafana/roles/grafana/README.md b/ansible_collections/community/grafana/roles/grafana/README.md new file mode 100644 index 000000000..f46434edf --- /dev/null +++ b/ansible_collections/community/grafana/roles/grafana/README.md @@ -0,0 +1,190 @@ +# Grafana Role for Ansible Collection Community.Grafana + +Configure Grafana organizations, dashboards, folders, datasources, teams and users. + +## Role Variables + +| Variable | Required | Default | +| ---------------- | -------- | ------- | +| grafana_url | yes | +| grafana_username | yes | +| grafana_password | yes | +| [**grafana_users**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_user_module.html) | +| email | no | +| is_admin | no | +| login | yes | +| name | yes | +| password | no | +| state | no | +| [**grafana_organizations**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_organization_module.html) | +| name | yes | +| state | no | +| [**grafana_teams**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_team_module.html) | +| email | yes | +| enforce_members | no | +| members | no | +| name | yes | +| skip_version_check | no | +| state | no | +| [**grafana_datasources**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_datasource_module.html) | +| access | no | +| additional_json_data | no | +| additional_secure_json_data | no | +| aws_access_key | no | +| aws_assume_role_arn | no | +| aws_auth_type | no | +| aws_credentials_profile | no | +| aws_custom_metrics_namespaces | no | +| aws_default_region | no | +| aws_secret_key | no | +| azure_client | no | +| azure_cloud | no | +| azure_secret | no | +| azure_tenant | no | +| basic_auth_password | no | +| basic_auth_user | no | +| database | no | +| ds_type | no | +| ds_url | no | +| enforce_secure_data | no | +| es_version | no | +| interval | no | +| is_default | no | +| max_concurrent_shard_requests | no | +| name | yes | +| org_id | no | +| org_name | no | +| password | no | +| sslmode | no | +| state | no | +| time_field | no | +| time_interval | no | +| tls_ca_cert | no | +| tls_client_cert | no | +| tls_client_key | no | +| tls_skip_verify | no | +| trends | no | +| tsdb_resolution | no | +| tsdb_version | no | +| uid | no | +| user | no | +| with_credentials | no | +| zabbix_password | no | +| 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 | +| [**grafana_dashboards**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_dashboard_module.html) | +| commit_message | no | +| dashboard_id | no | +| dashboard_revision | no | +| folder | no | +| org_id | no | +| org_name | no | +| overwrite | no | +| path | no | +| slug | no | +| state | no | +| uid | no | +| [**grafana_organization_users**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_organization_user_module.html) | +| login | yes | +| org_id | no | +| org_name | no | +| role | no | +| state | no | +| [**grafana_notification_channel**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_notification_channel_module.html) | +| dingding_message_type | no | +| dingding_url | no | +| disable_resolve_message | no | +| discord_message_content | no | +| discord_url | no | +| email_addresses | no | +| email_single | no | +| googlechat_url | no | +| hipchat_api_key | no | +| hipchat_room_id | no | +| hipchat_url | no | +| include_image | no | +| is_default | no | +| kafka_topic | no | +| kafka_url | no | +| line_token | no | +| name | yes | +| opsgenie_api_key | no | +| opsgenie_auto_close | no | +| opsgenie_override_priority | no | +| opsgenie_url | no | +| org_id | no | +| pagerduty_auto_resolve | no | +| pagerduty_integration_key | no | +| pagerduty_message_in_details | no | +| pagerduty_severity | no | +| prometheus_password | no | +| prometheus_url | no | +| prometheus_username | no | +| pushover_alert_sound | no | +| pushover_api_token | no | +| pushover_devices | no | +| pushover_expire | no | +| pushover_ok_sound | no | +| pushover_priority | no | +| pushover_retry | no | +| pushover_user_key | no | +| reminder_frequency | no | +| sensu_handler | no | +| sensu_password | no | +| sensu_source | no | +| sensu_url | no | +| sensu_username | no | +| slack_icon_emoji | no | +| slack_icon_url | no | +| slack_mention_channel | no | +| slack_mention_groups | no | +| slack_mention_users | no | +| slack_recipient | no | +| slack_token | no | +| slack_url | no | +| slack_username | no | +| state | no | +| teams_url | no | +| telegram_bot_token | no | +| telegram_chat_id | no | +| threema_api_secret | no | +| threema_gateway_id | no | +| threema_recipient_id | no | +| type | yes | +| uid | no | +| victorops_auto_resolve | no | +| victorops_url | no | +| webhook_http_method | no | +| webhook_password | no | +| webhook_url | no | +| webhook_username | no | + +## Example Playbook + +```yaml +--- +- hosts: localhost + gather_facts: false + + vars: + grafana_url: "https://monitoring.example.com" + grafana_username: "api-user" + grafana_password: "******" + + grafana_datasources: + - name: "Loki" + ds_type: "loki" + ds_url: "http://127.0.0.1:3100" + tls_skip_verify: yes + grafana_folders: + - name: my_service + - name: other_service + + roles: + - role: community.grafana.grafana +``` diff --git a/ansible_collections/community/grafana/roles/grafana/defaults/main.yml b/ansible_collections/community/grafana/roles/grafana/defaults/main.yml new file mode 100644 index 000000000..6a84370d3 --- /dev/null +++ b/ansible_collections/community/grafana/roles/grafana/defaults/main.yml @@ -0,0 +1,9 @@ +--- +grafana_organizations: [] +grafana_organization_users: [] +grafana_users: [] +grafana_teams: [] +grafana_datasources: [] +grafana_folders: [] +grafana_dashboards: [] +grafana_notification_channels: [] diff --git a/ansible_collections/community/grafana/roles/grafana/meta/main.yml b/ansible_collections/community/grafana/roles/grafana/meta/main.yml new file mode 100644 index 000000000..47d4af5b7 --- /dev/null +++ b/ansible_collections/community/grafana/roles/grafana/meta/main.yml @@ -0,0 +1,14 @@ +--- +galaxy_info: + role_name: grafana + author: community + description: Configure Grafana organizations, dashboards, folders, datasources, teams and users + license: GPLv3 + min_ansible_version: "2.14" + galaxy_tags: [grafana, monitoring] + platforms: + - {name: EL, versions: [all]} + - {name: Fedora, versions: [all]} + - {name: Amazon, versions: [all]} + - {name: Debian, versions: [all]} + - {name: Ubuntu, versions: [all]} diff --git a/ansible_collections/community/grafana/roles/grafana/tasks/main.yml b/ansible_collections/community/grafana/roles/grafana/tasks/main.yml new file mode 100644 index 000000000..82bbc633d --- /dev/null +++ b/ansible_collections/community/grafana/roles/grafana/tasks/main.yml @@ -0,0 +1,203 @@ +--- +- name: Group tasks for authentication parameters + module_defaults: + group/community.grafana.grafana: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + use_proxy: "{{ grafana_use_proxy | default(omit) }}" + validate_certs: "{{ grafana_validate_certs | default(omit) }}" + block: + - name: Manage organization # noqa: args[module] + community.grafana.grafana_organization: + name: "{{ organization.name }}" + state: "{{ organization.state | default(omit) }}" + loop: "{{ grafana_organizations }}" + loop_control: {loop_var: organization} + tags: organization + + - name: Manage notification channel + community.grafana.grafana_notification_channel: + dingding_message_type: "{{ notification_channel.dingding_message_type | default(omit) }}" + dingding_url: "{{ notification_channel.dingding_url | default(omit) }}" + disable_resolve_message: "{{ notification_channel.disable_resolve_message | default(omit) }}" + discord_message_content: "{{ notification_channel.discord_message_content | default(omit) }}" + discord_url: "{{ notification_channel.discord_url | default(omit) }}" + email_addresses: "{{ notification_channel.email_addresses | default(omit) }}" + email_single: "{{ notification_channel.email_single | default(omit) }}" + googlechat_url: "{{ notification_channel.googlechat_url | default(omit) }}" + hipchat_api_key: "{{ notification_channel.hipchat_api_key | default(omit) }}" + hipchat_room_id: "{{ notification_channel.hipchat_room_id | default(omit) }}" + hipchat_url: "{{ notification_channel.hipchat_url | default(omit) }}" + include_image: "{{ notification_channel.include_image | default(omit) }}" + is_default: "{{ notification_channel.is_default | default(omit) }}" + kafka_topic: "{{ notification_channel.kafka_topic | default(omit) }}" + kafka_url: "{{ notification_channel.kafka_url | default(omit) }}" + line_token: "{{ notification_channel.line_token | default(omit) }}" + name: "{{ notification_channel.name }}" + opsgenie_api_key: "{{ notification_channel.opsgenie_api_key | default(omit) }}" + opsgenie_auto_close: "{{ notification_channel.opsgenie_auto_close | default(omit) }}" + opsgenie_override_priority: "{{ notification_channel.opsgenie_override_priority | default(omit) }}" + opsgenie_url: "{{ notification_channel.opsgenie_url | default(omit) }}" + org_id: "{{ notification_channel.org_id | default(omit) }}" + pagerduty_auto_resolve: "{{ notification_channel.pagerduty_auto_resolve | default(omit) }}" + pagerduty_integration_key: "{{ notification_channel.pagerduty_integration_key | default(omit) }}" + pagerduty_message_in_details: "{{ notification_channel.pagerduty_message_in_details | default(omit) }}" + pagerduty_severity: "{{ notification_channel.pagerduty_severity | default(omit) }}" + prometheus_password: "{{ notification_channel.prometheus_password | default(omit) }}" + prometheus_url: "{{ notification_channel.prometheus_url | default(omit) }}" + prometheus_username: "{{ notification_channel.prometheus_username | default(omit) }}" + pushover_alert_sound: "{{ notification_channel.pushover_alert_sound | default(omit) }}" + pushover_api_token: "{{ notification_channel.pushover_api_token | default(omit) }}" + pushover_devices: "{{ notification_channel.pushover_devices | default(omit) }}" + pushover_expire: "{{ notification_channel.pushover_expire | default(omit) }}" + pushover_ok_sound: "{{ notification_channel.pushover_ok_sound | default(omit) }}" + pushover_priority: "{{ notification_channel.pushover_priority | default(omit) }}" + pushover_retry: "{{ notification_channel.pushover_retry | default(omit) }}" + pushover_user_key: "{{ notification_channel.pushover_user_key | default(omit) }}" + reminder_frequency: "{{ notification_channel.reminder_frequency | default(omit) }}" + sensu_handler: "{{ notification_channel.sensu_handler | default(omit) }}" + sensu_password: "{{ notification_channel.sensu_password | default(omit) }}" + sensu_source: "{{ notification_channel.sensu_source | default(omit) }}" + sensu_url: "{{ notification_channel.sensu_url | default(omit) }}" + sensu_username: "{{ notification_channel.sensu_username | default(omit) }}" + slack_icon_emoji: "{{ notification_channel.slack_icon_emoji | default(omit) }}" + slack_icon_url: "{{ notification_channel.slack_icon_url | default(omit) }}" + slack_mention_channel: "{{ notification_channel.slack_mention_channel | default(omit) }}" + slack_mention_groups: "{{ notification_channel.slack_mention_groups | default(omit) }}" + slack_mention_users: "{{ notification_channel.slack_mention_users | default(omit) }}" + slack_recipient: "{{ notification_channel.slack_recipient | default(omit) }}" + slack_token: "{{ notification_channel.slack_token | default(omit) }}" + slack_url: "{{ notification_channel.slack_url | default(omit) }}" + slack_username: "{{ notification_channel.slack_username | default(omit) }}" + state: "{{ notification_channel.state | default(omit) }}" + teams_url: "{{ notification_channel.teams_url | default(omit) }}" + telegram_bot_token: "{{ notification_channel.telegram_bot_token | default(omit) }}" + telegram_chat_id: "{{ notification_channel.telegram_chat_id | default(omit) }}" + threema_api_secret: "{{ notification_channel.threema_api_secret | default(omit) }}" + threema_gateway_id: "{{ notification_channel.threema_gateway_id | default(omit) }}" + threema_recipient_id: "{{ notification_channel.threema_recipient_id | default(omit) }}" + type: "{{ notification_channel.type }}" + uid: "{{ notification_channel.uid | default(omit) }}" + victorops_auto_resolve: "{{ notification_channel.victorops_auto_resolve | default(omit) }}" + victorops_url: "{{ notification_channel.victorops_url | default(omit) }}" + webhook_http_method: "{{ notification_channel.webhook_http_method | default(omit) }}" + webhook_password: "{{ notification_channel.webhook_password | default(omit) }}" + webhook_url: "{{ notification_channel.webhook_url | default(omit) }}" + webhook_username: "{{ notification_channel.webhook_username | default(omit) }}" + loop: "{{ grafana_notification_channels }}" + loop_control: {loop_var: notification_channel} + tags: notification_channel + + - name: Manage datasource + community.grafana.grafana_datasource: + access: "{{ datasource.access | default(omit) }}" + additional_json_data: "{{ datasource.additional_json_data | default(omit) }}" + additional_secure_json_data: "{{ datasource.additional_secure_json_data | default(omit) }}" + aws_access_key: "{{ datasource.aws_access_key | default(omit) }}" + aws_assume_role_arn: "{{ datasource.aws_assume_role_arn | default(omit) }}" + aws_auth_type: "{{ datasource.aws_auth_type | default(omit) }}" + aws_credentials_profile: "{{ datasource.aws_credentials_profile | default(omit) }}" + aws_custom_metrics_namespaces: "{{ datasource.aws_custom_metrics_namespaces | default(omit) }}" + aws_default_region: "{{ datasource.aws_default_region | default(omit) }}" + aws_secret_key: "{{ datasource.aws_secret_key | default(omit) }}" + azure_client: "{{ datasource.azure_client | default(omit) }}" + azure_cloud: "{{ datasource.azure_cloud | default(omit) }}" + azure_secret: "{{ datasource.azure_secret | default(omit) }}" + azure_tenant: "{{ datasource.azure_tenant | default(omit) }}" + basic_auth_password: "{{ datasource.basic_auth_password | default(omit) }}" + basic_auth_user: "{{ datasource.basic_auth_user | default(omit) }}" + database: "{{ datasource.database | default(omit) }}" + ds_type: "{{ datasource.ds_type | default(omit) }}" + ds_url: "{{ datasource.ds_url | default(omit) }}" + enforce_secure_data: "{{ datasource.enforce_secure_data | default(omit) }}" + es_version: "{{ datasource.es_version | default(omit) }}" + interval: "{{ datasource.interval | default(omit) }}" + is_default: "{{ datasource.is_default | default(omit) }}" + max_concurrent_shard_requests: "{{ datasource.max_concurrent_shard_requests | default(omit) }}" + name: "{{ datasource.name }}" + org_id: "{{ datasource.org_id | default(omit) }}" + org_name: "{{ datasource.org_name | default(omit) }}" + password: "{{ datasource.password | default(omit) }}" + sslmode: "{{ datasource.sslmode | default(omit) }}" + state: "{{ datasource.state | default(omit) }}" + time_field: "{{ datasource.time_field | default(omit) }}" + time_interval: "{{ datasource.time_interval | default(omit) }}" + tls_ca_cert: "{{ datasource.tls_ca_cert | default(omit) }}" + tls_client_cert: "{{ datasource.tls_client_cert | default(omit) }}" + tls_client_key: "{{ datasource.tls_client_key | default(omit) }}" + tls_skip_verify: "{{ datasource.tls_skip_verify | default(omit) }}" + trends: "{{ datasource.trends | default(omit) }}" + tsdb_resolution: "{{ datasource.tsdb_resolution | default(omit) }}" + tsdb_version: "{{ datasource.tsdb_version | default(omit) }}" + uid: "{{ datasource.uid | default(omit) }}" + user: "{{ datasource.user | default(omit) }}" + with_credentials: "{{ datasource.with_credentials | default(omit) }}" + zabbix_password: "{{ datasource.zabbix_password | default(omit) }}" + zabbix_user: "{{ datasource.zabbix_user | default(omit) }}" + loop: "{{ grafana_datasources }}" + loop_control: {loop_var: datasource} + tags: datasource + + - 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) }}" + loop: "{{ grafana_folders }}" + loop_control: {loop_var: folder} + tags: folder + + - name: Manage team # noqa: args[module] + community.grafana.grafana_team: + email: "{{ team.email }}" + enforce_members: "{{ team.enforce_members | default(omit) }}" + members: "{{ team.members | default(omit) }}" + name: "{{ team.name }}" + skip_version_check: "{{ team.skip_version_check | default(omit) }}" + state: "{{ team.state | default(omit) }}" + loop: "{{ grafana_teams }}" + loop_control: {loop_var: team} + tags: team + + - name: Manage user # noqa: args[module] + community.grafana.grafana_user: + email: "{{ user.email | default(omit) }}" + is_admin: "{{ user.is_admin | default(omit) }}" + login: "{{ user.login }}" + name: "{{ user.name }}" + password: "{{ user.password | default(omit) }}" + state: "{{ user.state | default(omit) }}" + loop: "{{ grafana_users }}" + loop_control: {loop_var: user} + tags: user + + - name: Manage organization users + community.grafana.grafana_organization_user: + login: "{{ organization_user.login }}" + org_id: "{{ organization_user.org_id | default(omit) }}" + org_name: "{{ organization_user.org_name | default(omit) }}" + role: "{{ organization_user.role | default(omit) }}" + state: "{{ organization_user.state | default(omit) }}" + loop: "{{ grafana_organization_users }}" + loop_control: {loop_var: organization_user} + tags: organization_user + + - name: Manage dashboard + community.grafana.grafana_dashboard: + commit_message: "{{ dashboard.commit_message | default(omit) }}" + dashboard_id: "{{ dashboard.dashboard_id | default(omit) }}" + dashboard_revision: "{{ dashboard.dashboard_revision | default(omit) }}" + folder: "{{ dashboard.folder | default(omit) }}" + org_id: "{{ dashboard.org_id | default(omit) }}" + org_name: "{{ dashboard.org_name | default(omit) }}" + overwrite: "{{ dashboard.overwrite | default(omit) }}" + path: "{{ dashboard.path | default(omit) }}" + slug: "{{ dashboard.slug | default(omit) }}" + state: "{{ dashboard.state | default(omit) }}" + uid: "{{ dashboard.uid | default(omit) }}" + loop: "{{ grafana_dashboards }}" + loop_control: {loop_var: dashboard} + tags: [dashboard, molecule-idempotence-notest] diff --git a/ansible_collections/community/grafana/ruff.toml b/ansible_collections/community/grafana/ruff.toml new file mode 100644 index 000000000..ef486c58f --- /dev/null +++ b/ansible_collections/community/grafana/ruff.toml @@ -0,0 +1 @@ +ignore = ["E402"] diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/defaults/main.yml index 8b9c9348a..3edac8598 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/defaults/main.yml @@ -1,7 +1,4 @@ --- - -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" - -... +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/runme.sh new file mode 100755 index 000000000..867afb0d3 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/site.yml new file mode 100644 index 000000000..c32364ef0 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_dashboard + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_dashboard diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml new file mode 100644 index 000000000..a493eb0f8 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-by-org-name.yml @@ -0,0 +1,84 @@ +--- +- module_defaults: + community.grafana.grafana_dashboard: + org_name: Main Org. + block: + - name: Create Dashboard from file by org_name | check mode | dashboard does not exist + 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 + check_mode: true + register: dbo_result1 + - ansible.builtin.assert: + that: + - dbo_result1.failed == false + - dbo_result1.changed == true + - dbo_result1.uid is not defined + - dbo_result1.msg == 'Dashboard test will be created' + + - name: Create Dashboard from file by org_name | run mode | dashboard does not exist + 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 + check_mode: false + register: dbo_result2 + - ansible.builtin.assert: + that: + - dbo_result2.failed == false + - dbo_result2.changed == true + - dbo_result2.uid is defined + - dbo_result2.uid | type_debug == "AnsibleUnsafeText" + - dbo_result2.msg == 'Dashboard test created' + + - name: Create Dashboard from file by org_name | check mode | dashboard exists + 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 + check_mode: true + register: dbo_result3 + - ansible.builtin.assert: + that: + - dbo_result3.failed == false + - dbo_result3.changed == false + - dbo_result3.uid is defined + - dbo_result3.uid | type_debug == "AnsibleUnsafeText" + - dbo_result3.msg == 'Dashboard test unchanged.' + + - name: Create Dashboard from file by org_name | run mode | dashboard exists + 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 + check_mode: false + register: dbo_result4 + - ansible.builtin.assert: + that: + - dbo_result4.failed == false + - dbo_result4.changed == false + - dbo_result4.uid is defined + - dbo_result4.uid | type_debug == "AnsibleUnsafeText" + - dbo_result4.msg == 'Dashboard test unchanged.' + + - ansible.builtin.include_tasks: + file: delete-dashboard.yml + vars: + uid_to_delete: '{{ dbo_result4.uid }}' diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml index 04b17f727..bb4f30e10 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-export.yml @@ -1,33 +1,93 @@ --- -- set_fact: - dashboard_uid: "{{ result.uid }}" +- name: Export Dashboard | check mode | uid does not exist + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: export + path: /tmp/dashboard_export.json + overwrite: true + uid: "090909090" + check_mode: true + ignore_errors: true + register: de_result1 +- ansible.builtin.assert: + that: + - de_result1.failed == false + - de_result1.changed == false + - de_result1.uid is defined + - de_result1.uid | type_debug == "AnsibleUnsafeText" + - de_result1.uid == "090909090" + - de_result1.msg == 'Dashboard ' ~ de_result1.uid ~ ' does not exist.' -- name: Check export grafana dashboard to file - grafana_dashboard: +- name: Export Dashboard | run mode | uid does not exist + 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 }}" - register: result + uid: "090909090" + check_mode: false + ignore_errors: true + register: de_result2 +- ansible.builtin.assert: + that: + - de_result2.failed == false + - de_result2.changed == false + - de_result2.uid is defined + - de_result2.uid | type_debug == "AnsibleUnsafeText" + - de_result2.uid == "090909090" + - de_result2.msg == 'Dashboard ' ~ de_result2.uid ~ ' does not exist.' -- debug: - var: result +- name: Export Dashboard | check mode | uid exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: export + path: /tmp/dashboard_export.json + overwrite: true + uid: "{{ uid_to_export }}" + check_mode: true + register: de_result3 +- ansible.builtin.assert: + that: + - de_result3.failed == false + - de_result3.changed == true + - de_result3.uid is defined + - de_result3.uid | type_debug == "AnsibleUnsafeText" + - de_result3.uid == uid_to_export + - de_result3.msg == 'Dashboard ' ~ de_result3.uid ~ ' will be exported to /tmp/dashboard_export.json' -- assert: +- name: Export Dashboard | run mode | uid exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: export + path: /tmp/dashboard_export.json + overwrite: true + uid: "{{ uid_to_export }}" + check_mode: false + register: de_result4 +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.msg == 'Dashboard {{ dashboard_uid }} exported to /tmp/dashboard.json'" + - de_result4.failed == false + - de_result4.changed == true + - de_result4.uid is defined + - de_result4.uid | type_debug == "AnsibleUnsafeText" + - de_result4.uid == uid_to_export + - de_result4.msg == 'Dashboard ' ~ de_result4.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: Export Dashboard | 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: +- name: Export Dashboard | Assert that exported dashboard contains formatted JSON + ansible.builtin.assert: that: - - "exported_dashboard_lines | length >= 2" - - "exported_dashboard_lines[0] == '{'" - - "exported_dashboard_lines[-1] == '}'" + - exported_dashboard_lines | length >= 2 + - exported_dashboard_lines[0] == '{' + - exported_dashboard_lines[-1] == '}' diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml index f4f3d9f4d..4704002f5 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-folder-destination.yml @@ -1,33 +1,38 @@ --- -- 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." +- name: Create Dashboard from file | check mode | folder does not exist + 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 + check_mode: true + register: dfff_result1 + ignore_errors: true +- ansible.builtin.assert: + that: + - dfff_result1.failed == true + - dfff_result1.changed == false + - "dfff_result1.msg == 'error : Dashboard folder \\'inexistent\\' does not exist.'" -- assert: +- name: Create Dashboard from file | run mode | folder does not exist + 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 + check_mode: false + register: dfff_result2 + ignore_errors: true +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.failed == true" - - "result.msg == expected_error" + - dfff_result2.failed == true + - dfff_result2.changed == false + - "dfff_result2.msg == 'error : Dashboard folder \\'inexistent\\' does not exist.'" diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml new file mode 100644 index 000000000..3d0019c5d --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file-export.yml @@ -0,0 +1,90 @@ +--- +- name: Create Dashboard from file | Copy dashboard file + ansible.builtin.copy: + src: files/dashboard.json + dest: /tmp/dashboard.json + +- name: Create Dashboard from file | check mode | dashboard does not exist + 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 + check_mode: true + register: dff_result1 +- ansible.builtin.assert: + that: + - dff_result1.failed == false + - dff_result1.changed == true + - dff_result1.uid is not defined + - dff_result1.msg == 'Dashboard test will be created' + +- name: Create Dashboard from file | run mode | dashboard does not exist + 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 + check_mode: false + register: dff_result2 +- ansible.builtin.assert: + that: + - dff_result2.failed == false + - dff_result2.changed == true + - dff_result2.uid is defined + - dff_result2.uid | type_debug == "AnsibleUnsafeText" + - dff_result2.msg == 'Dashboard test created' + +- name: Create Dashboard from file | check mode | dashboard exists + 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 + check_mode: true + register: dff_result3 +- ansible.builtin.assert: + that: + - dff_result3.failed == false + - dff_result3.changed == false + - dff_result3.uid is defined + - dff_result3.uid | type_debug == "AnsibleUnsafeText" + - dff_result3.msg == 'Dashboard test unchanged.' + +- name: Create Dashboard from file | run mode | dashboard exists + 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 + check_mode: false + register: dff_result4 +- ansible.builtin.assert: + that: + - dff_result4.failed == false + - dff_result4.changed == false + - dff_result4.uid is defined + - dff_result4.uid | type_debug == "AnsibleUnsafeText" + - dff_result4.msg == 'Dashboard test unchanged.' + +- ansible.builtin.include_tasks: + file: dashboard-export.yml + vars: + uid_to_export: '{{ dff_result4.uid }}' + +- ansible.builtin.include_tasks: + file: delete-dashboard.yml + vars: + uid_to_delete: '{{ dff_result4.uid }}' diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml deleted file mode 100644 index 93df1666a..000000000 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-file.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- -- name: copy dashboard file - copy: - src: "files/dashboard.json" - dest: "/tmp/dashboard.json" - - -- name: Check import grafana dashboard from file - 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 - -- debug: - var: result - -- assert: - that: - - "result.changed == true" - - "result.msg == 'Dashboard test created'" - -- name: Check import grafana dashboard from file idempotency - 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 - -- debug: - var: result - -- assert: - that: - - "result.changed == false" - - "result.msg == 'Dashboard test unchanged.'" diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml index 3b81ebf71..4de7b4675 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-id.yml @@ -1,6 +1,6 @@ --- -- name: Check import grafana dashboard from id - grafana_dashboard: +- name: Create Dashboard from ID | check mode | dashboard does not exist + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -9,18 +9,17 @@ dashboard_id: "6098" dashboard_revision: "1" overwrite: true - register: result - -- debug: - var: result - -- assert: + check_mode: true + register: dfi_result1 +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.msg == 'Dashboard Zabbix Host Status created'" + - dfi_result1.failed == false + - dfi_result1.changed == true + - dfi_result1.uid is not defined + - dfi_result1.msg == 'Dashboard Zabbix Host Status will be created' -- name: Check import grafana dashboard from id idempotency - grafana_dashboard: +- name: Create Dashboard from ID | run mode | dashboard does not exist + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -29,12 +28,57 @@ dashboard_id: "6098" dashboard_revision: "1" overwrite: true - register: result + check_mode: false + register: dfi_result2 +- ansible.builtin.assert: + that: + - dfi_result2.failed == false + - dfi_result2.changed == true + - dfi_result2.uid is defined + - dfi_result2.uid | type_debug == "AnsibleUnsafeText" + - dfi_result2.msg == 'Dashboard Zabbix Host Status created' -- debug: - var: result +- name: Create Dashboard from ID | check mode | dashboard exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: present + commit_message: Updated by ansible + dashboard_id: "6098" + dashboard_revision: "1" + overwrite: true + check_mode: true + register: dfi_result3 +- ansible.builtin.assert: + that: + - dfi_result3.failed == false + - dfi_result3.changed == false + - dfi_result3.uid is defined + - dfi_result3.uid | type_debug == "AnsibleUnsafeText" + - dfi_result3.msg == 'Dashboard Zabbix Host Status unchanged.' -- assert: +- name: Create Dashboard from ID | run mode | dashboard exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: present + commit_message: Updated by ansible + dashboard_id: "6098" + dashboard_revision: "1" + overwrite: true + check_mode: false + register: dfi_result4 +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.msg == 'Dashboard Zabbix Host Status unchanged.'" + - dfi_result4.failed == false + - dfi_result4.changed == false + - dfi_result4.uid is defined + - dfi_result4.uid | type_debug == "AnsibleUnsafeText" + - dfi_result4.msg == 'Dashboard Zabbix Host Status unchanged.' + +- ansible.builtin.include_tasks: + file: delete-dashboard.yml + vars: + uid_to_delete: '{{ dfi_result4.uid }}' diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml index 5146fc9a0..abdeacfd0 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/dashboard-from-url.yml @@ -1,7 +1,6 @@ --- - -- name: Check import grafana dashboard from url - grafana_dashboard: +- name: Create Dashboard from URL | check mode | dashboard does not exist + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -9,18 +8,17 @@ commit_message: Updated by ansible dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download overwrite: true - register: result - -- debug: - var: result - -- assert: + check_mode: true + register: dfu_result1 +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.msg == 'Dashboard Zabbix Host Status created'" + - dfu_result1.failed == false + - dfu_result1.changed == true + - dfu_result1.uid is not defined + - dfu_result1.msg == 'Dashboard Zabbix Host Status will be created' -- name: Check import grafana dashboard from url idempotency - grafana_dashboard: +- name: Create Dashboard from URL | run mode | dashboard does not exist + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" @@ -28,12 +26,55 @@ commit_message: Updated by ansible dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download overwrite: true - register: result + check_mode: false + register: dfu_result2 +- ansible.builtin.assert: + that: + - dfu_result2.failed == false + - dfu_result2.changed == true + - dfu_result2.uid is defined + - dfu_result2.uid | type_debug == "AnsibleUnsafeText" + - dfu_result2.msg == 'Dashboard Zabbix Host Status created' -- debug: - var: result +- name: Create Dashboard from URL | check mode | dashboard exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: present + commit_message: Updated by ansible + dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download + overwrite: true + check_mode: true + register: dfu_result3 +- ansible.builtin.assert: + that: + - dfu_result3.failed == false + - dfu_result3.changed == false + - dfu_result3.uid is defined + - dfu_result3.uid | type_debug == "AnsibleUnsafeText" + - dfu_result3.msg == 'Dashboard Zabbix Host Status unchanged.' -- assert: +- name: Create Dashboard from URL | run mode | dashboard exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: present + commit_message: Updated by ansible + dashboard_url: https://grafana.com/api/dashboards/6098/revisions/1/download + overwrite: true + check_mode: false + register: dfu_result4 +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.msg == 'Dashboard Zabbix Host Status unchanged.'" + - dfu_result4.failed == false + - dfu_result4.changed == false + - dfu_result4.uid is defined + - dfu_result4.uid | type_debug == "AnsibleUnsafeText" + - dfu_result4.msg == 'Dashboard Zabbix Host Status unchanged.' + +- ansible.builtin.include_tasks: + file: delete-dashboard.yml + vars: + uid_to_delete: '{{ dfu_result4.uid }}'
\ No newline at end of file diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml index 2013324fb..b2d27fdc8 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/delete-dashboard.yml @@ -1,16 +1,60 @@ -- name: Check delete dashboard is working - grafana_dashboard: +--- +- name: Delete dashboard | check mode | dashboard exists + community.grafana.grafana_dashboard: grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent - uid: "{{ result.uid }}" - register: result + uid: "{{ uid_to_delete }}" + check_mode: true + register: dd_result1 +- ansible.builtin.assert: + that: + - dd_result1.failed == false + - dd_result1.changed == true + - dd_result1.msg == 'Dashboard ' ~ dd_result1.uid ~ ' will be deleted' + +- name: Delete dashboard | run mode | dashboard exists + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + uid: "{{ uid_to_delete }}" + check_mode: false + register: dd_result2 +- ansible.builtin.assert: + that: + - dd_result2.failed == false + - dd_result2.changed == true + - dd_result2.msg == 'Dashboard ' ~ dd_result2.uid ~ ' deleted' -- debug: - var: result +- name: Delete dashboard | check mode | dashboard does not exist + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + uid: "{{ uid_to_delete }}" + check_mode: true + register: dd_result1 +- ansible.builtin.assert: + that: + - dd_result1.failed == false + - dd_result1.changed == false + - dd_result1.msg == 'Dashboard ' ~ dd_result1.uid ~ ' does not exist.' -- assert: +- name: Delete dashboard | run mode | dashboard does not exist + community.grafana.grafana_dashboard: + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + uid: "{{ uid_to_delete }}" + check_mode: false + register: dd_result2 +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.msg == 'Dashboard {{ result.uid }} deleted'" + - dd_result2.failed == false + - dd_result2.changed == false + - dd_result2.msg == 'Dashboard ' ~ dd_result2.uid ~ ' does not exist.'
\ No newline at end of file diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/main.yml index 4d9138fcf..86e756e95 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_dashboard/tasks/main.yml @@ -1,7 +1,20 @@ -- block: - - include: dashboard-from-url.yml - - include: delete-dashboard.yml - - include: dashboard-from-id.yml - - include: dashboard-from-file.yml - - include: dashboard-export.yml - - include: dashboard-folder-destination.yml +--- +- name: Create dashboard from url + ansible.builtin.include_tasks: + file: dashboard-from-url.yml + +- name: Create dashboard from id + ansible.builtin.include_tasks: + file: dashboard-from-id.yml + +- name: Create dashboard from file and export + ansible.builtin.include_tasks: + file: dashboard-from-file-export.yml + +- name: Create dashboard from file by org_name + ansible.builtin.include_tasks: + file: dashboard-by-org-name.yml + +- name: Create dashboard from file in non existing folder + ansible.builtin.include_tasks: + file: dashboard-folder-destination.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/defaults/main.yml index 8b9c9348a..4abf9bb43 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/defaults/main.yml @@ -1,7 +1,5 @@ --- -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" - -... +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/runme.sh new file mode 100755 index 000000000..867afb0d3 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/site.yml new file mode 100644 index 000000000..c8374dcdd --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_datasource + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_datasource diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/azure.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/azure.yml index a96691893..0dbd94963 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/azure.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/azure.yml @@ -1,11 +1,12 @@ +--- - name: Create azure datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-azure grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" - org_id: '1' + grafana_password: "{{ grafana_password }}" + org_id: "1" ds_type: grafana-azure-monitor-datasource ds_url: http://example.com azure_client: client1 @@ -13,38 +14,38 @@ azure_secret: secret1 azure_cloud: azuremonitor -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-azure created'" - - result.datasource.access == 'proxy' - - result.datasource.basicAuth == false - - result.datasource.database == '' - - result.datasource.isDefault == false - - result.datasource.jsonData.clientId == 'client1' - - result.datasource.jsonData.cloudName == 'azuremonitor' - - result.datasource.jsonData.tenantId == 'tenant1' - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.name == 'datasource-azure' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'grafana-azure-monitor-datasource' - - result.datasource.url == 'http://example.com' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - result.changed + - result.msg == 'Datasource datasource-azure created' + - result.datasource.access == 'proxy' + - result.datasource.basicAuth == false + - result.datasource.database == '' + - result.datasource.isDefault == false + - result.datasource.jsonData.clientId == 'client1' + - result.datasource.jsonData.cloudName == 'azuremonitor' + - result.datasource.jsonData.tenantId == 'tenant1' + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.name == 'datasource-azure' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'grafana-azure-monitor-datasource' + - result.datasource.url == 'http://example.com' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Check azure datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-azure grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" - org_id: '1' + grafana_password: "{{ grafana_password }}" + org_id: "1" ds_type: grafana-azure-monitor-datasource ds_url: http://example.com azure_client: client1 @@ -52,37 +53,37 @@ azure_secret: secret1 azure_cloud: azuremonitor -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.access == 'proxy' - - result.datasource.basicAuth == false - - result.datasource.database == '' - - result.datasource.isDefault == false - - result.datasource.jsonData.clientId == 'client1' - - result.datasource.jsonData.cloudName == 'azuremonitor' - - result.datasource.jsonData.tenantId == 'tenant1' - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.name == 'datasource-azure' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'grafana-azure-monitor-datasource' - - result.datasource.url == 'http://example.com' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.access == 'proxy' + - result.datasource.basicAuth == false + - result.datasource.database == '' + - result.datasource.isDefault == false + - result.datasource.jsonData.clientId == 'client1' + - result.datasource.jsonData.cloudName == 'azuremonitor' + - result.datasource.jsonData.tenantId == 'tenant1' + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.name == 'datasource-azure' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'grafana-azure-monitor-datasource' + - result.datasource.url == 'http://example.com' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Delete azure datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-azure grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" - org_id: '1' + grafana_password: "{{ grafana_password }}" + org_id: "1" ds_type: grafana-azure-monitor-datasource ds_url: http://example.com azure_client: client1 @@ -91,22 +92,22 @@ azure_cloud: azuremonitor state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-azure deleted.'" + - result.changed + - result.msg == 'Datasource datasource-azure deleted.' - name: Delete azure datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-azure grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" - org_id: '1' + grafana_password: "{{ grafana_password }}" + org_id: "1" ds_type: grafana-azure-monitor-datasource ds_url: http://example.com azure_client: client1 @@ -115,9 +116,9 @@ azure_cloud: azuremonitor state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/cloudwatch.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/cloudwatch.yml index 50268ea8f..79408b68b 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/cloudwatch.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/cloudwatch.yml @@ -1,11 +1,12 @@ +--- - name: Create cloudwatch datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-cloudwatch grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" - org_id: '1' + grafana_password: "{{ grafana_password }}" + org_id: "1" ds_type: cloudwatch ds_url: http://monitoring.us-west-1.amazonaws.com aws_auth_type: keys @@ -14,38 +15,38 @@ aws_secret_key: mel10n aws_custom_metrics_namespaces: n1,n2 -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-cloudwatch created'" - - result.datasource.access == 'proxy' - - result.datasource.basicAuth == false - - result.datasource.database == '' - - result.datasource.isDefault == false - - result.datasource.jsonData.authType == 'keys' - - result.datasource.jsonData.customMetricsNamespaces == 'n1,n2' - - result.datasource.jsonData.defaultRegion == 'us-west-1' - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.name == 'datasource-cloudwatch' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'cloudwatch' - - result.datasource.url == 'http://monitoring.us-west-1.amazonaws.com' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - result.changed + - result.msg == 'Datasource datasource-cloudwatch created' + - result.datasource.access == 'proxy' + - result.datasource.basicAuth == false + - result.datasource.database == '' + - result.datasource.isDefault == false + - result.datasource.jsonData.authType == 'keys' + - result.datasource.jsonData.customMetricsNamespaces == 'n1,n2' + - result.datasource.jsonData.defaultRegion == 'us-west-1' + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.name == 'datasource-cloudwatch' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'cloudwatch' + - result.datasource.url == 'http://monitoring.us-west-1.amazonaws.com' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Check cloudwatch datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-cloudwatch grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: cloudwatch ds_url: http://monitoring.us-west-1.amazonaws.com aws_auth_type: keys @@ -54,58 +55,58 @@ aws_secret_key: mel10n aws_custom_metrics_namespaces: n1,n2 -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.access == 'proxy' - - result.datasource.basicAuth == false - - result.datasource.database == '' - - result.datasource.isDefault == false - - result.datasource.jsonData.authType == 'keys' - - result.datasource.jsonData.customMetricsNamespaces == 'n1,n2' - - result.datasource.jsonData.defaultRegion == 'us-west-1' - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.name == 'datasource-cloudwatch' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'cloudwatch' - - result.datasource.url == 'http://monitoring.us-west-1.amazonaws.com' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.access == 'proxy' + - result.datasource.basicAuth == false + - result.datasource.database == '' + - result.datasource.isDefault == false + - result.datasource.jsonData.authType == 'keys' + - result.datasource.jsonData.customMetricsNamespaces == 'n1,n2' + - result.datasource.jsonData.defaultRegion == 'us-west-1' + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.name == 'datasource-cloudwatch' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'cloudwatch' + - result.datasource.url == 'http://monitoring.us-west-1.amazonaws.com' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Delete cloudwatch datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-cloudwatch grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-cloudwatch deleted.'" + - result.changed + - result.msg == 'Datasource datasource-cloudwatch deleted.' - name: Delete cloudwatch datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-cloudwatch grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/elastic.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/elastic.yml index 73b258426..ff05b93f5 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/elastic.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/elastic.yml @@ -1,313 +1,313 @@ --- - name: Create elasticsearch datasource with legacy elasticsearch format register: result - grafana_datasource: - name: "datasource/elasticLegacy" - uid: "myuid" + community.grafana.grafana_datasource: + name: datasource/elasticLegacy + uid: myuid grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.company.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily es_version: 56 max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == 56 - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elasticLegacy' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.company.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials - - "result.msg == 'Datasource datasource/elasticLegacy created'" + - result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == 56 + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elasticLegacy' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.company.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.msg == 'Datasource datasource/elasticLegacy created' - name: Create elasticsearch datasource with new elsaticsearch version format register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.company.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily - es_version: "7.10+" + es_version: 7.10+ max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == "7.10.0" - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elastic' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.company.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials - - "result.msg == 'Datasource datasource/elastic created'" + - result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == "7.10.0" + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elastic' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.company.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.msg == 'Datasource datasource/elastic created' - name: Check elasticsearch datasource creation idempotency register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.company.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily - es_version: "7.10+" + es_version: 7.10+ max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == '7.10.0' - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elastic' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.company.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials + - not result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == '7.10.0' + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elastic' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.company.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials -- name: update elasticsearch datasource creation +- name: Update elasticsearch datasource creation register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.example.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily - es_version: "7.10+" + es_version: 7.10+ max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == '7.10.0' - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elastic' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.example.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials + - result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == '7.10.0' + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elastic' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.example.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials -- name: update elasticsearch datasource (ignoring secureJsonData) +- name: Update elasticsearch datasource (ignoring secureJsonData) register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.example.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily - es_version: "7.10+" + es_version: 7.10+ max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem enforce_secure_data: false additional_json_data: - nonSecureTest: "nonsecure" + nonSecureTest: nonsecure additional_secure_json_data: - secureTest: "secure" + secureTest: secure -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == '7.10.0' - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elastic' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.example.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials - - result.datasource.jsonData.nonSecureTest == 'nonsecure' + - result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == '7.10.0' + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elastic' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.example.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.datasource.jsonData.nonSecureTest == 'nonsecure' -- name: update elasticsearch datasource (including secureJsonData) +- name: Update elasticsearch datasource (including secureJsonData) register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.example.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily - es_version: "7.10+" + es_version: 7.10+ max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem enforce_secure_data: true additional_json_data: - nonSecureTest: "nonsecure" + nonSecureTest: nonsecure additional_secure_json_data: - secureTest: "secure" + secureTest: secure -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == '7.10.0' - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elastic' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.example.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials - - result.datasource.jsonData.nonSecureTest == 'nonsecure' - - result.datasource.secureJsonFields.secureTest == true - - result.diff.after.secureJsonData is defined + - result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == '7.10.0' + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elastic' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.example.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.datasource.jsonData.nonSecureTest == 'nonsecure' + - result.datasource.secureJsonFields.secureTest == true + - result.diff.after.secureJsonData is defined - name: Delete elasticsearch datasource register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete elasticsearch datasource (idempotency) register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/errors.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/errors.yml index 731a19d8f..779b4724d 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/errors.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/errors.yml @@ -1,18 +1,18 @@ --- - name: Create datasource without `ds_type` and `ds_url` (expect failure) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-postgres grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" ignore_errors: true -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.failed - - "result.msg == 'state is present but all of the following are missing: ds_type, ds_url'" + - not result.changed + - result.failed + - "result.msg == 'state is present but all of the following are missing: ds_type, ds_url'" diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/influx.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/influx.yml index da61833f6..7952bec6b 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/influx.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/influx.yml @@ -1,88 +1,89 @@ +--- - name: Create influxdb datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-influxdb grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: influxdb ds_url: https://influx.company.com:8086 database: telegraf - time_interval: '>10s' + time_interval: ">10s" tls_ca_cert: /etc/ssl/certs/ca.pem -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-influxdb created'" + - result.changed + - result.msg == 'Datasource datasource-influxdb created' - name: Check influxdb datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-influxdb grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: influxdb ds_url: https://influx.company.com:8086 database: telegraf - time_interval: '>10s' + time_interval: ">10s" tls_ca_cert: /etc/ssl/certs/ca.pem -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.basicAuth == false - - result.datasource.access == 'proxy' - - result.datasource.database == 'telegraf' - - result.datasource.isDefault == false - - result.datasource.jsonData.timeInterval == '>10s' - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource-influxdb' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'influxdb' - - result.datasource.url == 'https://influx.company.com:8086' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.basicAuth == false + - result.datasource.access == 'proxy' + - result.datasource.database == 'telegraf' + - result.datasource.isDefault == false + - result.datasource.jsonData.timeInterval == '>10s' + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource-influxdb' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'influxdb' + - result.datasource.url == 'https://influx.company.com:8086' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Delete influxdb datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-influxdb grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete influxdb datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-influxdb grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/issues.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/issues.yml index f80677d8d..3b4df5a71 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/issues.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/issues.yml @@ -1,46 +1,43 @@ --- - -- name: test datasource name with slash +- name: Test datasource name with slash register: result - grafana_datasource: - name: "datasource/elastic" + community.grafana.grafana_datasource: + name: datasource/elastic grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: elasticsearch ds_url: https://elastic.company.com:9200 - database: '[logstash_]YYYY.MM.DD' + database: "[logstash_]YYYY.MM.DD" basic_auth_user: grafana - basic_auth_password: '******' - time_field: '@timestamp' + basic_auth_password: "******" + time_field: "@timestamp" time_interval: 1m interval: Daily es_version: 56 max_concurrent_shard_requests: 42 tls_ca_cert: /etc/ssl/certs/ca.pem -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.basicAuth - - result.datasource.basicAuthUser == 'grafana' - - result.datasource.access == 'proxy' - - result.datasource.database == '[logstash_]YYYY.MM.DD' - - not result.datasource.isDefault - - result.datasource.jsonData.esVersion == 56 - - result.datasource.jsonData.interval == 'Daily' - - result.datasource.jsonData.maxConcurrentShardRequests == 42 - - result.datasource.jsonData.timeField == '@timestamp' - - not result.datasource.jsonData.tlsAuth - - not result.datasource.jsonData.tlsAuthWithCACert - - result.datasource.name == 'datasource/elastic' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'elasticsearch' - - result.datasource.url == 'https://elastic.company.com:9200' - - result.datasource.user == '' - - not result.datasource.withCredentials - - "result.msg == 'Datasource datasource/elastic created'" - -... + - result.changed + - result.datasource.basicAuth + - result.datasource.basicAuthUser == 'grafana' + - result.datasource.access == 'proxy' + - result.datasource.database == '[logstash_]YYYY.MM.DD' + - not result.datasource.isDefault + - result.datasource.jsonData.esVersion == 56 + - result.datasource.jsonData.interval == 'Daily' + - result.datasource.jsonData.maxConcurrentShardRequests == 42 + - result.datasource.jsonData.timeField == '@timestamp' + - not result.datasource.jsonData.tlsAuth + - not result.datasource.jsonData.tlsAuthWithCACert + - result.datasource.name == 'datasource/elastic' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'elasticsearch' + - result.datasource.url == 'https://elastic.company.com:9200' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.msg == 'Datasource datasource/elastic created' diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/loki.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/loki.yml index 68eca9802..a501b99d2 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/loki.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/loki.yml @@ -1,82 +1,83 @@ +--- - name: Create loki datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-loki grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" - org_id: '1' + grafana_password: "{{ grafana_password }}" + org_id: "1" ds_type: loki ds_url: https://loki.company.com:3100 -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-loki created'" + - result.changed + - result.msg == 'Datasource datasource-loki created' - name: Check loki datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-loki grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: loki ds_url: https://loki.company.com:3100 -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.basicAuth == false - - result.datasource.access == 'proxy' - - result.datasource.database == '' - - result.datasource.isDefault == false - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.name == 'datasource-loki' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'loki' - - result.datasource.url == 'https://loki.company.com:3100' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.basicAuth == false + - result.datasource.access == 'proxy' + - result.datasource.database == '' + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.name == 'datasource-loki' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'loki' + - result.datasource.url == 'https://loki.company.com:3100' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Delete loki datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-loki grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-loki deleted.'" + - result.changed + - result.msg == 'Datasource datasource-loki deleted.' - name: Delete loki datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-loki grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/main.yml index b5798e9a8..0b3ea4cd6 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/main.yml @@ -1,16 +1,24 @@ --- - - block: - - include: errors.yml - - include: elastic.yml - - include: influx.yml - - include: postgres.yml - - include: cloudwatch.yml - - include: thruk.yml - - include: loki.yml - - include: zabbix.yml - - include: redis.yml - - include: azure.yml - - include: uid.yml - -... + - ansible.builtin.include_tasks: + file: errors.yml + - ansible.builtin.include_tasks: + file: elastic.yml + - ansible.builtin.include_tasks: + file: influx.yml + - ansible.builtin.include_tasks: + file: postgres.yml + - ansible.builtin.include_tasks: + file: cloudwatch.yml + - ansible.builtin.include_tasks: + file: thruk.yml + - ansible.builtin.include_tasks: + file: loki.yml + - ansible.builtin.include_tasks: + file: zabbix.yml + - ansible.builtin.include_tasks: + file: redis.yml + - ansible.builtin.include_tasks: + file: azure.yml + - ansible.builtin.include_tasks: + file: uid.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/postgres.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/postgres.yml index 80221f012..b07bc752b 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/postgres.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/postgres.yml @@ -1,11 +1,12 @@ +--- - name: Create postgres datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-postgres grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: postgres ds_url: postgres.company.com:5432 database: db @@ -15,22 +16,22 @@ additional_json_data: timescaledb: true -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-postgres created'" + - result.changed + - result.msg == 'Datasource datasource-postgres created' - name: Check postgres datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-postgres grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: postgres ds_url: postgres.company.com:5432 database: db @@ -40,54 +41,54 @@ additional_json_data: timescaledb: true -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.basicAuth == false - - result.datasource.database == 'db' - - result.datasource.isDefault == false - - result.datasource.jsonData.sslmode == 'verify-full' - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.jsonData.timescaledb == true - - result.datasource.name == 'datasource-postgres' - - result.datasource.orgId == 1 - - result.datasource.type == 'postgres' - - result.datasource.url == 'postgres.company.com:5432' - - result.datasource.user == 'postgres' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.basicAuth == false + - result.datasource.database == 'db' + - result.datasource.isDefault == false + - result.datasource.jsonData.sslmode == 'verify-full' + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.jsonData.timescaledb == true + - result.datasource.name == 'datasource-postgres' + - result.datasource.orgId == 1 + - result.datasource.type == 'postgres' + - result.datasource.url == 'postgres.company.com:5432' + - result.datasource.user == 'postgres' + - result.datasource.withCredentials == false - name: Delete postgres datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-postgres grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete postgres datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-postgres grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/quickwit.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/quickwit.yml new file mode 100644 index 000000000..8221dcb42 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/quickwit.yml @@ -0,0 +1,137 @@ +--- +- name: Create Quickwit datasource + register: result + community.grafana.grafana_datasource: + name: Quickwit + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + org_id: "1" + ds_type: quickwit-quickwit-datasource + ds_url: http://localhost:7280/api/v1 + additional_json_data: + index: hdfs-logs + timeField: timestamp + timeOutputFormat: unix_timestamp_secs + logMessageField: body + logLevelField: severity_text + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - result.changed + - result.datasource.access == 'proxy' + - not result.datasource.isDefault + - result.datasource.database == '' + - result.datasource.name == 'Quickwit' + - result.datasource.orgId == 1 + - result.datasource.type == 'quickwit-quickwit-datasource' + - result.datasource.url == 'http://localhost:7280/api/v1' + - result.msg == 'Datasource Quickwit created' + - result.datasource.jsonData.index == 'hdfs-logs' + - result.datasource.jsonData.timeField == 'timestamp' + - result.datasource.jsonData.timeOutputFormat == 'unix_timestamp_secs' + - result.datasource.jsonData.logMessageField == 'body' + - result.datasource.jsonData.logLevelField == 'severity_text' + +- name: Check Quickwit datasource creation (idempotency) + register: result + community.grafana.grafana_datasource: + name: Quickwit + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + org_id: "1" + ds_type: quickwit-quickwit-datasource + ds_url: http://localhost:7280/api/v1 + additional_json_data: + index: hdfs-logs + timeField: timestamp + timeOutputFormat: unix_timestamp_secs + logMessageField: body + logLevelField: severity_text + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - not result.changed + - result.datasource.access == 'proxy' + - not result.datasource.isDefault + - result.datasource.database == '' + - result.datasource.name == 'Quickwit' + - result.datasource.orgId == 1 + - result.datasource.type == 'quickwit-quickwit-datasource' + - result.datasource.url == 'http://localhost:7280/api/v1' + - result.msg == 'Datasource Quickwit created' + - result.datasource.jsonData.index == 'hdfs-logs' + - result.datasource.jsonData.timeField == 'timestamp' + - result.datasource.jsonData.timeOutputFormat == 'unix_timestamp_secs' + - result.datasource.jsonData.logMessageField == 'body' + - result.datasource.jsonData.logLevelField == 'severity_text' + +- name: Update Quickwit datasource + register: result + community.grafana.grafana_datasource: + name: Quickwit + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + org_id: "1" + ds_type: quickwit-quickwit-datasource + ds_url: http://quickwit-url:7280/api/v1 + additional_json_data: + index: hdfs-logs + timeField: timestamp + timeOutputFormat: unix_timestamp_millis + logMessageField: body + logLevelField: severity_text + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - result.changed + - result.datasource.access == 'proxy' + - not result.datasource.isDefault + - result.datasource.database == '' + - result.datasource.name == 'Quickwit' + - result.datasource.orgId == 1 + - result.datasource.type == 'quickwit-quickwit-datasource' + - result.datasource.url == 'http://quickwit-url:7280/api/v1' + - result.msg == 'Datasource Quickwit created' + - result.datasource.jsonData.index == 'hdfs-logs' + - result.datasource.jsonData.timeField == 'timestamp' + - result.datasource.jsonData.timeOutputFormat == 'unix_timestamp_millis' + - result.datasource.jsonData.logMessageField == 'body' + - result.datasource.jsonData.logLevelField == 'severity_text' + +- name: Delete Quickwit datasource + register: result + community.grafana.grafana_datasource: + name: Quickwit + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + +- ansible.builtin.assert: + that: + - result.changed + +- name: Delete Quickwit datasource (idempotency) + register: result + community.grafana.grafana_datasource: + name: Quickwit + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + +- ansible.builtin.assert: + that: + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/redis.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/redis.yml index 933695354..dc39be64e 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/redis.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/redis.yml @@ -1,101 +1,102 @@ +--- - name: Create redis datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-redis grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: redis-datasource ds_url: https://redis.company.com:6379 time_interval: 1m -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.access == 'proxy' - - not result.datasource.isDefault - - result.datasource.name == 'datasource-redis' - - result.datasource.orgId == 1 - - result.datasource.type == 'redis-datasource' - - result.datasource.url == 'https://redis.company.com:6379' - - "result.msg == 'Datasource datasource-redis created'" + - result.changed + - result.datasource.access == 'proxy' + - not result.datasource.isDefault + - result.datasource.name == 'datasource-redis' + - result.datasource.orgId == 1 + - result.datasource.type == 'redis-datasource' + - result.datasource.url == 'https://redis.company.com:6379' + - result.msg == 'Datasource datasource-redis created' - name: Check redis-datasource datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-redis grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: redis-datasource ds_url: https://redis.company.com:6379 time_interval: 1m -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.access == 'proxy' - - not result.datasource.isDefault - - result.datasource.name == 'datasource-redis' - - result.datasource.orgId == 1 - - result.datasource.type == 'redis-datasource' - - result.datasource.url == 'https://redis.company.com:6379' + - not result.changed + - result.datasource.access == 'proxy' + - not result.datasource.isDefault + - result.datasource.name == 'datasource-redis' + - result.datasource.orgId == 1 + - result.datasource.type == 'redis-datasource' + - result.datasource.url == 'https://redis.company.com:6379' -- name: update redis-datasource datasource creation +- name: Update redis-datasource datasource creation register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-redis grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: redis-datasource ds_url: https://redisnew.company.com:6379 time_interval: 1m -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.access == 'proxy' - - not result.datasource.isDefault - - result.datasource.name == 'datasource-redis' - - result.datasource.orgId == 1 - - result.datasource.type == 'redis-datasource' - - result.datasource.url == 'https://redisnew.company.com:6379' + - result.changed + - result.datasource.access == 'proxy' + - not result.datasource.isDefault + - result.datasource.name == 'datasource-redis' + - result.datasource.orgId == 1 + - result.datasource.type == 'redis-datasource' + - result.datasource.url == 'https://redisnew.company.com:6379' -- name: Delete redis-datasource datasource +- name: Delete redis-datasource datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-redis grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete redis-datasource datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-redis grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/tempo.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/tempo.yml new file mode 100644 index 000000000..51ab52513 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/tempo.yml @@ -0,0 +1,79 @@ +--- +- name: Create tempo datasource + register: result + community.grafana.grafana_datasource: + name: datasource-tempo + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + org_id: "1" + ds_type: tempo + ds_url: tempo.company.com:3100 + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - result.changed + - result.msg == 'Datasource datasource-tempo created' + +- name: Check tempo datasource creation idempotency + register: result + community.grafana.grafana_datasource: + name: datasource-tempo + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + org_id: "1" + ds_type: tempo + ds_url: tempo.company.com:3100 + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - not result.changed + - result.datasource.basicAuth == false + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.secureJsonFields.httpHeaderValue1 == true + - result.datasource.name == 'datasource-tempo' + - result.datasource.orgId == 1 + - result.datasource.type == 'tempo' + - result.datasource.url == 'tempo.company.com:3100' + - result.datasource.withCredentials == false + +- name: Delete tempo datasource + register: result + community.grafana.grafana_datasource: + name: datasource-tempo + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - result.changed + +- name: Delete tempo datasource + register: result + community.grafana.grafana_datasource: + name: datasource-tempo + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password }}" + state: absent + +- ansible.builtin.debug: + var: result + +- ansible.builtin.assert: + that: + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/thruk.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/thruk.yml index 3ecc92d94..74f7c4dfa 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/thruk.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/thruk.yml @@ -1,78 +1,79 @@ +--- - name: Create thruk datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-thruk grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" org_id: "1" ds_type: sni-thruk-datasource - ds_url: "https://thruk.company.com/sitename/thruk" - tls_skip_verify: yes - validate_certs: no + ds_url: https://thruk.company.com/sitename/thruk + tls_skip_verify: true + validate_certs: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-thruk created'" + - result.changed + - result.msg == 'Datasource datasource-thruk created' - name: Check thruk datasource creation idempotency register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-thruk grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: sni-thruk-datasource - ds_url: "https://thruk.company.com/sitename/thruk" - tls_skip_verify: yes - validate_certs: no + ds_url: https://thruk.company.com/sitename/thruk + tls_skip_verify: true + validate_certs: false -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.basicAuth == false - - result.datasource.access == 'proxy' - - result.datasource.isDefault == false - - result.datasource.jsonData.tlsAuth == false - - result.datasource.jsonData.tlsAuthWithCACert == false - - result.datasource.name == 'datasource-thruk' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'sni-thruk-datasource' - - result.datasource.url == 'https://thruk.company.com/sitename/thruk' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.basicAuth == false + - result.datasource.access == 'proxy' + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.jsonData.tlsAuthWithCACert == false + - result.datasource.name == 'datasource-thruk' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'sni-thruk-datasource' + - result.datasource.url == 'https://thruk.company.com/sitename/thruk' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Delete thruk datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-thruk grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete thruk datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-thruk grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/uid.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/uid.yml index 71102a074..7a472b42f 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/uid.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/uid.yml @@ -1,253 +1,254 @@ +--- - name: Create datasource with uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-with-uid uid: uid1 grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-with-uid created'" + - result.changed + - result.msg == 'Datasource datasource-with-uid created' - name: Create datasource without uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-without-uid grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - "result.msg == 'Datasource datasource-without-uid created'" + - result.changed + - result.msg == 'Datasource datasource-without-uid created' - name: Check datasource creation idempotency with uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-with-uid uid: uid1 grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.access == 'proxy' - - result.datasource.isDefault == false - - result.datasource.jsonData.tlsAuth == false - - result.datasource.name == 'datasource-with-uid' - - result.datasource.uid == 'uid1' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'prometheus' - - result.datasource.url == 'https://prometheus.company.com:8086' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.access == 'proxy' + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.name == 'datasource-with-uid' + - result.datasource.uid == 'uid1' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'prometheus' + - result.datasource.url == 'https://prometheus.company.com:8086' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Check datasource creation idempotency without uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-without-uid grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - result.datasource.access == 'proxy' - - result.datasource.isDefault == false - - result.datasource.jsonData.tlsAuth == false - - result.datasource.name == 'datasource-without-uid' - - result.datasource.uid - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'prometheus' - - result.datasource.url == 'https://prometheus.company.com:8086' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - not result.changed + - result.datasource.access == 'proxy' + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.name == 'datasource-without-uid' + - result.datasource.uid + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'prometheus' + - result.datasource.url == 'https://prometheus.company.com:8086' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Set uid for datasource with random uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-without-uid uid: uid3 grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.access == 'proxy' - - result.datasource.isDefault == false - - result.datasource.jsonData.tlsAuth == false - - result.datasource.name == 'datasource-without-uid' - - result.datasource.uid == 'uid3' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'prometheus' - - result.datasource.url == 'https://prometheus.company.com:8086' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - result.changed + - result.datasource.access == 'proxy' + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.name == 'datasource-without-uid' + - result.datasource.uid == 'uid3' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'prometheus' + - result.datasource.url == 'https://prometheus.company.com:8086' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Change uid for datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-with-uid uid: uid2 grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - result.datasource.access == 'proxy' - - result.datasource.isDefault == false - - result.datasource.jsonData.tlsAuth == false - - result.datasource.name == 'datasource-with-uid' - - result.datasource.uid == 'uid2' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'prometheus' - - result.datasource.url == 'https://prometheus.company.com:8086' - - result.datasource.user == '' - - result.datasource.withCredentials == false + - result.changed + - result.datasource.access == 'proxy' + - result.datasource.isDefault == false + - result.datasource.jsonData.tlsAuth == false + - result.datasource.name == 'datasource-with-uid' + - result.datasource.uid == 'uid2' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'prometheus' + - result.datasource.url == 'https://prometheus.company.com:8086' + - result.datasource.user == '' + - result.datasource.withCredentials == false - name: Delete datasource-with-uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-with-uid uid: uid1 grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete datasource-without-uid register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-without-uid grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete datasource-with-uid (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-with-uid uid: uid1 grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: prometheus ds_url: https://prometheus.company.com:8086 - basic_auth_user: "admin" - basic_auth_password: "admin" - validate_certs: False - is_default: no + basic_auth_user: admin + basic_auth_password: admin + validate_certs: false + is_default: false state: absent -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/zabbix.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/zabbix.yml index c8a8236bf..d238efb3e 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/zabbix.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/zabbix.yml @@ -1,114 +1,115 @@ +--- - name: Create zabbix datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-zabbix grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: alexanderzobnin-zabbix-datasource ds_url: https://zabbix.company.com zabbix_user: grafana - zabbix_password: '******' + zabbix_password: "******" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - not result.datasource.isDefault - - result.datasource.jsonData.username == 'grafana' - - result.datasource.name == 'datasource-zabbix' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'alexanderzobnin-zabbix-datasource' - - result.datasource.url == 'https://zabbix.company.com' - - result.datasource.user == '' - - not result.datasource.withCredentials - - "result.msg == 'Datasource datasource-zabbix created'" + - result.changed + - not result.datasource.isDefault + - result.datasource.jsonData.username == 'grafana' + - result.datasource.name == 'datasource-zabbix' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'alexanderzobnin-zabbix-datasource' + - result.datasource.url == 'https://zabbix.company.com' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.msg == 'Datasource datasource-zabbix created' - name: Create zabbix datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-zabbix grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: alexanderzobnin-zabbix-datasource ds_url: https://zabbix.company.com zabbix_user: grafana - zabbix_password: '******' + zabbix_password: "******" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - not result.changed - - not result.datasource.isDefault - - result.datasource.jsonData.username == 'grafana' - - result.datasource.name == 'datasource-zabbix' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'alexanderzobnin-zabbix-datasource' - - result.datasource.url == 'https://zabbix.company.com' - - result.datasource.user == '' - - not result.datasource.withCredentials + - not result.changed + - not result.datasource.isDefault + - result.datasource.jsonData.username == 'grafana' + - result.datasource.name == 'datasource-zabbix' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'alexanderzobnin-zabbix-datasource' + - result.datasource.url == 'https://zabbix.company.com' + - result.datasource.user == '' + - not result.datasource.withCredentials - name: Update zabbix datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-zabbix grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" - org_id: '1' + org_id: "1" ds_type: alexanderzobnin-zabbix-datasource ds_url: https://zabbix.example.com zabbix_user: grafana - zabbix_password: '******' + zabbix_password: "******" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - result.changed - - not result.datasource.isDefault - - result.datasource.jsonData.username == 'grafana' - - result.datasource.name == 'datasource-zabbix' - - result.datasource.orgId == 1 - - ('password' not in result.datasource) or (result.datasource.password == '') - - result.datasource.type == 'alexanderzobnin-zabbix-datasource' - - result.datasource.url == 'https://zabbix.example.com' - - result.datasource.user == '' - - not result.datasource.withCredentials - - "result.msg == 'Datasource datasource-zabbix updated'" + - result.changed + - not result.datasource.isDefault + - result.datasource.jsonData.username == 'grafana' + - result.datasource.name == 'datasource-zabbix' + - result.datasource.orgId == 1 + - ('password' not in result.datasource) or (result.datasource.password == '') + - result.datasource.type == 'alexanderzobnin-zabbix-datasource' + - result.datasource.url == 'https://zabbix.example.com' + - result.datasource.user == '' + - not result.datasource.withCredentials + - result.msg == 'Datasource datasource-zabbix updated' - name: Delete zabbix datasource register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-zabbix grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - result.changed + - result.changed - name: Delete zabbix datasource (idempotency) register: result - grafana_datasource: + community.grafana.grafana_datasource: name: datasource-zabbix grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" grafana_password: "{{ grafana_password }}" state: absent -- assert: +- ansible.builtin.assert: that: - - not result.changed + - not result.changed diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/defaults/main.yml index 8b9c9348a..4abf9bb43 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/defaults/main.yml @@ -1,7 +1,5 @@ --- -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" - -... +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/runme.sh new file mode 100755 index 000000000..9daa487db --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml --check +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/site.yml new file mode 100644 index 000000000..2041400ea --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_folder + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_folder diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/create-delete.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/create-delete.yml new file mode 100644 index 000000000..c818c2ef1 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/create-delete.yml @@ -0,0 +1,58 @@ +--- +- 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 + +- 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 + +- 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 + +- 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 + +- ansible.builtin.assert: + that: + - result.changed == false + when: not ansible_check_mode diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/main.yml index c6a520560..0ac91f375 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/main.yml @@ -1,55 +1,6 @@ --- +- name: Folder creation and deletion + ansible.builtin.include_tasks: create-delete.yml -- name: Create a Folder - grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: "grafana_working_group" - state: present - register: result - -- assert: - that: - - "result.changed == true" - - "result.folder.title == 'grafana_working_group'" - -- name: Test folder creation idempotency - grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: "grafana_working_group" - state: present - register: result - -- assert: - that: - - "result.changed == false" - - "result.folder.title == 'grafana_working_group'" - -- name: Delete a Folder - grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: "grafana_working_group" - state: absent - register: result - -- assert: - that: - - "result.changed == true" - -- name: Test folder deletion idempotency - grafana_folder: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - title: "grafana_working_group" - state: absent - register: result - -- assert: - that: - - "result.changed == false" +- name: Folder creation and deletion for organization + ansible.builtin.include_tasks: org.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/org.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/org.yml new file mode 100644 index 000000000..f30d48c14 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_folder/tasks/org.yml @@ -0,0 +1,7 @@ +--- +- module_defaults: + community.grafana.grafana_folder: + org_name: Main Org. + block: + - name: Folder creation and deletion + ansible.builtin.include_tasks: create-delete.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/defaults/main.yml index 8b9c9348a..4abf9bb43 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/defaults/main.yml @@ -1,7 +1,5 @@ --- -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" - -... +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/runme.sh new file mode 100755 index 000000000..867afb0d3 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/site.yml new file mode 100644 index 000000000..e20f44e32 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_notification_channel + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_notification_channel diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml index 7279e313b..80d2f07d8 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml @@ -1,19 +1,19 @@ --- - name: Create dingding notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: dingding name: dingding type: dingding dingding_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == True - result.channel.name == "dingding" @@ -22,19 +22,19 @@ - name: Create dingding notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: dingding name: dingding type: dingding dingding_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == False - result.channel.name == "dingding" @@ -43,32 +43,32 @@ - name: Delete dingding notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: dingding state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == True - name: Delete dingding notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: dingding state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/discord.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/discord.yml index 29cfece70..30c70d63e 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/discord.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/discord.yml @@ -1,19 +1,19 @@ --- - name: Create discord notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: discord name: discord type: discord discord_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == True - result.channel.name == "discord" @@ -22,19 +22,19 @@ - name: Create discord notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: discord name: discord type: discord discord_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == False - result.channel.name == "discord" @@ -43,32 +43,32 @@ - name: Delete discord notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: discord state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == True - name: Delete discord notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: discord state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/email.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/email.yml index 85a236c9e..29e188755 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/email.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/email.yml @@ -1,7 +1,7 @@ --- - name: Create email notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: email name: email type: email @@ -10,21 +10,21 @@ - bar@example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "email" - result.channel.type == "email" - result.channel.uid == "email" - name: Create email notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: email name: email type: email @@ -33,47 +33,47 @@ - bar@example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "email" - result.channel.type == "email" - result.channel.uid == "email" - name: Delete discord notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: email state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" - - "result.state == 'absent'" + - result.changed == True + - result.state == 'absent' - name: Delete discord notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: email state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml index abc7db644..8acdf0a68 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml @@ -1,74 +1,74 @@ --- - name: Create googlechat notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: googlechat name: googlechat type: googlechat googlechat_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "googlechat" - result.channel.uid == "googlechat" - result.channel.type == "googlechat" - name: Create googlechat notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: googlechat name: googlechat type: googlechat googlechat_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "googlechat" - result.channel.uid == "googlechat" - result.channel.type == "googlechat" - name: Delete googlechat notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: googlechat state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete googlechat notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: googlechat state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml index 4a9ec1704..6423cafc8 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml @@ -1,74 +1,74 @@ --- - name: Create hipchat notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: hipchat name: hipchat type: hipchat hipchat_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "hipchat" - result.channel.type == "hipchat" - result.channel.uid == "hipchat" - name: Create hipchat notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: hipchat name: hipchat type: hipchat hipchat_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "hipchat" - result.channel.type == "hipchat" - result.channel.uid == "hipchat" - name: Delete hipchat notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: hipchat state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete hipchat notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: hipchat state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml index ffc208acb..d8ec9cad3 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml @@ -1,7 +1,7 @@ --- - name: Create kafka notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: kafka name: kafka type: kafka @@ -9,21 +9,21 @@ kafka_topic: test grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "kafka" - result.channel.uid == "kafka" - result.channel.type == "kafka" - name: Create kafka notification channel (idempotentcy) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: kafka name: kafka type: kafka @@ -31,46 +31,46 @@ kafka_topic: test grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "kafka" - result.channel.uid == "kafka" - result.channel.type == "kafka" - name: Delete kafka notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: kafka state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete kafka notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: kafka state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/line.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/line.yml index 83a1863ba..dde928c3e 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/line.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/line.yml @@ -1,74 +1,74 @@ --- - name: Create line notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: line name: line type: line line_token: xxx grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.uid == "line" - result.channel.name == "line" - result.channel.type == "line" - name: Create line notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: line name: line type: line line_token: xxx grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.uid == "line" - result.channel.name == "line" - result.channel.type == "line" - name: Delete line notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: line state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete line notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: line state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/main.yml index ada6338c7..af0b2f861 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/main.yml @@ -1,20 +1,34 @@ --- - block: - - include: dingding.yml - - include: discord.yml - - include: email.yml - - include: googlechat.yml - - include: hipchat.yml - - include: kafka.yml -# - include: line.yml - - include: teams.yml - - include: opsgenie.yml - - include: pagerduty.yml - - include: prometheus.yml - - include: pushover.yml - - include: sensu.yml - - include: slack-and-beyond.yml - - include: telegram.yml -# - include: threema.yml - - include: victorops.yml - - include: webhook.yml + - ansible.builtin.include_tasks: + file: dingding.yml + - ansible.builtin.include_tasks: + file: discord.yml + - ansible.builtin.include_tasks: + file: email.yml + - ansible.builtin.include_tasks: + file: googlechat.yml + - ansible.builtin.include_tasks: + file: hipchat.yml + - ansible.builtin.include_tasks: + file: kafka.yml + - ansible.builtin.include_tasks: + file: teams.yml + - ansible.builtin.include_tasks: + file: opsgenie.yml + - ansible.builtin.include_tasks: + file: pagerduty.yml + - ansible.builtin.include_tasks: + file: prometheus.yml + - ansible.builtin.include_tasks: + file: pushover.yml + - ansible.builtin.include_tasks: + file: sensu.yml + - ansible.builtin.include_tasks: + file: slack-and-beyond.yml + - ansible.builtin.include_tasks: + file: telegram.yml + - ansible.builtin.include_tasks: + file: victorops.yml + - ansible.builtin.include_tasks: + file: webhook.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml index f871fe719..cd1048b86 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml @@ -1,7 +1,7 @@ --- - name: Create opsgenie notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: opsgenie name: opsgenie type: opsgenie @@ -9,21 +9,21 @@ opsgenie_api_key: xxx grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "opsgenie" - result.channel.type == "opsgenie" - result.channel.uid == "opsgenie" - name: Create opsgenie notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: opsgenie name: opsgenie type: opsgenie @@ -31,47 +31,47 @@ opsgenie_api_key: xxx grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "opsgenie" - result.channel.type == "opsgenie" - result.channel.uid == "opsgenie" - name: Delete opsgenie notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: opsgenie state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" - - "result.state == 'absent'" + - result.changed == True + - result.state == 'absent' - name: Delete opsgenie notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: opsgenie state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml index a8fa940b0..40fd00468 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml @@ -1,74 +1,74 @@ --- - name: Create pagerduty notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pagerduty name: pagerduty type: pagerduty pagerduty_integration_key: xxx grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "pagerduty" - result.channel.type == "pagerduty" - result.channel.uid == "pagerduty" - name: Create pagerduty notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pagerduty name: pagerduty type: pagerduty pagerduty_integration_key: xxx grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "pagerduty" - result.channel.type == "pagerduty" - result.channel.uid == "pagerduty" - name: Delete pagerduty notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pagerduty state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete pagerduty notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pagerduty state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml index 40810eaf3..c85d26f2e 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml @@ -1,74 +1,74 @@ --- - name: Create prometheus notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: prometheus name: prometheus type: prometheus prometheus_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "prometheus" - result.channel.uid == "prometheus" - result.channel.type == "prometheus-alertmanager" - name: Create prometheus notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: prometheus name: prometheus type: prometheus prometheus_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "prometheus" - result.channel.uid == "prometheus" - result.channel.type == "prometheus-alertmanager" - name: Delete prometheus notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: prometheus state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete prometheus notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: prometheus state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml index 894bd71e6..ee7b9bea4 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml @@ -1,7 +1,7 @@ --- - name: Create pushover notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pushover name: pushover type: pushover @@ -9,21 +9,21 @@ pushover_user_key: yyy grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "pushover" - result.channel.uid == "pushover" - result.channel.type == "pushover" - name: Create pushover notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pushover name: pushover type: pushover @@ -31,46 +31,46 @@ pushover_user_key: yyy grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "pushover" - result.channel.uid == "pushover" - result.channel.type == "pushover" - name: Delete pushover notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pushover state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete pushover notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: pushover state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml index 01619b719..a46ef1d0a 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml @@ -1,74 +1,74 @@ --- - name: Create sensu notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: sensu name: sensu type: sensu sensu_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "sensu" - result.channel.type == "sensu" - result.channel.uid == "sensu" - name: Create sensu notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: sensu name: sensu type: sensu sensu_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "sensu" - result.channel.type == "sensu" - result.channel.uid == "sensu" - name: Delete sensu notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: sensu state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete sensu notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: sensu state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml index 0748c8bdb..1cbab6d5c 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml @@ -1,110 +1,110 @@ --- - name: Create slack notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: slack name: slack type: slack slack_url: https://hooks.slack.com/services/xxx/yyy/zzz grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "slack" - result.channel.type == "slack" - result.channel.uid == "slack" - name: Create slack notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: slack name: slack type: slack slack_url: https://hooks.slack.com/services/xxx/yyy/zzz grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "slack" - result.channel.type == "slack" - result.channel.uid == "slack" - name: Check slack notification channel idempotency register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: slack name: slack type: slack slack_url: https://hooks.slack.com/services/xxx/yyy/zzz grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - name: Update slack notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: slack name: slack type: slack slack_url: https://hooks.slack.com/services/xxx/yyy/fff grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete slack notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: state: absent uid: slack grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete slack notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: state: absent uid: slack grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/teams.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/teams.yml index cfc44aef3..d9c7471c5 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/teams.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/teams.yml @@ -1,74 +1,74 @@ --- - name: Create teams notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: teams name: teams type: teams teams_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "teams" - result.channel.uid == "teams" - result.channel.type == "teams" - name: Create teams notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: teams name: teams type: teams teams_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "teams" - result.channel.uid == "teams" - result.channel.type == "teams" - name: Delete teams notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: teams state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete teams notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: teams state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml index 98a7a3c6d..38e27ef57 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml @@ -1,7 +1,7 @@ --- - name: Create telegram notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: telegram name: telegram type: telegram @@ -9,21 +9,21 @@ telegram_chat_id: yyy grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "telegram" - result.channel.type == "telegram" - result.channel.uid == "telegram" - name: Create telegram notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: telegram name: telegram type: telegram @@ -31,46 +31,46 @@ telegram_chat_id: yyy grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "telegram" - result.channel.type == "telegram" - result.channel.uid == "telegram" - name: Delete telegram notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: telegram state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete telegram notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: telegram state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/threema.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/threema.yml index 7858b6977..c61eae316 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/threema.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/threema.yml @@ -1,7 +1,7 @@ --- - name: Create threema notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: threema name: threema type: threema @@ -10,21 +10,21 @@ threema_api_secret: zzz grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "threema" - result.channel.type == "threema" - result.channel.uid == "threema" - name: Create threema notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: threema name: threema type: threema @@ -33,46 +33,46 @@ threema_api_secret: zzz grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "threema" - result.channel.type == "threema" - result.channel.uid == "threema" - name: Delete threema notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: threema state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete threema notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: threema state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml index 3779e813a..e21679132 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml @@ -1,74 +1,74 @@ --- - name: Create victorops notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: victorops name: victorops type: victorops victorops_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "victorops" - result.channel.type == "victorops" - result.channel.uid == "victorops" - name: Create victorops notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: victorops name: victorops type: victorops victorops_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "victorops" - result.channel.type == "victorops" - result.channel.uid == "victorops" - name: Delete victorops notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: victorops state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete victorops notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: victorops state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml index c9efd9193..5a00878f9 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml @@ -1,74 +1,74 @@ --- - name: Create webhook notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: webhook name: webhook type: webhook webhook_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - result.channel.name == "webhook" - result.channel.uid == "webhook" - result.channel.type == "webhook" - name: Create webhook notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: webhook name: webhook type: webhook webhook_url: https://example.org grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False - result.channel.name == "webhook" - result.channel.uid == "webhook" - result.channel.type == "webhook" - name: Delete webhook notification channel register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: webhook state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == True" + - result.changed == True - name: Delete webhook notification channel (idempotency) register: result - grafana_notification_channel: + community.grafana.grafana_notification_channel: uid: webhook state: absent grafana_url: "{{ grafana_url }}" grafana_user: "{{ grafana_username }}" - grafana_password: "{{ grafana_password}}" + grafana_password: "{{ grafana_password }}" -- debug: +- ansible.builtin.debug: var: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == False" + - result.changed == False diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/defaults/main.yml index 7bb77ea3f..3edac8598 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/defaults/main.yml @@ -1,4 +1,4 @@ --- -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/runme.sh new file mode 100755 index 000000000..9daa487db --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml --check +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/site.yml new file mode 100644 index 000000000..e2e4900bd --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_organization + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_organization diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/tasks/main.yml index f8ccdf3bf..936b147cb 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization/tasks/main.yml @@ -1,5 +1,4 @@ --- - - name: Create a Grafana organization community.grafana.grafana_organization: url: "{{ grafana_url }}" @@ -8,13 +7,15 @@ name: orgtest state: present register: result -- assert: + +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.failed == false" - - "result.org.name == 'orgtest'" + - result.changed == true + - result.failed == false + - result.org.name == 'orgtest' + when: not ansible_check_mode -- name: check idempotency Grafana organization +- name: Check idempotency Grafana organization community.grafana.grafana_organization: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" @@ -22,11 +23,13 @@ name: orgtest state: present register: result -- assert: + +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.failed == false" - - "result.org.name == 'orgtest'" + - result.changed == false + - result.failed == false + - result.org.name == 'orgtest' + when: not ansible_check_mode - name: Delete a Grafana organization community.grafana.grafana_organization: @@ -36,13 +39,15 @@ name: orgtest state: absent register: result -- assert: + +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.failed == false" - - "result.msg |length > 0" + - result.changed == true + - result.failed == false + - result.msg |length > 0 + when: not ansible_check_mode -- name: check idempotency delete a Grafana organization +- name: Check idempotency delete a Grafana organization community.grafana.grafana_organization: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" @@ -50,8 +55,10 @@ name: orgtest state: absent register: result -- assert: + +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.failed == false" - - "result.msg == 'No org found, nothing to do'" + - result.changed == false + - result.failed == false + - result.msg == 'No org found, nothing to do' + when: not ansible_check_mode diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/defaults/main.yml new file mode 100644 index 000000000..3edac8598 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/defaults/main.yml @@ -0,0 +1,4 @@ +--- +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/runme.sh new file mode 100755 index 000000000..9daa487db --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml --check +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/site.yml new file mode 100644 index 000000000..80e5ea916 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_organization_user + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_organization_user diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/tasks/main.yml new file mode 100644 index 000000000..73b0482ac --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_organization_user/tasks/main.yml @@ -0,0 +1,172 @@ +--- +- name: Create test user (default org 1) + community.grafana.grafana_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: orgtest + email: orgtest@example.com + login: orgtest + password: userpassword + state: present + register: result + +- name: Remove user from organization (default org 1) + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: orgtest + state: absent + register: result +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + when: not ansible_check_mode + +- name: Check idempotency on user removal from org + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: orgtest + state: absent + register: result +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == false + when: not ansible_check_mode + +- name: Add user to organization + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: orgtest + role: viewer + state: present + register: result +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + - result.user.orgId == 1 + - result.user.role == 'Viewer' + when: not ansible_check_mode + +- name: Update existing user role + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: orgtest + role: editor + state: present + register: result +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + - result.user.orgId == 1 + - result.user.role == 'Editor' + when: not ansible_check_mode + +- name: Check idempotency on user update + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + login: orgtest + role: editor + state: present + register: result +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == false + when: not ansible_check_mode + +- name: Create a new organization + community.grafana.grafana_organization: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: neworgtest + state: present + register: org + +- name: Add user to the new organization by org_id + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_id: "{{ org.org.id }}" + login: orgtest + role: admin + state: present + register: result + when: not ansible_check_mode + +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + - result.user.orgId == org.org.id + - result.user.role == 'Admin' + when: not ansible_check_mode + +- name: Remove user from new organization by org_id + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_id: "{{ org.org.id }}" + login: orgtest + state: absent + register: result + when: not ansible_check_mode + +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + when: not ansible_check_mode + +- name: Add user to the new organization by org_name + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_name: "{{ org.org.name }}" + login: orgtest + role: admin + state: present + register: result + when: not ansible_check_mode + +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + - result.user.orgId == org.org.id + - result.user.role == 'Admin' + when: not ansible_check_mode + +- name: Remove user from new organization by org_name + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_name: "{{ org.org.name }}" + login: orgtest + state: absent + register: result + when: not ansible_check_mode + +- ansible.builtin.assert: + that: + - result.failed == false + - result.changed == true + when: not ansible_check_mode diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/defaults/main.yml index 8b9c9348a..4abf9bb43 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/defaults/main.yml @@ -1,7 +1,5 @@ --- -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" - -... +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/runme.sh new file mode 100755 index 000000000..867afb0d3 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/site.yml new file mode 100644 index 000000000..c8a618891 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_team + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_team diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/create_user.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/create_user.yml index 6971d07b6..9641e7185 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/create_user.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/create_user.yml @@ -1,29 +1,30 @@ +--- - name: Create John Doe for tests purpose through uri module - uri: + ansible.builtin.uri: url: "{{ grafana_url }}api/admin/users" method: POST user: "{{ grafana_username }}" password: "{{ grafana_password }}" - force_basic_auth: yes + force_basic_auth: true body: - name: "John" - email: "john+doe@example.com" - login: "john" - password: "userpassword" + name: John + email: john+doe@example.com + login: john + password: userpassword body_format: json status_code: 200 - name: Create Jane Doe for tests purpose through uri module - uri: + ansible.builtin.uri: url: "{{ grafana_url }}api/admin/users" method: POST user: "{{ grafana_username }}" password: "{{ grafana_password }}" - force_basic_auth: yes + force_basic_auth: true body: - name: "Jane" - email: "jane.doe@example.com" - login: "jane" - password: "userpassword" + name: Jane + email: jane.doe@example.com + login: jane + password: userpassword body_format: json status_code: 200 diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/main.yml index 2d74581b7..d43eb8286 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_team/tasks/main.yml @@ -1,187 +1,198 @@ --- - - name: Create a Team - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + state: present + skip_version_check: true register: result -- set_fact: +- ansible.builtin.set_fact: # From Grafana 9.0.0, the API user automatically becomes a member of the team auto_member: "{{ result.team.memberCount == 1 }}" -- set_fact: +- ansible.builtin.set_fact: expected_members: "{{ auto_member | ternary(['********@localhost'], []) }}" -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" + - result.changed == true + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members + when: not ansible_check_mode - name: Check idempotency on team creation - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" + - result.changed == false + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members + when: not ansible_check_mode - name: Check a team can be deleted - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - state: absent + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + state: absent register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.message == 'Team deleted'" + - result.changed == true + - result.message == 'Team deleted' + when: not ansible_check_mode - name: Check idempotency on team deletion - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - state: absent + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + state: absent register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.message == 'No team found'" + - result.changed == false + - result.message == 'No team found' + when: not ansible_check_mode - name: Create users for tests purpose - import_tasks: create_user.yml + ansible.builtin.import_tasks: + file: create_user.yml - name: Create a Team with members - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - members: - - "john+doe@example.com" - - "jane.doe@example.com" - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + members: + - john+doe@example.com + - jane.doe@example.com + state: present register: result -- set_fact: - expected_members: "{{ auto_member | ternary(['********@localhost', 'jane.doe@example.com', 'john+doe@example.com'], ['jane.doe@example.com', 'john+doe@example.com']) }}" +- ansible.builtin.set_fact: + expected_members: "{{ auto_member | ternary(['********@localhost', 'jane.doe@example.com', 'john+doe@example.com'], ['jane.doe@example.com', 'john+doe@example.com']) + }}" -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" + - result.changed == true + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members + when: not ansible_check_mode - name: Ensure a Team exists with member not enforced - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - members: - - "john+doe@example.com" - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + members: + - john+doe@example.com + state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" - -- set_fact: + - result.changed == false + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members + when: not ansible_check_mode + +- ansible.builtin.set_fact: enforced_members: "{{ auto_member | ternary(['admin@localhost', 'john+doe@example.com'], ['john+doe@example.com']) }}" expected_members: "{{ auto_member | ternary(['********@localhost', 'john+doe@example.com'], ['john+doe@example.com']) }}" - name: Ensure a Team exists with member enforced - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - members: "{{ enforced_members }}" - enforce_members: true - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + members: "{{ enforced_members }}" + enforce_members: true + state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" + - result.changed == true + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members - name: Ensure a Team exists with members omitted - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" + - result.changed == false + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members + when: not ansible_check_mode - name: Add new member to existing Team - grafana_team: - url: "{{ grafana_url }}" - url_username: "{{ grafana_username }}" - url_password: "{{ grafana_password }}" - name: "grafana_working_group" - email: "foo.bar@example.com" - members: - - "john+doe@example.com" - - "jane.doe@example.com" - state: present + community.grafana.grafana_team: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + name: grafana working group + email: foo.bar@example.com + members: + - john+doe@example.com + - jane.doe@example.com + state: present register: result -- set_fact: - expected_members: "{{ auto_member | ternary(['********@localhost', 'jane.doe@example.com', 'john+doe@example.com'], ['jane.doe@example.com', 'john+doe@example.com']) }}" +- ansible.builtin.set_fact: + expected_members: "{{ auto_member | ternary(['********@localhost', 'jane.doe@example.com', 'john+doe@example.com'], ['jane.doe@example.com', 'john+doe@example.com']) + }}" -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.team.name == 'grafana_working_group'" - - "result.team.email == 'foo.bar@example.com'" - - "result.team.memberCount == (expected_members|length)" - - "result.team.members == expected_members" + - result.changed == true + - result.team.name == 'grafana working group' + - result.team.email == 'foo.bar@example.com' + - result.team.memberCount == (expected_members|length) + - result.team.members == expected_members + when: not ansible_check_mode diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_user/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/defaults/main.yml index 7bb77ea3f..3edac8598 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_user/defaults/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/defaults/main.yml @@ -1,4 +1,4 @@ --- -grafana_url: "http://grafana:3000/" -grafana_username: "admin" -grafana_password: "admin" +grafana_url: http://grafana:3000/ +grafana_username: admin +grafana_password: admin diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_user/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/runme.sh new file mode 100755 index 000000000..9daa487db --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/runme.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook site.yml --check +ansible-playbook site.yml diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_user/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/site.yml new file mode 100644 index 000000000..b080803f8 --- /dev/null +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/site.yml @@ -0,0 +1,6 @@ +--- +- name: Run tests for grafana_user + hosts: localhost + tasks: + - ansible.builtin.include_role: + name: ../../grafana_user diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_user/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/tasks/main.yml index c62801653..83a3891e1 100644 --- a/ansible_collections/community/grafana/tests/integration/targets/grafana_user/tasks/main.yml +++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_user/tasks/main.yml @@ -1,237 +1,251 @@ --- - name: Create new admin - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Harley Quinn" + name: Harley Quinn email: harley.quinn@gotham.city login: harley password: Wy3ta6ob6M3wHELv58MPfqOe126RTnWpcYfEhyJm is_admin: true state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.failed == false" - - "result.user.name == 'Harley Quinn'" - - "result.user.email == 'harley.quinn@gotham.city'" - - "result.user.isGrafanaAdmin == true" + - result.changed == true + - result.failed == false + - result.user.name == 'Harley Quinn' + - result.user.email == 'harley.quinn@gotham.city' + - result.user.isGrafanaAdmin == true + when: not ansible_check_mode - name: Check idempotency on admin creation - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Harley Quinn" + name: Harley Quinn email: harley.quinn@gotham.city login: harley password: Wy3ta6ob6M3wHELv58MPfqOe126RTnWpcYfEhyJm is_admin: true state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.user.name == 'Harley Quinn'" - - "result.user.email == 'harley.quinn@gotham.city'" - - "result.user.isGrafanaAdmin == true" + - result.changed == false + - result.user.name == 'Harley Quinn' + - result.user.email == 'harley.quinn@gotham.city' + - result.user.isGrafanaAdmin == true + when: not ansible_check_mode - name: Check user creation with Grafana API - uri: + ansible.builtin.uri: url: "{{ grafana_url }}api/users/lookup?loginOrEmail=harley" user: "{{ grafana_username }}" password: "{{ grafana_password }}" - force_basic_auth: yes + force_basic_auth: true status_code: 200 headers: Accept: application/json Content-Type: application/json register: result -- assert: +- ansible.builtin.assert: that: - - "result.json.name == 'Harley Quinn'" - - "result.json.email == 'harley.quinn@gotham.city'" - - "result.json.isGrafanaAdmin == true" + - result.json.name == 'Harley Quinn' + - result.json.email == 'harley.quinn@gotham.city' + - result.json.isGrafanaAdmin == true + when: not ansible_check_mode - name: Create a Grafana user without password (expect failure) - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Bruce Wayne" + name: Bruce Wayne email: batman@gotham.city login: batman state: present register: result - ignore_errors: yes -- assert: + ignore_errors: true +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.failed == true" + - result.changed == false + - result.failed == true - "result.msg == 'missing required arguments: password'" + when: not ansible_check_mode - name: Create a Grafana user - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Bruce Wayne" + name: Bruce Wayne email: batman@gotham.city login: batman password: robin state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.user.name == 'Bruce Wayne'" - - "result.user.email == 'batman@gotham.city'" - - "result.user.isGrafanaAdmin == false" + - result.changed == true + - result.user.name == 'Bruce Wayne' + - result.user.email == 'batman@gotham.city' + - result.user.isGrafanaAdmin == false + when: not ansible_check_mode - name: Check idempotency on user creation (password not requiered) - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Bruce Wayne" + name: Bruce Wayne email: batman@gotham.city login: batman state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.user.name == 'Bruce Wayne'" - - "result.user.email == 'batman@gotham.city'" - - "result.user.isGrafanaAdmin == false" + - result.changed == false + - result.user.name == 'Bruce Wayne' + - result.user.email == 'batman@gotham.city' + - result.user.isGrafanaAdmin == false + when: not ansible_check_mode - name: Check user creation with Grafana API - uri: + ansible.builtin.uri: url: "{{ grafana_url }}api/users/lookup?loginOrEmail=batman" user: "{{ grafana_username }}" password: "{{ grafana_password }}" - force_basic_auth: yes + force_basic_auth: true status_code: 200 headers: Accept: application/json Content-Type: application/json register: result -- assert: +- ansible.builtin.assert: that: - - "result.json.name == 'Bruce Wayne'" - - "result.json.email == 'batman@gotham.city'" - - "result.json.isGrafanaAdmin == false" + - result.json.name == 'Bruce Wayne' + - result.json.email == 'batman@gotham.city' + - result.json.isGrafanaAdmin == false + when: not ansible_check_mode - name: Update Grafana user - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "The Dark Knight" + name: The Dark Knight email: thedarkknight@gotham.city login: batman password: robin is_admin: true state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.user.name == 'The Dark Knight'" - - "result.user.email == 'thedarkknight@gotham.city'" - - "result.user.isGrafanaAdmin == true" + - result.changed == true + - result.user.name == 'The Dark Knight' + - result.user.email == 'thedarkknight@gotham.city' + - result.user.isGrafanaAdmin == true + when: not ansible_check_mode - name: Check user update with Grafana API - uri: + ansible.builtin.uri: url: "{{ grafana_url }}api/users/lookup?loginOrEmail=batman" user: "{{ grafana_username }}" password: "{{ grafana_password }}" - force_basic_auth: yes + force_basic_auth: true status_code: 200 headers: Accept: application/json Content-Type: application/json register: result -- assert: +- ansible.builtin.assert: that: - - "result.json.name == 'The Dark Knight'" - - "result.json.email == 'thedarkknight@gotham.city'" - - "result.json.isGrafanaAdmin == true" + - result.json.name == 'The Dark Knight' + - result.json.email == 'thedarkknight@gotham.city' + - result.json.isGrafanaAdmin == true + when: not ansible_check_mode - name: Delete a Grafana user - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" login: batman state: absent register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.message == 'User deleted'" + - result.changed == true + - result.message == 'User deleted' + when: not ansible_check_mode - name: Check idempotency on user deletion - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" login: batman state: absent register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.message == 'No user found, nothing to do'" + - result.changed == false + - result.message == 'No user found, nothing to do' + when: not ansible_check_mode - name: Check user deletion with Grafana API (expect 404 Not Found) - uri: + ansible.builtin.uri: url: "{{ grafana_url }}api/users/lookup?loginOrEmail=batman" user: "{{ grafana_username }}" password: "{{ grafana_password }}" - force_basic_auth: yes + force_basic_auth: true status_code: 404 headers: Accept: application/json Content-Type: application/json register: result -- assert: +- ansible.builtin.assert: that: - - "result.json.message | lower == 'user not found'" + - result.json.message | lower == 'user not found' + when: not ansible_check_mode - name: Create a Grafana user with character encoding - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Bruce Wayne" + name: Bruce Wayne email: bruce+wayne@gotham.city login: bruce+wayne@gotham.city password: robin state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == true" - - "result.user.name == 'Bruce Wayne'" - - "result.user.email == 'bruce+wayne@gotham.city'" - - "result.user.isGrafanaAdmin == false" + - result.changed == true + - result.user.name == 'Bruce Wayne' + - result.user.email == 'bruce+wayne@gotham.city' + - result.user.isGrafanaAdmin == false + when: not ansible_check_mode - name: Check idempotency on user creation (password not requiered) - grafana_user: + community.grafana.grafana_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" url_password: "{{ grafana_password }}" - name: "Bruce Wayne" + name: Bruce Wayne email: bruce+wayne@gotham.city login: bruce+wayne@gotham.city state: present register: result -- assert: +- ansible.builtin.assert: that: - - "result.changed == false" - - "result.user.name == 'Bruce Wayne'" - - "result.user.email == 'bruce+wayne@gotham.city'" - - "result.user.isGrafanaAdmin == false" + - result.changed == false + - result.user.name == 'Bruce Wayne' + - result.user.email == 'bruce+wayne@gotham.city' + - result.user.isGrafanaAdmin == false + when: not ansible_check_mode diff --git a/ansible_collections/community/grafana/tests/requirements.yml b/ansible_collections/community/grafana/tests/requirements.yml new file mode 100644 index 000000000..b9d14e2c3 --- /dev/null +++ b/ansible_collections/community/grafana/tests/requirements.yml @@ -0,0 +1,3 @@ +collections: + - name: community.grafana + version: 1.5.3 diff --git a/ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt b/ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt new file mode 100644 index 000000000..5c82494f9 --- /dev/null +++ b/ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt @@ -0,0 +1,4 @@ +plugins/modules/grafana_dashboard.py validate-modules:invalid-argument-name +tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py pep8:W291 +hacking/check_fragment.sh shebang +hacking/find_grafana_versions.py shebang diff --git a/ansible_collections/community/grafana/tests/sanity/ignore-2.17.txt b/ansible_collections/community/grafana/tests/sanity/ignore-2.17.txt new file mode 100644 index 000000000..5c82494f9 --- /dev/null +++ b/ansible_collections/community/grafana/tests/sanity/ignore-2.17.txt @@ -0,0 +1,4 @@ +plugins/modules/grafana_dashboard.py validate-modules:invalid-argument-name +tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py pep8:W291 +hacking/check_fragment.sh shebang +hacking/find_grafana_versions.py shebang diff --git a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py index d2fba0fe1..ff8e4f242 100644 --- a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py +++ b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py @@ -1,7 +1,7 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from unittest import TestCase -from unittest.mock import call, patch, MagicMock +from unittest.mock import patch from ansible_collections.community.grafana.plugins.modules import grafana_datasource from ansible.module_utils._text import to_bytes from ansible.module_utils import basic @@ -13,200 +13,207 @@ __metaclass__ = type def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) basic._ANSIBLE_ARGS = to_bytes(args) def exit_json(*args, **kwargs): """function to patch over exit_json; package return data into an exception""" - if 'changed' not in kwargs: - kwargs['changed'] = False + if "changed" not in kwargs: + kwargs["changed"] = False raise AnsibleExitJson(kwargs) def fail_json(*args, **kwargs): """function to patch over fail_json; package return data into an exception""" - kwargs['failed'] = True + kwargs["failed"] = True raise AnsibleFailJson(kwargs) class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" + pass class AnsibleFailJson(Exception): """Exception class to be raised by module.fail_json and caught by the test case""" + pass class GrafanaDatasource(TestCase): - def setUp(self): self.authorization = basic_auth_header("admin", "admin") - self.mock_module_helper = patch.multiple(basic.AnsibleModule, - exit_json=exit_json, - fail_json=fail_json) + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) def test_payload_prometheus(self): expected_payload = { - 'access': 'proxy', - 'basicAuth': False, - 'database': '', - 'isDefault': False, - 'jsonData': { - 'tlsAuth': False, - 'tlsAuthWithCACert': False, - 'tlsSkipVerify': True + "access": "proxy", + "basicAuth": False, + "database": "", + "isDefault": False, + "jsonData": { + "tlsAuth": False, + "tlsAuthWithCACert": False, + "tlsSkipVerify": True, }, - 'name': 'openshift_prometheus', - 'uid': 'xyz123', - 'orgId': 1, - 'secureJsonData': {}, - 'type': 'prometheus', - 'url': 'https://openshift-monitoring.company.com', - 'user': '', - 'withCredentials': False + "name": "openshift_prometheus", + "uid": "xyz123", + "orgId": 1, + "secureJsonData": {}, + "type": "prometheus", + "url": "https://openshift-monitoring.company.com", + "user": "", + "withCredentials": False, } - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'admin', - 'name': 'openshift_prometheus', - 'uid': 'xyz123', - 'ds_type': 'prometheus', - 'ds_url': 'https://openshift-monitoring.company.com', - 'access': 'proxy', - 'tls_skip_verify': 'true', - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "admin", + "name": "openshift_prometheus", + "uid": "xyz123", + "ds_type": "prometheus", + "ds_url": "https://openshift-monitoring.company.com", + "access": "proxy", + "tls_skip_verify": "true", + } + ) module = grafana_datasource.setup_module_object() payload = grafana_datasource.get_datasource_payload(module.params) self.assertEqual(payload, expected_payload) def test_payload_prometheus_with_basic_auth(self): expected_payload = { - 'access': 'proxy', - 'basicAuth': True, - 'basicAuthUser': 'admin', - 'database': '', - 'isDefault': False, - 'jsonData': { - 'tlsAuth': False, - 'tlsAuthWithCACert': False, - 'tlsSkipVerify': True + "access": "proxy", + "basicAuth": True, + "basicAuthUser": "admin", + "database": "", + "isDefault": False, + "jsonData": { + "tlsAuth": False, + "tlsAuthWithCACert": False, + "tlsSkipVerify": True, }, - 'name': 'openshift_prometheus', - 'uid': 'xyz123', - 'orgId': 1, - 'secureJsonData': {'basicAuthPassword': 'admin'}, - 'type': 'prometheus', - 'url': 'https://openshift-monitoring.company.com', - 'user': '', - 'withCredentials': False + "name": "openshift_prometheus", + "uid": "xyz123", + "orgId": 1, + "secureJsonData": {"basicAuthPassword": "admin"}, + "type": "prometheus", + "url": "https://openshift-monitoring.company.com", + "user": "", + "withCredentials": False, } - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'admin', - 'name': 'openshift_prometheus', - 'uid': 'xyz123', - 'ds_type': 'prometheus', - 'ds_url': 'https://openshift-monitoring.company.com', - 'access': 'proxy', - 'basic_auth_user': 'admin', - 'basic_auth_password': 'admin', - 'tls_skip_verify': 'true', - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "admin", + "name": "openshift_prometheus", + "uid": "xyz123", + "ds_type": "prometheus", + "ds_url": "https://openshift-monitoring.company.com", + "access": "proxy", + "basic_auth_user": "admin", + "basic_auth_password": "admin", + "tls_skip_verify": "true", + } + ) module = grafana_datasource.setup_module_object() payload = grafana_datasource.get_datasource_payload(module.params) self.assertEqual(payload, expected_payload) def test_payload_influxdb(self): expected_payload = { - 'access': 'proxy', - 'basicAuth': False, - 'database': 'telegraf', - 'isDefault': False, - 'jsonData': { - 'timeInterval': '>10s', - 'tlsAuth': False, - 'tlsAuthWithCACert': True - }, - 'name': 'datasource-influxdb', - 'uid': 'xyz123', - 'orgId': 1, - 'secureJsonData': { - 'tlsCACert': '/etc/ssl/certs/ca.pem' + "access": "proxy", + "basicAuth": False, + "database": "telegraf", + "isDefault": False, + "jsonData": { + "timeInterval": ">10s", + "tlsAuth": False, + "tlsAuthWithCACert": True, }, - 'type': 'influxdb', - 'url': 'https://influx.company.com:8086', - 'user': '', - 'withCredentials': False + "name": "datasource-influxdb", + "uid": "xyz123", + "orgId": 1, + "secureJsonData": {"tlsCACert": "/etc/ssl/certs/ca.pem"}, + "type": "influxdb", + "url": "https://influx.company.com:8086", + "user": "", + "withCredentials": False, } - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'admin', - 'name': 'datasource-influxdb', - 'uid': 'xyz123', - 'ds_type': 'influxdb', - 'ds_url': 'https://influx.company.com:8086', - 'database': 'telegraf', - 'time_interval': '>10s', - 'tls_ca_cert': '/etc/ssl/certs/ca.pem' - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "admin", + "name": "datasource-influxdb", + "uid": "xyz123", + "ds_type": "influxdb", + "ds_url": "https://influx.company.com:8086", + "database": "telegraf", + "time_interval": ">10s", + "tls_ca_cert": "/etc/ssl/certs/ca.pem", + } + ) module = grafana_datasource.setup_module_object() payload = grafana_datasource.get_datasource_payload(module.params) self.assertEqual(payload, expected_payload) def test_payload_elastic(self): expected_payload = { - 'access': 'proxy', - 'basicAuth': True, - 'basicAuthUser': 'grafana', - 'database': '[logstash_]YYYY.MM.DD', - 'isDefault': False, - 'jsonData': { - 'esVersion': 56, - 'interval': 'Daily', - 'maxConcurrentShardRequests': 42, - 'timeField': '@timestamp', - 'timeInterval': '1m', - 'tlsAuth': False, - 'tlsAuthWithCACert': True + "access": "proxy", + "basicAuth": True, + "basicAuthUser": "grafana", + "database": "[logstash_]YYYY.MM.DD", + "isDefault": False, + "jsonData": { + "esVersion": 56, + "interval": "Daily", + "maxConcurrentShardRequests": 42, + "timeField": "@timestamp", + "timeInterval": "1m", + "tlsAuth": False, + "tlsAuthWithCACert": True, }, - 'name': 'datasource-elastic', - 'uid': 'xyz123', - 'orgId': 1, - 'secureJsonData': { - 'basicAuthPassword': 'grafana', - 'tlsCACert': '/etc/ssl/certs/ca.pem' + "name": "datasource-elastic", + "uid": "xyz123", + "orgId": 1, + "secureJsonData": { + "basicAuthPassword": "grafana", + "tlsCACert": "/etc/ssl/certs/ca.pem", }, - 'type': 'elasticsearch', - 'url': 'https://elastic.company.com:9200', - 'user': '', - 'withCredentials': False + "type": "elasticsearch", + "url": "https://elastic.company.com:9200", + "user": "", + "withCredentials": False, } - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'admin', - 'name': 'datasource-elastic', - 'uid': 'xyz123', - 'ds_type': 'elasticsearch', - 'ds_url': 'https://elastic.company.com:9200', - 'database': '[logstash_]YYYY.MM.DD', - 'basic_auth_user': 'grafana', - 'basic_auth_password': 'grafana', - 'time_field': '@timestamp', - 'time_interval': '1m', - 'interval': 'Daily', - 'es_version': 56, - 'max_concurrent_shard_requests': 42, - 'tls_ca_cert': '/etc/ssl/certs/ca.pem' - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "admin", + "name": "datasource-elastic", + "uid": "xyz123", + "ds_type": "elasticsearch", + "ds_url": "https://elastic.company.com:9200", + "database": "[logstash_]YYYY.MM.DD", + "basic_auth_user": "grafana", + "basic_auth_password": "grafana", + "time_field": "@timestamp", + "time_interval": "1m", + "interval": "Daily", + "es_version": 56, + "max_concurrent_shard_requests": 42, + "tls_ca_cert": "/etc/ssl/certs/ca.pem", + } + ) module = grafana_datasource.setup_module_object() payload = grafana_datasource.get_datasource_payload(module.params) self.assertEqual(payload, expected_payload) diff --git a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py index b3b025c4e..ec691a467 100644 --- a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py +++ b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py @@ -1,4 +1,4 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from unittest import TestCase from unittest.mock import patch, MagicMock @@ -53,14 +53,13 @@ Error: ✗ plugin does not exist class GrafanaPlugin(TestCase): - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin') + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin" + ) def test_plugin_install_zip(self, mock_grafana_cli_bin): mock_grafana_cli_bin.return_value = "grafana-cli plugins" - params = { - "name": "alexanderzobnin-zabbix-app" - } + params = {"name": "alexanderzobnin-zabbix-app"} module = MagicMock() module.run_command.return_value = run_command_install_zip() @@ -68,13 +67,13 @@ class GrafanaPlugin(TestCase): result = grafana_plugin.get_grafana_plugin_version(module, params) self.assertEqual(result, None) - @patch('ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin') + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin" + ) def test_plugin_ls(self, mock_grafana_cli_bin): mock_grafana_cli_bin.return_value = "grafana-cli plugins" - params = { - "name": "alexanderzobnin-zabbix-app" - } + params = {"name": "alexanderzobnin-zabbix-app"} module = MagicMock() module.run_command.return_value = run_command_ls() @@ -82,13 +81,13 @@ class GrafanaPlugin(TestCase): result = grafana_plugin.get_grafana_plugin_version(module, params) self.assertEqual(result, "3.10.5") - @patch('ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin') + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin" + ) def test_plugin_uninstall(self, mock_grafana_cli_bin): mock_grafana_cli_bin.return_value = "grafana-cli plugins" - params = { - "name": "alexanderzobnin-zabbix-app" - } + params = {"name": "alexanderzobnin-zabbix-app"} module = MagicMock() module.run_command.return_value = run_command_uninstall() @@ -96,13 +95,13 @@ class GrafanaPlugin(TestCase): result = grafana_plugin.get_grafana_plugin_version(module, params) self.assertEqual(result, None) - @patch('ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin') + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_plugin.grafana_cli_bin" + ) def test_plugin_uninstall_again(self, mock_grafana_cli_bin): mock_grafana_cli_bin.return_value = "grafana-cli plugins" - params = { - "name": "alexanderzobnin-zabbix-app" - } + params = {"name": "alexanderzobnin-zabbix-app"} module = MagicMock() module.run_command.return_value = run_command_uninstall_again() diff --git a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_team/test_grafana_team.py b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_team/test_grafana_team.py index c59953afa..8efba998b 100644 --- a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_team/test_grafana_team.py +++ b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_team/test_grafana_team.py @@ -1,7 +1,7 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from unittest import TestCase -from unittest.mock import patch, MagicMock +from unittest.mock import patch from ansible_collections.community.grafana.plugins.modules import grafana_team from ansible.module_utils._text import to_bytes from ansible.module_utils import basic @@ -21,30 +21,32 @@ class MockedReponse(object): def exit_json(*args, **kwargs): """function to patch over exit_json; package return data into an exception""" - if 'changed' not in kwargs: - kwargs['changed'] = False + if "changed" not in kwargs: + kwargs["changed"] = False raise AnsibleExitJson(kwargs) def fail_json(*args, **kwargs): """function to patch over fail_json; package return data into an exception""" - kwargs['failed'] = True + kwargs["failed"] = True raise AnsibleFailJson(kwargs) class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" + pass class AnsibleFailJson(Exception): """Exception class to be raised by module.fail_json and caught by the test case""" + pass def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) basic._ANSIBLE_ARGS = to_bytes(args) @@ -65,7 +67,10 @@ def get_low_version_resp(): def team_exists_resp(): - server_response = json.dumps({"totalCount": 1, "teams": [{"name": "MyTestTeam", "email": "email@test.com"}]}, sort_keys=True) + server_response = json.dumps( + {"totalCount": 1, "teams": [{"name": "MyTestTeam", "email": "email@test.com"}]}, + sort_keys=True, + ) return (MockedReponse(server_response), {"status": 200}) @@ -90,21 +95,26 @@ def team_deleted_resp(): def team_members_resp(): - server_response = json.dumps([{ - "orgId": 1, - "teamId": 2, - "userId": 3, - "email": "user1@email.com", - "login": "user1", - "avatarUrl": r"\/avatar\/1b3c32f6386b0185c40d359cdc733a79" - }, { - "orgId": 1, - "teamId": 2, - "userId": 2, - "email": "user2@email.com", - "login": "user2", - "avatarUrl": r"\/avatar\/cad3c68da76e45d10269e8ef02f8e73e" - }]) + server_response = json.dumps( + [ + { + "orgId": 1, + "teamId": 2, + "userId": 3, + "email": "user1@email.com", + "login": "user1", + "avatarUrl": r"\/avatar\/1b3c32f6386b0185c40d359cdc733a79", + }, + { + "orgId": 1, + "teamId": 2, + "userId": 2, + "email": "user2@email.com", + "login": "user2", + "avatarUrl": r"\/avatar\/cad3c68da76e45d10269e8ef02f8e73e", + }, + ] + ) return (MockedReponse(server_response), {"status": 200}) @@ -124,12 +134,11 @@ def delete_team_member_resp(): class GrafanaTeamsTest(TestCase): - def setUp(self): self.authorization = basic_auth_header("admin", "admin") - self.mock_module_helper = patch.multiple(basic.AnsibleModule, - exit_json=exit_json, - fail_json=fail_json) + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) @@ -139,112 +148,148 @@ class GrafanaTeamsTest(TestCase): with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - err, arg_list = result.exception.args[0]['msg'].split(':') - missing_args = [item.strip() for item in arg_list.split(',')] - self.assertEqual(err, 'missing required arguments') + err, arg_list = result.exception.args[0]["msg"].split(":") + self.assertEqual(err, "missing required arguments") self.assertEqual(arg_list, ["name", "email", "url"]) def test_module_setup_fails_without_name(self): - set_module_args({ - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + {"email": "email@test.com", "url": "http://grafana.example.com"} + ) with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertEqual(result.exception.args[0]['msg'], 'missing required arguments: name') + self.assertEqual( + result.exception.args[0]["msg"], "missing required arguments: name" + ) def test_module_setup_fails_without_email(self): - set_module_args({ - 'name': 'MyTestTeam', - 'url': 'http://grafana.example.com' - }) + set_module_args({"name": "MyTestTeam", "url": "http://grafana.example.com"}) with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertEqual(result.exception.args[0]['msg'], 'missing required arguments: email') + self.assertEqual( + result.exception.args[0]["msg"], "missing required arguments: email" + ) def test_module_setup_fails_without_url(self): - set_module_args({ - 'name': 'MyTestTeam', - 'email': 'email@test.com', - }) + set_module_args( + { + "name": "MyTestTeam", + "email": "email@test.com", + } + ) with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertEqual(result.exception.args[0]['msg'], 'missing required arguments: url') + self.assertEqual( + result.exception.args[0]["msg"], "missing required arguments: url" + ) def test_module_setup_fails_with_mutually_exclusive_auth_methods(self): - set_module_args({ - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com', - 'grafana_user': 'admin', - 'grafana_api_key': 'random_api_key', - }) + set_module_args( + { + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + "grafana_user": "admin", + "grafana_api_key": "random_api_key", + } + ) with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertEqual(result.exception.args[0]['msg'], 'parameters are mutually exclusive: url_username|grafana_api_key') - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') + self.assertEqual( + result.exception.args[0]["msg"], + "parameters are mutually exclusive: url_username|grafana_api_key", + ) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) def test_module_fails_with_low_grafana_version(self, mock_get_version): - set_module_args({ - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com', - 'grafana_user': 'admin', - 'grafana_password': 'admin', - }) + set_module_args( + { + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + "grafana_user": "admin", + "grafana_password": "admin", + } + ) - module = grafana_team.setup_module_object() mock_get_version.return_value = get_low_version_resp() with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertEqual(result.exception.args[0]['msg'], 'Teams API is available starting Grafana v5') - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') - def test_module_failure_with_unauthorized_resp(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com', - }) - module = grafana_team.setup_module_object() + self.assertEqual( + result.exception.args[0]["msg"], + "Teams API is available starting Grafana v5", + ) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) + def test_module_failure_with_unauthorized_resp( + self, mock_fetch_url, mock_get_version + ): + set_module_args( + { + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) mock_fetch_url.return_value = unauthorized_resp() mock_get_version.return_value = get_version_resp() with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertTrue(result.exception.args[0]['msg'].startswith('Unauthorized to perform action')) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') - def test_module_failure_with_permission_denied_resp(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com', - }) - module = grafana_team.setup_module_object() + self.assertTrue( + result.exception.args[0]["msg"].startswith("Unauthorized to perform action") + ) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) + def test_module_failure_with_permission_denied_resp( + self, mock_fetch_url, mock_get_version + ): + set_module_args( + { + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) mock_fetch_url.return_value = permission_denied_resp() mock_get_version.return_value = get_version_resp() with self.assertRaises(AnsibleFailJson) as result: grafana_team.main() - self.assertTrue(result.exception.args[0]['msg'].startswith('Permission Denied')) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + self.assertTrue(result.exception.args[0]["msg"].startswith("Permission Denied")) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_get_team_method_with_existing_team(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_exists_resp() mock_get_version.return_value = get_version_resp() @@ -252,21 +297,34 @@ class GrafanaTeamsTest(TestCase): grafana_iface = grafana_team.GrafanaTeamInterface(module) res = grafana_iface.get_team("MyTestTeam") mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/search?name=MyTestTeam', + module, + "http://grafana.example.com/api/teams/search?name=MyTestTeam", data=None, - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='GET') - self.assertEquals(res, {"email": "email@test.com", "name": "MyTestTeam"}) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') - def test_get_team_method_with_non_existing_team(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="GET", + ) + self.assertEqual(res, {"email": "email@test.com", "name": "MyTestTeam"}) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) + def test_get_team_method_with_non_existing_team( + self, mock_fetch_url, mock_get_version + ): + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_not_found_resp() mock_get_version.return_value = get_version_resp() @@ -274,21 +332,32 @@ class GrafanaTeamsTest(TestCase): grafana_iface = grafana_team.GrafanaTeamInterface(module) res = grafana_iface.get_team("MyTestTeam") mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/search?name=MyTestTeam', + module, + "http://grafana.example.com/api/teams/search?name=MyTestTeam", data=None, - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='GET') - self.assertEquals(res, None) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="GET", + ) + self.assertEqual(res, None) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_create_team_method(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_created_resp() mock_get_version.return_value = get_version_resp() @@ -297,21 +366,34 @@ class GrafanaTeamsTest(TestCase): res = grafana_iface.create_team("MyTestTeam", "email@test.com") mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams', - data=json.dumps({"email": "email@test.com", "name": "MyTestTeam"}, sort_keys=True), - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='POST') - self.assertEquals(res, {"message": "Team created", "teamId": 2}) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + module, + "http://grafana.example.com/api/teams", + data=json.dumps( + {"email": "email@test.com", "name": "MyTestTeam"}, sort_keys=True + ), + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="POST", + ) + self.assertEqual(res, {"message": "Team created", "teamId": 2}) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_update_team_method(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_updated_resp() mock_get_version.return_value = get_version_resp() @@ -319,21 +401,34 @@ class GrafanaTeamsTest(TestCase): grafana_iface = grafana_team.GrafanaTeamInterface(module) res = grafana_iface.update_team(2, "MyTestTeam", "email@test.com") mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/2', - data=json.dumps({"email": "email@test.com", "name": "MyTestTeam"}, sort_keys=True), - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='PUT') - self.assertEquals(res, {"message": "Team updated"}) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + module, + "http://grafana.example.com/api/teams/2", + data=json.dumps( + {"email": "email@test.com", "name": "MyTestTeam"}, sort_keys=True + ), + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="PUT", + ) + self.assertEqual(res, {"message": "Team updated"}) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_delete_team_method(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'absent', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "absent", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_deleted_resp() mock_get_version.return_value = get_version_resp() @@ -341,21 +436,32 @@ class GrafanaTeamsTest(TestCase): grafana_iface = grafana_team.GrafanaTeamInterface(module) res = grafana_iface.delete_team(2) mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/2', + module, + "http://grafana.example.com/api/teams/2", data=None, - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='DELETE') - self.assertEquals(res, {"message": "Team deleted"}) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="DELETE", + ) + self.assertEqual(res, {"message": "Team deleted"}) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_get_team_members_method(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_members_resp() mock_get_version.return_value = get_version_resp() @@ -363,21 +469,34 @@ class GrafanaTeamsTest(TestCase): grafana_iface = grafana_team.GrafanaTeamInterface(module) res = grafana_iface.get_team_members(2) mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/2/members', + module, + "http://grafana.example.com/api/teams/2/members", data=None, - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='GET') - self.assertEquals(res, ["user1@email.com", "user2@email.com"]) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') - def test_get_team_members_method_no_members_returned(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="GET", + ) + self.assertEqual(res, ["user1@email.com", "user2@email.com"]) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) + def test_get_team_members_method_no_members_returned( + self, mock_fetch_url, mock_get_version + ): + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = team_members_no_members_resp() mock_get_version.return_value = get_version_resp() @@ -385,63 +504,96 @@ class GrafanaTeamsTest(TestCase): grafana_iface = grafana_team.GrafanaTeamInterface(module) res = grafana_iface.get_team_members(2) mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/2/members', + module, + "http://grafana.example.com/api/teams/2/members", data=None, - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='GET') - self.assertEquals(res, []) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="GET", + ) + self.assertEqual(res, []) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_add_team_member_method(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = add_team_member_resp() mock_get_version.return_value = get_version_resp() grafana_iface = grafana_team.GrafanaTeamInterface(module) - with patch.object(grafana_team.GrafanaTeamInterface, 'get_user_id_from_mail') as mock_get_user_id_from_mail: + with patch.object( + grafana_team.GrafanaTeamInterface, "get_user_id_from_mail" + ) as mock_get_user_id_from_mail: mock_get_user_id_from_mail.return_value = 42 res = grafana_iface.add_team_member(2, "another@test.com") mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/2/members', - data=json.dumps({'userId': 42}), - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='POST') - self.assertEquals(res, None) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version') - @patch('ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url') + module, + "http://grafana.example.com/api/teams/2/members", + data=json.dumps({"userId": 42}), + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="POST", + ) + self.assertEqual(res, None) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.GrafanaTeamInterface.get_version" + ) + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_team.fetch_url" + ) def test_delete_team_member_method(self, mock_fetch_url, mock_get_version): - set_module_args({ - 'state': 'present', - 'name': 'MyTestTeam', - 'email': 'email@test.com', - 'url': 'http://grafana.example.com' - }) + set_module_args( + { + "state": "present", + "name": "MyTestTeam", + "email": "email@test.com", + "url": "http://grafana.example.com", + } + ) module = grafana_team.setup_module_object() mock_fetch_url.return_value = delete_team_member_resp() mock_get_version.return_value = get_version_resp() grafana_iface = grafana_team.GrafanaTeamInterface(module) - with patch.object(grafana_team.GrafanaTeamInterface, 'get_user_id_from_mail') as mock_get_user_id_from_mail: + with patch.object( + grafana_team.GrafanaTeamInterface, "get_user_id_from_mail" + ) as mock_get_user_id_from_mail: mock_get_user_id_from_mail.return_value = 42 res = grafana_iface.delete_team_member(2, "another@test.com") mock_fetch_url.assert_called_once_with( - module, 'http://grafana.example.com/api/teams/2/members/42', + module, + "http://grafana.example.com/api/teams/2/members/42", data=None, - headers={'Content-Type': 'application/json', 'Authorization': self.authorization}, - method='DELETE') - self.assertEquals(res, None) + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="DELETE", + ) + self.assertEqual(res, None) def test_diff_members_function(self): list1 = ["foo@example.com", "bar@example.com"] list2 = ["bar@example.com", "random@example.com"] res = grafana_team.diff_members(list1, list2) - self.assertEquals(res, {"to_del": ["random@example.com"], "to_add": ["foo@example.com"]}) + self.assertEqual( + res, {"to_del": ["random@example.com"], "to_add": ["foo@example.com"]} + ) diff --git a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_user/test_grafana_user.py b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_user/test_grafana_user.py index 925c01655..09d0dce05 100644 --- a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_user/test_grafana_user.py +++ b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_user/test_grafana_user.py @@ -1,7 +1,7 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function from unittest import TestCase -from unittest.mock import call, patch, MagicMock +from unittest.mock import call, patch from ansible_collections.community.grafana.plugins.modules import grafana_user from ansible.module_utils._text import to_bytes from ansible.module_utils import basic @@ -21,30 +21,32 @@ class MockedReponse(object): def exit_json(*args, **kwargs): """function to patch over exit_json; package return data into an exception""" - if 'changed' not in kwargs: - kwargs['changed'] = False + if "changed" not in kwargs: + kwargs["changed"] = False raise AnsibleExitJson(kwargs) def fail_json(*args, **kwargs): """function to patch over fail_json; package return data into an exception""" - kwargs['failed'] = True + kwargs["failed"] = True raise AnsibleFailJson(kwargs) class AnsibleExitJson(Exception): """Exception class to be raised by module.exit_json and caught by the test case""" + pass class AnsibleFailJson(Exception): """Exception class to be raised by module.fail_json and caught by the test case""" + pass def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) basic._ANSIBLE_ARGS = to_bytes(args) @@ -59,75 +61,94 @@ def user_already_exists_resp(): def user_created_resp(): - server_response = json.dumps({ - "id": 2, - "email": "robin@gotham.com", - "name": "Robin", - "login": "adrobinmin", - "theme": "light", - "orgId": 1, - "isGrafanaAdmin": False, - "isDisabled": False, - "isExternal": False, - "authLabels": None, - "updatedAt": "2019-09-25T14:44:37+01:00", - "createdAt": "2019-09-25T14:44:37+01:00" - }, sort_keys=True) + server_response = json.dumps( + { + "id": 2, + "email": "robin@gotham.com", + "name": "Robin", + "login": "adrobinmin", + "theme": "light", + "orgId": 1, + "isGrafanaAdmin": False, + "isDisabled": False, + "isExternal": False, + "authLabels": None, + "updatedAt": "2019-09-25T14:44:37+01:00", + "createdAt": "2019-09-25T14:44:37+01:00", + }, + sort_keys=True, + ) return (MockedReponse(server_response), {"status": 200}) class GrafanaUserTest(TestCase): - def setUp(self): self.authorization = basic_auth_header("admin", "changeme") - self.mock_module_helper = patch.multiple(basic.AnsibleModule, - exit_json=exit_json, - fail_json=fail_json) + self.mock_module_helper = patch.multiple( + basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json + ) self.mock_module_helper.start() self.addCleanup(self.mock_module_helper.stop) # create an already existing user - @patch('ansible_collections.community.grafana.plugins.modules.grafana_user.fetch_url') + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_user.fetch_url" + ) def test_create_user_existing_user(self, mock_fetch_url): - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'changeme', - 'name': 'Joker', - 'email': 'joker@gotham.com', - 'login': 'joker', - 'password': 'oups', - 'state': 'present' - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "changeme", + "name": "Joker", + "email": "joker@gotham.com", + "login": "joker", + "password": "oups", + "state": "present", + } + ) module = grafana_user.setup_module_object() mock_fetch_url.return_value = user_already_exists_resp() grafana_iface = grafana_user.GrafanaUserInterface(module) with self.assertRaises(AnsibleFailJson): - grafana_iface.create_user( - 'Joker', 'joker@gotham.com', 'joker', 'oups') + grafana_iface.create_user("Joker", "joker@gotham.com", "joker", "oups") mock_fetch_url.assert_called_once_with( module, - 'https://grafana.example.com/api/admin/users', - data=json.dumps({'name': 'Joker', 'email': 'joker@gotham.com', - 'login': 'joker', 'password': 'oups'}, sort_keys=True), - headers={'Content-Type': 'application/json', - 'Authorization': self.authorization}, - method='POST') + "https://grafana.example.com/api/admin/users", + data=json.dumps( + { + "name": "Joker", + "email": "joker@gotham.com", + "login": "joker", + "password": "oups", + }, + sort_keys=True, + ), + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="POST", + ) # create a new user - @patch('ansible_collections.community.grafana.plugins.modules.grafana_user.fetch_url') + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_user.fetch_url" + ) def test_create_user_new_user(self, mock_fetch_url): - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'changeme', - 'name': 'Robin', - 'email': 'robin@gotham.com', - 'login': 'robin', - 'password': 'oups', - 'state': 'present' - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "changeme", + "name": "Robin", + "email": "robin@gotham.com", + "login": "robin", + "password": "oups", + "state": "present", + } + ) module = grafana_user.setup_module_object() mock_fetch_url.return_value = user_created_resp() @@ -136,54 +157,71 @@ class GrafanaUserTest(TestCase): # first call to create user call( module, - 'https://grafana.example.com/api/admin/users', - data=json.dumps({'name': 'Robin', 'email': 'robin@gotham.com', - 'login': 'robin', 'password': 'oups'}, sort_keys=True), - headers={'Content-Type': 'application/json', - 'Authorization': self.authorization}, - method='POST'), - + "https://grafana.example.com/api/admin/users", + data=json.dumps( + { + "name": "Robin", + "email": "robin@gotham.com", + "login": "robin", + "password": "oups", + }, + sort_keys=True, + ), + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="POST", + ), # second call to return created user call( module, - 'https://grafana.example.com/api/users/lookup?loginOrEmail=robin', + "https://grafana.example.com/api/users/lookup?loginOrEmail=robin", data=None, - headers={'Content-Type': 'application/json', - 'Authorization': self.authorization}, - method='GET'), + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="GET", + ), ] grafana_iface = grafana_user.GrafanaUserInterface(module) - result = grafana_iface.create_user( - 'Robin', 'robin@gotham.com', 'robin', 'oups') - - mock_fetch_url.assert_has_calls( - expected_fetch_url_calls, any_order=False) - - self.assertEquals(result, { - "id": 2, - "email": "robin@gotham.com", - "name": "Robin", - "login": "adrobinmin", - "theme": "light", - "orgId": 1, - "isGrafanaAdmin": False, - "isDisabled": False, - "isExternal": False, - "authLabels": None, - "updatedAt": "2019-09-25T14:44:37+01:00", - "createdAt": "2019-09-25T14:44:37+01:00" - }) - - @patch('ansible_collections.community.grafana.plugins.modules.grafana_user.fetch_url') + result = grafana_iface.create_user("Robin", "robin@gotham.com", "robin", "oups") + + mock_fetch_url.assert_has_calls(expected_fetch_url_calls, any_order=False) + + self.assertEqual( + result, + { + "id": 2, + "email": "robin@gotham.com", + "name": "Robin", + "login": "adrobinmin", + "theme": "light", + "orgId": 1, + "isGrafanaAdmin": False, + "isDisabled": False, + "isExternal": False, + "authLabels": None, + "updatedAt": "2019-09-25T14:44:37+01:00", + "createdAt": "2019-09-25T14:44:37+01:00", + }, + ) + + @patch( + "ansible_collections.community.grafana.plugins.modules.grafana_user.fetch_url" + ) def test_delete_user(self, mock_fetch_url): - set_module_args({ - 'url': 'https://grafana.example.com', - 'url_username': 'admin', - 'url_password': 'changeme', - 'login': 'batman', - 'state': 'absent' - }) + set_module_args( + { + "url": "https://grafana.example.com", + "url_username": "admin", + "url_password": "changeme", + "login": "batman", + "state": "absent", + } + ) module = grafana_user.setup_module_object() mock_fetch_url.return_value = user_deleted_resp() @@ -192,9 +230,12 @@ class GrafanaUserTest(TestCase): result = grafana_iface.delete_user(user_id) mock_fetch_url.assert_called_once_with( module, - 'https://grafana.example.com/api/admin/users/42', + "https://grafana.example.com/api/admin/users/42", data=None, - headers={'Content-Type': 'application/json', - 'Authorization': self.authorization}, - method='DELETE') - self.assertEquals(result, {"message": "User deleted"}) + headers={ + "Content-Type": "application/json", + "Authorization": self.authorization, + }, + method="DELETE", + ) + self.assertEqual(result, {"message": "User deleted"}) |