summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/grafana
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ansible_collections/community/grafana/.github/workflows/ansible-test.yml6
-rw-r--r--ansible_collections/community/grafana/CHANGELOG.rst30
-rw-r--r--ansible_collections/community/grafana/FILES.json187
-rw-r--r--ansible_collections/community/grafana/MANIFEST.json4
-rw-r--r--ansible_collections/community/grafana/README.md3
-rw-r--r--ansible_collections/community/grafana/changelogs/changelog.yaml33
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/353-docs-add-influxdb-flux-example.yml3
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/354-fix-find-grafana-versions.yml3
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/355-rm-dashboard-message-argument.yml3
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/356-lookup-dashboards-add-custom-certs-verification-logic.yml2
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/357-module-grafana-silence.yml2
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/358-role-grafana-silence.yml2
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/362-plugin-umask.yml2
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/364-first-datasource-default.yml3
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/367-dashboard-undo-breaing-change-message.yml3
-rw-r--r--ansible_collections/community/grafana/changelogs/fragments/368-molecule-pin-requests.yml3
-rwxr-xr-xansible_collections/community/grafana/hacking/check_fragment.sh6
-rw-r--r--ansible_collections/community/grafana/hacking/find_grafana_versions.py31
-rw-r--r--ansible_collections/community/grafana/meta/runtime.yml1
-rw-r--r--ansible_collections/community/grafana/molecule/default/converge.yml11
-rw-r--r--ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py52
-rw-r--r--ansible_collections/community/grafana/plugins/modules/grafana_datasource.py23
-rw-r--r--ansible_collections/community/grafana/plugins/modules/grafana_plugin.py2
-rw-r--r--ansible_collections/community/grafana/plugins/modules/grafana_silence.py369
-rw-r--r--ansible_collections/community/grafana/roles/grafana/README.md7
-rw-r--r--ansible_collections/community/grafana/roles/grafana/defaults/main.yml1
-rw-r--r--ansible_collections/community/grafana/roles/grafana/meta/main.yml2
-rw-r--r--ansible_collections/community/grafana/roles/grafana/tasks/main.yml12
-rw-r--r--ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/elastic.yml26
-rw-r--r--ansible_collections/community/grafana/tests/integration/targets/grafana_datasource/tasks/postgres.yml2
-rw-r--r--ansible_collections/community/grafana/tests/integration/targets/grafana_silence/defaults/main.yml5
-rwxr-xr-xansible_collections/community/grafana/tests/integration/targets/grafana_silence/runme.sh5
-rw-r--r--ansible_collections/community/grafana/tests/integration/targets/grafana_silence/site.yml6
-rw-r--r--ansible_collections/community/grafana/tests/integration/targets/grafana_silence/tasks/main.yml89
-rw-r--r--ansible_collections/community/grafana/tests/sanity/ignore-2.15.txt3
-rw-r--r--ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt3
-rw-r--r--ansible_collections/community/grafana/tests/sanity/ignore-2.18.txt1
-rw-r--r--ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py2
-rw-r--r--ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_silence/test_grafana_silence.py211
39 files changed, 1100 insertions, 59 deletions
diff --git a/ansible_collections/community/grafana/.github/workflows/ansible-test.yml b/ansible_collections/community/grafana/.github/workflows/ansible-test.yml
index a5717781f..7d474372b 100644
--- a/ansible_collections/community/grafana/.github/workflows/ansible-test.yml
+++ b/ansible_collections/community/grafana/.github/workflows/ansible-test.yml
@@ -45,7 +45,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- grafana_version: ["9.5.14", "8.5.27", "10.2.2"]
+ grafana_version: ["8.5.27", "9.5.18", "10.4.2"]
ansible_version: ["stable-2.15", "stable-2.16", "devel"]
python_version: ["3.10"]
services:
@@ -67,7 +67,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- grafana_version: ["9.5.14", "8.5.27", "10.2.2"]
+ grafana_version: ["8.5.27", "9.5.18", "10.4.2"]
ansible_version: ["stable-2.15", "stable-2.16", "devel"]
python_version: ["3.10"]
services:
@@ -86,7 +86,7 @@ jobs:
- 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
+ pip install "git+https://github.com/ansible/ansible@${{ matrix.ansible_version }}" molecule molecule-plugins[docker] 'requests<2.32.0'
- name: Test with molecule
run: |
diff --git a/ansible_collections/community/grafana/CHANGELOG.rst b/ansible_collections/community/grafana/CHANGELOG.rst
index 8a40ac18f..9449bdc5c 100644
--- a/ansible_collections/community/grafana/CHANGELOG.rst
+++ b/ansible_collections/community/grafana/CHANGELOG.rst
@@ -4,6 +4,35 @@ Grafana Collection Release Notes
.. contents:: Topics
+v1.9.1
+======
+
+Bugfixes
+--------
+
+- undo removed deprecated `message` argument in `grafana_dashboard`
+
+v1.9.0
+======
+
+Minor Changes
+-------------
+
+- Add new module `grafana_silence` to create and delete silences through the API
+- Add role components for `grafana_silence` module
+- lookup - grafana_dashboards - add `validate_certs` and `ca_path` options to plugin for custom certs validation
+
+Removed Features (previously deprecated)
+----------------------------------------
+
+- removed deprecated `message` argument in `grafana_dashboard`
+
+Bugfixes
+--------
+
+- Handling of desired default state for first `grafana_datasource`
+- Ignore `type` argument for diff comparison if `grafana-postgresq-datasource` alias `postgres` is used
+- Set umask for `grafana_plugin` command
v1.8.0
======
@@ -288,4 +317,3 @@ Release Summary
---------------
Initial migration of Grafana content from Ansible core (2.9/devel)
-
diff --git a/ansible_collections/community/grafana/FILES.json b/ansible_collections/community/grafana/FILES.json
index be95fa2bc..037433bd7 100644
--- a/ansible_collections/community/grafana/FILES.json
+++ b/ansible_collections/community/grafana/FILES.json
@@ -53,7 +53,7 @@
"name": ".github/workflows/ansible-test.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e3e774ec33a3dc2a5ea1da40931ab5e6b96d3b67e57454fc867f9009cbc9797f",
+ "chksum_sha256": "cd6239f3c1bd2e8e47e65c2e1504273adabd0debe5034b52c0b3d1817d5ece75",
"format": 1
},
{
@@ -470,6 +470,76 @@
"format": 1
},
{
+ "name": "changelogs/fragments/353-docs-add-influxdb-flux-example.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a07b0c46777ec3102120e2d1374c24f11fa49b643f9ee598697d7cb7931a6fb0",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/354-fix-find-grafana-versions.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "988704ddd40d7a4e6c20a045fa813de4fff2db0ad722f64f97c08af0ebcc3ee6",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/355-rm-dashboard-message-argument.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1f9b81756d099816b1ef211ed9420406256f31e44a1af8e21025d1781ec8c452",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/356-lookup-dashboards-add-custom-certs-verification-logic.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "45e0bb6e524aecb31ef2ed642a981a964164da2871ec3b5167ce79205ef77d3e",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/357-module-grafana-silence.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d4302eaaf80c43b0e4b8d9b919579ec6fbf2156966696b17342ff3caaa4b39a8",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/358-role-grafana-silence.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "44f72091e352d9f720a2c69be372a251ae80371c9bbb3d046a1c92e3169597f9",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/362-plugin-umask.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8e15e87758848e90e4798e0d8bbf4ae3d69c8df49f36d3f6f4665b7bac175dc",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/364-first-datasource-default.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1706a7f2ab349d835f22e173fc4b1f1abd30d08ddb6a24054a12999254d5f816",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/367-dashboard-undo-breaing-change-message.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d9379b41ea1756af06746c98636b2f29c04b3dcb3d1c05d131d30d84d569e75f",
+ "format": 1
+ },
+ {
+ "name": "changelogs/fragments/368-molecule-pin-requests.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf41eb78ee4dc1c0b4e70f18040bf236de5764067de76a46c2ca60bb787a0801",
+ "format": 1
+ },
+ {
"name": "changelogs/fragments/add-units-datasource.yml",
"ftype": "file",
"chksum_type": "sha256",
@@ -543,7 +613,7 @@
"name": "changelogs/changelog.yaml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "641de48bd2e1d3ab8daeace7b522413299bd4d2521204d1f707e8c23bb445480",
+ "chksum_sha256": "58b3c24d8492d4052650bd4595af4f4150984005e7e2cf1fa45d86c6710e3632",
"format": 1
},
{
@@ -571,14 +641,14 @@
"name": "hacking/check_fragment.sh",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "89b848ffbab17ebb115443ad4d599db0238a1f35a95d52ab4eedf990eef097a8",
+ "chksum_sha256": "911f382f8cb1de3f26d67809bba3a950ad75cd0a95cd39de13c2eb24131bc9aa",
"format": 1
},
{
"name": "hacking/find_grafana_versions.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "39acc81ec9733183bbb4eeae0c10c3aeecdeedd1ffc2dabcc675b84e16565f3e",
+ "chksum_sha256": "72518bc80cd4e6c1e6c28aa057bfb05e96231cffd71fb481dba53b76fcb86786",
"format": 1
},
{
@@ -599,7 +669,7 @@
"name": "meta/runtime.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "6872a5ea5042e0a7a43f2b60fe6c02a24e3e76b25deb50a46567df781345e41b",
+ "chksum_sha256": "0ccfa35f79e37b4162e45d083ec7170e8dd3b3011e506101f58ccbc5d183adbb",
"format": 1
},
{
@@ -620,7 +690,7 @@
"name": "molecule/default/converge.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c1e0e02c807ceafdf7bd707e0def1e9d2118e6389af9af44dc832dfc0483dab9",
+ "chksum_sha256": "707ee0f0231e6bfb2f467ec7d5089125b0b7d77ea85560921ad4eb179453c922",
"format": 1
},
{
@@ -711,7 +781,7 @@
"name": "plugins/lookup/grafana_dashboard.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7483dabbd84c95d3cd603e2d9d905b51f00e9f6ab4974164f31387c3ee63535d",
+ "chksum_sha256": "7e46eff65f8eda5fd1fbd4de9790d018acac261fa5e747dbdc1c9bc6062608cf",
"format": 1
},
{
@@ -753,7 +823,7 @@
"name": "plugins/modules/grafana_datasource.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "23081add13f2aba994b4cddb264972d219bee823ac1e4f454931d5a6a374bd94",
+ "chksum_sha256": "108ff562f0e37620ddcf6751f144aad05432bd7f3369bfbfc957e9df49310001",
"format": 1
},
{
@@ -788,7 +858,14 @@
"name": "plugins/modules/grafana_plugin.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "fb0ee164a2936bc35381cf79026b6725c7a1df139e71d58989f8912432043c00",
+ "chksum_sha256": "a6e739390d7afc946f656dc2080e96ab0c2e630e016fc20aaa2351ec909b5d64",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/grafana_silence.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1a90d5e29905a481dfed1fbd573a342984cf1e891c3643be5c13541302810cc5",
"format": 1
},
{
@@ -830,7 +907,7 @@
"name": "roles/grafana/defaults/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ee6026b2dd62777a731f2214949124f45d73309102123cad31ad55489a90ca45",
+ "chksum_sha256": "bce677aac146e75eee5d57c22d7ad1bdff128abb52d4bf05f8cd30f47d6c4105",
"format": 1
},
{
@@ -844,7 +921,7 @@
"name": "roles/grafana/meta/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "192ef2ac687cd5594febffbc0ab6f3d184cceed34dc66c11ff217db19f4ccfc2",
+ "chksum_sha256": "840995ce8972789e894e6c9be6d2fcd030fd3c478da292e7e363cb40c6f462c9",
"format": 1
},
{
@@ -858,14 +935,14 @@
"name": "roles/grafana/tasks/main.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "0de2cd11feb7e45f3427d063c3123024f94401bc5c1b48eaecea9716efcc03d5",
+ "chksum_sha256": "beb75cb00b20421fd4f4c81d72d32d13986005e809dd2c550a15290f09f1b9a2",
"format": 1
},
{
"name": "roles/grafana/README.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ab4f69856777b7b0c208c3c3d06a9771c98f81a729618dc94df80ea981b1b6ab",
+ "chksum_sha256": "f04e735d327e90a7bf9b51ce09c4834f8ed6c6e5ba6d635cdd4e92d807171794",
"format": 1
},
{
@@ -1054,7 +1131,7 @@
"name": "tests/integration/targets/grafana_datasource/tasks/elastic.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "69fba433ef869f3bb1916f2036a43985af48d21118f08e969f6020532a963d43",
+ "chksum_sha256": "c2db61ab9613c93dc35ed793aca09451ae672ab5e24c43b456a970ad0ff63f56",
"format": 1
},
{
@@ -1096,7 +1173,7 @@
"name": "tests/integration/targets/grafana_datasource/tasks/postgres.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4ed316aab332c0dfcc005213ef28087ea49ed011c0a73bdd0e5f8481e9403de4",
+ "chksum_sha256": "bdd63b0df46202a010b8d06b6658c303ed3a8893733e807ff79dd13ca042d8b7",
"format": 1
},
{
@@ -1499,6 +1576,55 @@
"format": 1
},
{
+ "name": "tests/integration/targets/grafana_silence",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/grafana_silence/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/grafana_silence/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d7d3a06f224d1576e39fcc2083e33261f30e3ceac18506f17772d8bcb72315c2",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/grafana_silence/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/grafana_silence/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "330c281b5f69223f9f6292aab612dc3ebe4511c87b2f9748b386d1aaca2f5a89",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/grafana_silence/runme.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b9aa6c8dadef0b4ce9cb08a05d0cf6c40f882d04f9e698bff795b26896e8853c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/grafana_silence/site.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9c2b6df4d70c00df0765796d40ef1dbaa673984ce30a06c0e89569c1a7bcaea2",
+ "format": 1
+ },
+ {
"name": "tests/integration/targets/grafana_team",
"ftype": "dir",
"chksum_type": null,
@@ -1649,14 +1775,14 @@
"name": "tests/sanity/ignore-2.15.txt",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "0908af1f4e86722688b3f158cfa9cc81c931ef40c0a166c398f47a4af69a93f9",
+ "chksum_sha256": "743d0edd0ef882d4dca19699436e4ea09d2acb4e3418385c8e7b89cfaf6e415e",
"format": 1
},
{
"name": "tests/sanity/ignore-2.16.txt",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "0908af1f4e86722688b3f158cfa9cc81c931ef40c0a166c398f47a4af69a93f9",
+ "chksum_sha256": "743d0edd0ef882d4dca19699436e4ea09d2acb4e3418385c8e7b89cfaf6e415e",
"format": 1
},
{
@@ -1667,6 +1793,13 @@
"format": 1
},
{
+ "name": "tests/sanity/ignore-2.18.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "743d0edd0ef882d4dca19699436e4ea09d2acb4e3418385c8e7b89cfaf6e415e",
+ "format": 1
+ },
+ {
"name": "tests/sanity/ignore-2.9.txt",
"ftype": "file",
"chksum_type": "sha256",
@@ -1719,7 +1852,21 @@
"name": "tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ada20cb8aef9852aee7b79afddd78f144f813c9df55baf3b8adc1b3863a8e9db",
+ "chksum_sha256": "9564a8a336726addac97b13fb19813c1d97824d5ca65e6cdc55b9c940554c255",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/modules/grafana/grafana_silence",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/modules/grafana/grafana_silence/test_grafana_silence.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7b6a2c19e99236c08e318b25fb9ee100ee79f6ce07abd9853254f17c64d4c1c8",
"format": 1
},
{
@@ -1782,7 +1929,7 @@
"name": "CHANGELOG.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e20df0ad2408ce9c311cf3bf18c99b2362c6eee388db48286b13be28ecedd8db",
+ "chksum_sha256": "70056dd2063eee72103302580afc77a58435eca179f53de3a2fefb3ad1798b09",
"format": 1
},
{
@@ -1796,7 +1943,7 @@
"name": "README.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8e93c6a3ce656865252643666a94835e98d904107d7a678f0a74774445f91125",
+ "chksum_sha256": "f7151290ee62498487eb8bbb7fb07383ac448d6f192b519f069bd99f453d2385",
"format": 1
},
{
diff --git a/ansible_collections/community/grafana/MANIFEST.json b/ansible_collections/community/grafana/MANIFEST.json
index 71f9afdc1..2c1f7f1a0 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.8.0",
+ "version": "1.9.1",
"authors": [
"R\u00e9mi REY (@rrey)",
"Thierry Sall\u00e9 (@seuf)"
@@ -25,7 +25,7 @@
"name": "FILES.json",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "98a10c2378e1ad46d7031d895c557d33af10809314832b9e3af5c2e607099e0b",
+ "chksum_sha256": "4ff85c9d6729fe479fe05c2e56a41833f87286a8ee1af5b2b730ccd876c04ebb",
"format": 1
},
"format": 1
diff --git a/ansible_collections/community/grafana/README.md b/ansible_collections/community/grafana/README.md
index 6d23f1b96..9ba981ae7 100644
--- a/ansible_collections/community/grafana/README.md
+++ b/ansible_collections/community/grafana/README.md
@@ -31,13 +31,14 @@ Click on the name of a plugin or module to view that content's documentation:
- [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)
+ - [grafana_silence](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_silence_module.html)
## Supported Grafana versions
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.5.14", "8.5.27", "10.2.2"]
+grafana_version: ["8.5.27", "9.5.18", "10.4.2"]
```
## Installation and Usage
diff --git a/ansible_collections/community/grafana/changelogs/changelog.yaml b/ansible_collections/community/grafana/changelogs/changelog.yaml
index 465b42bc3..f8d3f898b 100644
--- a/ansible_collections/community/grafana/changelogs/changelog.yaml
+++ b/ansible_collections/community/grafana/changelogs/changelog.yaml
@@ -279,3 +279,36 @@ releases:
- 349-role-notification-channel.yml
- 350-python3.12.yml
release_date: '2024-02-21'
+ 1.9.0:
+ changes:
+ bugfixes:
+ - Handling of desired default state for first `grafana_datasource`
+ - Ignore `type` argument for diff comparison if `grafana-postgresq-datasource`
+ alias `postgres` is used
+ - Set umask for `grafana_plugin` command
+ minor_changes:
+ - Add new module `grafana_silence` to create and delete silences through the
+ API
+ - Add role components for `grafana_silence` module
+ - lookup - grafana_dashboards - add `validate_certs` and `ca_path` options to
+ plugin for custom certs validation
+ removed_features:
+ - removed deprecated `message` argument in `grafana_dashboard`
+ fragments:
+ - 353-docs-add-influxdb-flux-example.yml
+ - 354-fix-find-grafana-versions.yml
+ - 355-rm-dashboard-message-argument.yml
+ - 356-lookup-dashboards-add-custom-certs-verification-logic.yml
+ - 357-module-grafana-silence.yml
+ - 358-role-grafana-silence.yml
+ - 362-plugin-umask.yml
+ - 364-first-datasource-default.yml
+ release_date: '2024-04-30'
+ 1.9.1:
+ changes:
+ bugfixes:
+ - undo removed deprecated `message` argument in `grafana_dashboard`
+ fragments:
+ - 367-dashboard-undo-breaing-change-message.yml
+ - 368-molecule-pin-requests.yml
+ release_date: '2024-05-21'
diff --git a/ansible_collections/community/grafana/changelogs/fragments/353-docs-add-influxdb-flux-example.yml b/ansible_collections/community/grafana/changelogs/fragments/353-docs-add-influxdb-flux-example.yml
new file mode 100644
index 000000000..b3baa1bc5
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/353-docs-add-influxdb-flux-example.yml
@@ -0,0 +1,3 @@
+---
+trivial:
+ - grafana_datasource - (docs) add influxdb v2 flux example \ No newline at end of file
diff --git a/ansible_collections/community/grafana/changelogs/fragments/354-fix-find-grafana-versions.yml b/ansible_collections/community/grafana/changelogs/fragments/354-fix-find-grafana-versions.yml
new file mode 100644
index 000000000..4ba22771a
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/354-fix-find-grafana-versions.yml
@@ -0,0 +1,3 @@
+---
+trivial:
+ - fix find grafana versions python script comparation
diff --git a/ansible_collections/community/grafana/changelogs/fragments/355-rm-dashboard-message-argument.yml b/ansible_collections/community/grafana/changelogs/fragments/355-rm-dashboard-message-argument.yml
new file mode 100644
index 000000000..3f54afa5e
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/355-rm-dashboard-message-argument.yml
@@ -0,0 +1,3 @@
+---
+removed_features:
+ - removed deprecated `message` argument in `grafana_dashboard`
diff --git a/ansible_collections/community/grafana/changelogs/fragments/356-lookup-dashboards-add-custom-certs-verification-logic.yml b/ansible_collections/community/grafana/changelogs/fragments/356-lookup-dashboards-add-custom-certs-verification-logic.yml
new file mode 100644
index 000000000..182822d9b
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/356-lookup-dashboards-add-custom-certs-verification-logic.yml
@@ -0,0 +1,2 @@
+minor_changes:
+ - lookup - grafana_dashboards - add `validate_certs` and `ca_path` options to plugin for custom certs validation
diff --git a/ansible_collections/community/grafana/changelogs/fragments/357-module-grafana-silence.yml b/ansible_collections/community/grafana/changelogs/fragments/357-module-grafana-silence.yml
new file mode 100644
index 000000000..a9ff03703
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/357-module-grafana-silence.yml
@@ -0,0 +1,2 @@
+minor_changes:
+ - Add new module `grafana_silence` to create and delete silences through the API
diff --git a/ansible_collections/community/grafana/changelogs/fragments/358-role-grafana-silence.yml b/ansible_collections/community/grafana/changelogs/fragments/358-role-grafana-silence.yml
new file mode 100644
index 000000000..a72161f04
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/358-role-grafana-silence.yml
@@ -0,0 +1,2 @@
+minor_changes:
+ - Add role components for `grafana_silence` module \ No newline at end of file
diff --git a/ansible_collections/community/grafana/changelogs/fragments/362-plugin-umask.yml b/ansible_collections/community/grafana/changelogs/fragments/362-plugin-umask.yml
new file mode 100644
index 000000000..cbef21ffd
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/362-plugin-umask.yml
@@ -0,0 +1,2 @@
+bugfixes:
+ - Set umask for `grafana_plugin` command
diff --git a/ansible_collections/community/grafana/changelogs/fragments/364-first-datasource-default.yml b/ansible_collections/community/grafana/changelogs/fragments/364-first-datasource-default.yml
new file mode 100644
index 000000000..3d39e9a0a
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/364-first-datasource-default.yml
@@ -0,0 +1,3 @@
+bugfixes:
+ - Handling of desired default state for first `grafana_datasource`
+ - Ignore `type` argument for diff comparison if `grafana-postgresq-datasource` alias `postgres` is used
diff --git a/ansible_collections/community/grafana/changelogs/fragments/367-dashboard-undo-breaing-change-message.yml b/ansible_collections/community/grafana/changelogs/fragments/367-dashboard-undo-breaing-change-message.yml
new file mode 100644
index 000000000..085ae0cb8
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/367-dashboard-undo-breaing-change-message.yml
@@ -0,0 +1,3 @@
+---
+bugfixes:
+ - undo removed deprecated `message` argument in `grafana_dashboard`
diff --git a/ansible_collections/community/grafana/changelogs/fragments/368-molecule-pin-requests.yml b/ansible_collections/community/grafana/changelogs/fragments/368-molecule-pin-requests.yml
new file mode 100644
index 000000000..6e60e0348
--- /dev/null
+++ b/ansible_collections/community/grafana/changelogs/fragments/368-molecule-pin-requests.yml
@@ -0,0 +1,3 @@
+---
+trivial:
+ - pin version of requests package for molecule tests
diff --git a/ansible_collections/community/grafana/hacking/check_fragment.sh b/ansible_collections/community/grafana/hacking/check_fragment.sh
index bf90cae65..3173b342f 100755
--- a/ansible_collections/community/grafana/hacking/check_fragment.sh
+++ b/ansible_collections/community/grafana/hacking/check_fragment.sh
@@ -1,7 +1,7 @@
-#!/bin/bash
+#!/usr/bin/env bash
function fail() {
- cat << EOF
+ cat <<EOF
Dear contributor,
Thank you for you Pull Request !
@@ -10,7 +10,7 @@ function fail() {
It will help your change be released faster !
Thank you !
EOF
- exit 1
+ exit 1
}
FRAGMENTS=$(git fetch && git diff --name-only --diff-filter=ACMRT origin/main..HEAD | grep "changelogs")
diff --git a/ansible_collections/community/grafana/hacking/find_grafana_versions.py b/ansible_collections/community/grafana/hacking/find_grafana_versions.py
index b31fc530a..716f877a8 100644
--- a/ansible_collections/community/grafana/hacking/find_grafana_versions.py
+++ b/ansible_collections/community/grafana/hacking/find_grafana_versions.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
import json
import requests
@@ -7,12 +7,12 @@ import requests
def get_by_major(version):
if version.startswith("v"):
version = version[1:]
- return (version[0], version, int(version.replace(".", "")))
+ return int(version.split(".")[0]), version, tuple(map(int, version.split(".")))
def get_grafana_releases():
r = requests.get(
- "https://api.github.com/repos/grafana/grafana/releases?per_page=50",
+ "https://api.github.com/repos/grafana/grafana/releases?per_page=100",
headers={"Accept": "application/vnd.github.v3+json"},
)
if r.status_code != 200:
@@ -20,19 +20,20 @@ def get_grafana_releases():
return r.json()
-by_major = {}
-
if __name__ == "__main__":
releases = get_grafana_releases()
- for item in releases:
- if item.get("prerelease"):
+ by_major = {}
+
+ for release in releases:
+ if release.get("prerelease") or any(
+ char in release.get("tag_name") for char in "-+"
+ ):
continue
- major, version, as_int = get_by_major(item.get("tag_name"))
- if major not in by_major.keys() or by_major[major]["as_int"] < as_int:
- by_major[major] = {"version": version, "as_int": as_int}
- latest_3_majors = sorted(list(by_major.keys()), reverse=True)[:3]
-
- latest_releases = []
- for idx in latest_3_majors:
- latest_releases.append(by_major[idx]["version"])
+ major, version, as_tuple = get_by_major(release.get("tag_name"))
+ if major not in by_major.keys() or by_major[major]["as_tuple"] < as_tuple:
+ by_major[major] = {"version": version, "as_tuple": as_tuple}
+
+ latest_3_majors = sorted(list(by_major.keys()))[:3]
+ latest_releases = [by_major[idx]["version"] for idx in latest_3_majors]
+
print(json.dumps(latest_releases))
diff --git a/ansible_collections/community/grafana/meta/runtime.yml b/ansible_collections/community/grafana/meta/runtime.yml
index 15f5554fc..d29f1dda8 100644
--- a/ansible_collections/community/grafana/meta/runtime.yml
+++ b/ansible_collections/community/grafana/meta/runtime.yml
@@ -11,3 +11,4 @@ action_groups:
- grafana_plugin
- grafana_team
- grafana_user
+ - grafana_silence
diff --git a/ansible_collections/community/grafana/molecule/default/converge.yml b/ansible_collections/community/grafana/molecule/default/converge.yml
index b1e1cf20b..4046e3369 100644
--- a/ansible_collections/community/grafana/molecule/default/converge.yml
+++ b/ansible_collections/community/grafana/molecule/default/converge.yml
@@ -45,4 +45,15 @@
path: test_dashboard.json
overwrite: true
+ grafana_silences:
+ - comment: molecule test
+ created_by: ansible
+ ends_at: "2030-01-01T00:00:00.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: false
+ name: environment
+ value: test
+ starts_at: "2029-12-31T23:23:59.000Z"
+
roles: [{role: community.grafana.grafana}]
diff --git a/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py b/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py
index c7fa31574..83ee250f7 100644
--- a/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py
+++ b/ansible_collections/community/grafana/plugins/lookup/grafana_dashboard.py
@@ -41,6 +41,13 @@ options:
description: optional filter for dashboard search.
env:
- name: GRAFANA_DASHBOARD_SEARCH
+ validate_certs:
+ description: flag to control SSL certificate validation
+ type: boolean
+ default: true
+ ca_path:
+ description: string of the file system path to CA cert bundle to use for validation
+ type: string
"""
EXAMPLES = """
@@ -51,13 +58,25 @@ EXAMPLES = """
- name: get all grafana dashboards
set_fact:
grafana_dashboards: "{{ lookup('grafana_dashboard', 'grafana_url=http://grafana.company.com grafana_api_key=' ~ grafana_api_key) }}"
+
+- name: get project foo grafana dashboards (validate SSL certificates of the instance with custom CA Certificate Bundle)
+ set_fact:
+ grafana_dashboards: |
+ {{
+ lookup(
+ 'grafana_dashboard',
+ 'grafana_url=https://grafana.company.com grafana_user=admin grafana_password=admin search=foo',
+ validate_certs=true,
+ ca_path='/path/to/chain.crt'
+ )
+ }}
"""
import json
import os
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
-from ansible.module_utils.urls import basic_auth_header, open_url
+from ansible.module_utils.urls import basic_auth_header, open_url, SSLValidationError
from ansible.module_utils._text import to_native
from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible.utils.display import Display
@@ -96,13 +115,15 @@ class GrafanaAPIException(Exception):
class GrafanaAPI:
- def __init__(self, **kwargs):
+ def __init__(self, validate_certs, ca_path, **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.validate_certs = validate_certs
+ self.ca_path = ca_path
def grafana_switch_organisation(self, headers):
try:
@@ -110,12 +131,19 @@ class GrafanaAPI:
"%s/api/user/using/%s" % (self.grafana_url, self.grafana_org_id),
headers=headers,
method="POST",
+ validate_certs=self.validate_certs,
+ ca_path=self.ca_path,
)
except HTTPError as e:
raise GrafanaAPIException(
"Unable to switch to organization %s : %s"
% (self.grafana_org_id, to_native(e))
)
+ except SSLValidationError as e:
+ raise GrafanaAPIException(
+ "Unable to validate server's certificate with %s: %s"
+ % (self.ca_path, to_native(e))
+ )
if r.getcode() != 200:
raise GrafanaAPIException(
"Unable to switch to organization %s : %s"
@@ -153,13 +181,24 @@ class GrafanaAPI:
"%s/api/search?query=%s" % (self.grafana_url, self.search),
headers=headers,
method="GET",
+ validate_certs=self.validate_certs,
+ ca_path=self.ca_path,
)
else:
r = open_url(
- "%s/api/search/" % self.grafana_url, headers=headers, method="GET"
+ "%s/api/search/" % self.grafana_url,
+ headers=headers,
+ method="GET",
+ validate_certs=self.validate_certs,
+ ca_path=self.ca_path,
)
except HTTPError as e:
raise GrafanaAPIException("Unable to search dashboards : %s" % to_native(e))
+ except SSLValidationError as e:
+ raise GrafanaAPIException(
+ "Unable to validate server's certificate with %s: %s"
+ % (self.ca_path, to_native(e))
+ )
if r.getcode() == 200:
try:
dashboard_list = json.loads(r.read())
@@ -175,6 +214,7 @@ class GrafanaAPI:
class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
+ self.set_options(var_options=variables, direct=kwargs)
grafana_args = terms[0].split(" ")
grafana_dict = {}
ret = []
@@ -189,7 +229,11 @@ class LookupModule(LookupBase):
)
grafana_dict[key] = value
- grafana = GrafanaAPI(**grafana_dict)
+ grafana = GrafanaAPI(
+ **grafana_dict,
+ validate_certs=self.get_option("validate_certs"),
+ ca_path=self.get_option("ca_path"),
+ )
ret = grafana.grafana_list_dashboards()
diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py b/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py
index 29cdbea7c..b011e5db1 100644
--- a/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py
+++ b/ansible_collections/community/grafana/plugins/modules/grafana_datasource.py
@@ -400,6 +400,23 @@ EXAMPLES = """
time_interval: ">10s"
tls_ca_cert: "/etc/ssl/certs/ca.pem"
+- name: Create influxdbv2 datasource using fluxql
+ community.grafana.grafana_datasource:
+ name: "datasource-influxdb-flux"
+ grafana_url: "https://grafana.company.com"
+ grafana_user: "admin"
+ grafana_password: "xxxxxx"
+ org_id: "1"
+ ds_type: "influxdb"
+ ds_url: "https://influx.company.com:8086"
+ additional_json_data:
+ version: "Flux"
+ organization: "organization"
+ defaultBucket: "bucket"
+ tlsSkipVerify: false
+ additional_secure_json_data:
+ token: "token"
+
- name: Create postgres datasource
community.grafana.grafana_datasource:
name: "datasource-postgres"
@@ -536,6 +553,9 @@ def compare_datasources(new, current, compareSecureData=True):
del current["password"]
if "basicAuthPassword" in current:
del current["basicAuthPassword"]
+ if current["type"] == "grafana-postgresql-datasource" and new["type"] == "postgres":
+ del current["type"]
+ del new["type"]
# check if secureJsonData should be compared
if not compareSecureData:
@@ -928,6 +948,9 @@ def main():
if ds is None:
grafana_iface.create_datasource(payload)
ds = grafana_iface.datasource_by_name(name)
+ if ds.get("isDefault") != module.params["is_default"]:
+ grafana_iface.update_datasource(ds.get("id"), payload)
+ ds = grafana_iface.datasource_by_name(name)
module.exit_json(
changed=True, datasource=ds, msg="Datasource %s created" % name
)
diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py b/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py
index c510f02ba..db1e8553a 100644
--- a/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py
+++ b/ansible_collections/community/grafana/plugins/modules/grafana_plugin.py
@@ -223,7 +223,7 @@ def grafana_plugin(module, params):
else:
cmd = "{0} uninstall {1}".format(grafana_cli, params["name"])
- rc, stdout, stderr = module.run_command(cmd)
+ rc, stdout, stderr = module.run_command(cmd, umask=0o0022)
if rc == 0:
stdout_lines = stdout.split("\n")
for line in stdout_lines:
diff --git a/ansible_collections/community/grafana/plugins/modules/grafana_silence.py b/ansible_collections/community/grafana/plugins/modules/grafana_silence.py
new file mode 100644
index 000000000..35f54a3d8
--- /dev/null
+++ b/ansible_collections/community/grafana/plugins/modules/grafana_silence.py
@@ -0,0 +1,369 @@
+#!/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) 2023, flkhndlr (@flkhndlr)
+
+from __future__ import absolute_import, division, print_function
+
+DOCUMENTATION = """
+module: grafana_silence
+author:
+ - flkhndlr (@flkhndlr)
+version_added: "1.8.0"
+short_description: Manage Grafana Silences
+description:
+ - Create/delete Grafana Silences through the Alertmanager Silence API.
+requirements:
+ - The Alertmanager API is only available starting Grafana 8 and the module will fail if the server version is lower than version 8.
+options:
+ comment:
+ description:
+ - The comment that describes the silence.
+ required: true
+ type: str
+ created_by:
+ description:
+ - The author that creates the silence.
+ required: true
+ type: str
+ starts_at:
+ description:
+ - ISO 8601 Timestamp with milliseconds e.g. "2029-07-29T08:45:45.000Z" when the silence starts.
+ type: str
+ required: true
+ ends_at:
+ description:
+ - ISO 8601 Timestamp with milliseconds e.g. "2029-07-29T08:45:45.000Z" when the silence will end.
+ type: str
+ required: true
+ matchers:
+ description:
+ - List of matchers to select which alerts are affected by the silence.
+ type: list
+ elements: dict
+ required: true
+ state:
+ description:
+ - Delete the first occurrence of a silence with the same settings. Can be "absent" or "present".
+ default: present
+ type: str
+ choices: ["present", "absent"]
+ 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
+ required: False
+ type: bool
+ default: False
+extends_documentation_fragment:
+- community.grafana.basic_auth
+- community.grafana.api_key
+"""
+
+EXAMPLES = """
+---
+- name: Create a silence
+ community.grafana.grafana_silence:
+ grafana_url: "https://grafana.example.com"
+ grafana_api_key: "{{ some_api_token_value }}"
+ comment: "a testcomment"
+ created_by: "me"
+ starts_at: "2029-07-29T08:45:45.000Z"
+ ends_at: "2029-07-29T08:55:45.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: true
+ name: environment
+ value: test
+ state: present
+
+- name: Delete a silence
+ community.grafana.grafana_silence:
+ grafana_url: "https://grafana.example.com"
+ grafana_api_key: "{{ some_api_token_value }}"
+ comment: "a testcomment"
+ created_by: "me"
+ starts_at: "2029-07-29T08:45:45.000Z"
+ ends_at: "2029-07-29T08:55:45.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: true
+ name: environment
+ value: test
+ state: absent
+"""
+
+RETURN = """
+---
+silence:
+ description: Information about the silence
+ returned: On success
+ type: complex
+ contains:
+ id:
+ description: The id of the silence
+ returned: success
+ type: str
+ sample:
+ - ec27df6b-ac3c-412f-ae0b-6e3e1f41c9c3
+ comment:
+ description: The comment of the silence
+ returned: success
+ type: str
+ sample:
+ - this is a test
+ createdBy:
+ description: The author of the silence
+ returned: success
+ type: str
+ sample:
+ - me
+ startsAt:
+ description: The begin timestamp of the silence
+ returned: success
+ type: str
+ sample:
+ - "2029-07-29T08:45:45.000Z"
+ endsAt:
+ description: The end timestamp of the silence
+ returned: success
+ type: str
+ sample:
+ - "2029-07-29T08:55:45.000Z"
+ matchers:
+ description: The matchers of the silence
+ returned: success
+ type: list
+ sample:
+ - [{"isEqual": true, "isRegex": true, "name": "environment", "value": "test"}]
+ status:
+ description: The status of the silence
+ returned: success
+ type: dict
+ sample:
+ - {"state": "pending"}
+ updatedAt:
+ description: The timestamp of the last update for the silence
+ returned: success
+ type: str
+ sample:
+ - "2023-07-27T13:27:33.042Z"
+"""
+
+import json
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.urls import fetch_url, basic_auth_header
+from ansible.module_utils._text import to_text
+from ansible_collections.community.grafana.plugins.module_utils import base
+
+__metaclass__ = type
+
+
+class GrafanaError(Exception):
+ pass
+
+
+class GrafanaSilenceInterface(object):
+ def __init__(self, module):
+ self._module = module
+ # {{{ Authentication header
+ self.headers = {"Content-Type": "application/json"}
+ module.params["force_basic_auth"] = True
+ 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.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"] < 8:
+ self._module.fail_json(
+ failed=True,
+ msg="Silences API is available starting with Grafana v8",
+ )
+
+ def _send_request(self, url, data=None, headers=None, method="GET"):
+ if data is not None:
+ data = json.dumps(data)
+ if not headers:
+ 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
+ )
+ 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),
+ )
+ elif status_code == 403:
+ self._module.fail_json(failed=True, msg="Permission Denied")
+ elif status_code in [200, 202]:
+ return self._module.from_json(resp.read())
+ elif status_code == 400:
+ self._module.fail_json(failed=True, msg=info)
+ self._module.fail_json(
+ failed=True, msg="Grafana Silences 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"
+ )
+ version = response.get("version")
+ if version is not None:
+ major, minor, rev = version.split(".")
+ return {"major": int(major), "minor": int(minor), "rev": int(rev)}
+ raise GrafanaError("Failed to retrieve version from '%s'" % url)
+
+ def create_silence(self, comment, created_by, starts_at, ends_at, matchers):
+ url = "/api/alertmanager/grafana/api/v2/silences"
+ silence = dict(
+ comment=comment,
+ createdBy=created_by,
+ endsAt=ends_at,
+ matchers=matchers,
+ startsAt=starts_at,
+ )
+ response = self._send_request(
+ url, data=silence, headers=self.headers, method="POST"
+ )
+ if self.get_version()["major"] == 8:
+ response["silenceID"] = response["id"]
+ response.pop("id", None)
+ return response
+
+ def get_silence(self, comment, created_by, starts_at, ends_at, matchers):
+ url = "/api/alertmanager/grafana/api/v2/silences"
+
+ responses = self._send_request(url, headers=self.headers, method="GET")
+
+ for response in responses:
+ if (
+ response["comment"] == comment
+ and response["createdBy"] == created_by
+ and response["startsAt"] == starts_at
+ and response["endsAt"] == ends_at
+ and response["matchers"] == matchers
+ ):
+ return response
+ return None
+
+ def get_silence_by_id(self, silence_id):
+ url = "/api/alertmanager/grafana/api/v2/silence/{SilenceId}".format(
+ SilenceId=silence_id
+ )
+ response = self._send_request(url, headers=self.headers, method="GET")
+ return response
+
+ def get_silences(self):
+ url = "/api/alertmanager/grafana/api/v2/silences"
+ response = self._send_request(url, headers=self.headers, method="GET")
+ return response
+
+ def delete_silence(self, silence_id):
+ url = "/api/alertmanager/grafana/api/v2/silence/{SilenceId}".format(
+ SilenceId=silence_id
+ )
+ response = self._send_request(url, headers=self.headers, method="DELETE")
+ return response
+
+
+def setup_module_object():
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=False,
+ required_together=base.grafana_required_together(),
+ mutually_exclusive=base.grafana_mutually_exclusive(),
+ )
+ return module
+
+
+argument_spec = base.grafana_argument_spec()
+argument_spec.update(
+ comment=dict(type="str", required=True),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ created_by=dict(type="str", required=True),
+ starts_at=dict(type="str", required=True),
+ ends_at=dict(type="str", required=True),
+ matchers=dict(type="list", elements="dict", required=True),
+ skip_version_check=dict(type="bool", default=False),
+)
+
+
+def main():
+
+ module = setup_module_object()
+ comment = module.params["comment"]
+ created_by = module.params["created_by"]
+ starts_at = module.params["starts_at"]
+ ends_at = module.params["ends_at"]
+ matchers = module.params["matchers"]
+ state = module.params["state"]
+
+ changed = False
+ failed = False
+ grafana_iface = GrafanaSilenceInterface(module)
+
+ silence = grafana_iface.get_silence(
+ comment, created_by, starts_at, ends_at, matchers
+ )
+
+ if state == "present":
+
+ if not silence:
+ silence = grafana_iface.create_silence(
+ comment, created_by, starts_at, ends_at, matchers
+ )
+ silence = grafana_iface.get_silence_by_id(silence["silenceID"])
+ changed = True
+ else:
+ module.exit_json(
+ failed=failed,
+ changed=changed,
+ msg="Silence with same parameters already exists! eg. '%s'"
+ % silence["id"],
+ )
+ elif state == "absent":
+ if silence:
+ grafana_iface.delete_silence(silence["id"])
+ changed = True
+ else:
+ module.exit_json(
+ failed=False,
+ changed=changed,
+ msg="Silence does not exist",
+ )
+ module.exit_json(failed=failed, changed=changed, silence=silence)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/community/grafana/roles/grafana/README.md b/ansible_collections/community/grafana/roles/grafana/README.md
index f46434edf..f76e687fc 100644
--- a/ansible_collections/community/grafana/roles/grafana/README.md
+++ b/ansible_collections/community/grafana/roles/grafana/README.md
@@ -163,6 +163,13 @@ Configure Grafana organizations, dashboards, folders, datasources, teams and use
| webhook_password | no |
| webhook_url | no |
| webhook_username | no |
+| [**grafana_silence**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_silence_module.html) |
+| comment | yes |
+| created_by | yes |
+| ends_at | yes |
+| matchers | yes |
+| starts_at | yes |
+| state | no |
## Example Playbook
diff --git a/ansible_collections/community/grafana/roles/grafana/defaults/main.yml b/ansible_collections/community/grafana/roles/grafana/defaults/main.yml
index 6a84370d3..9756a1331 100644
--- a/ansible_collections/community/grafana/roles/grafana/defaults/main.yml
+++ b/ansible_collections/community/grafana/roles/grafana/defaults/main.yml
@@ -7,3 +7,4 @@ grafana_datasources: []
grafana_folders: []
grafana_dashboards: []
grafana_notification_channels: []
+grafana_silences: []
diff --git a/ansible_collections/community/grafana/roles/grafana/meta/main.yml b/ansible_collections/community/grafana/roles/grafana/meta/main.yml
index 47d4af5b7..b5df42193 100644
--- a/ansible_collections/community/grafana/roles/grafana/meta/main.yml
+++ b/ansible_collections/community/grafana/roles/grafana/meta/main.yml
@@ -2,7 +2,7 @@
galaxy_info:
role_name: grafana
author: community
- description: Configure Grafana organizations, dashboards, folders, datasources, teams and users
+ description: Configure Grafana organizations, dashboards, folders, datasources, silences, teams and users
license: GPLv3
min_ansible_version: "2.14"
galaxy_tags: [grafana, monitoring]
diff --git a/ansible_collections/community/grafana/roles/grafana/tasks/main.yml b/ansible_collections/community/grafana/roles/grafana/tasks/main.yml
index 82bbc633d..1665a8562 100644
--- a/ansible_collections/community/grafana/roles/grafana/tasks/main.yml
+++ b/ansible_collections/community/grafana/roles/grafana/tasks/main.yml
@@ -201,3 +201,15 @@
loop: "{{ grafana_dashboards }}"
loop_control: {loop_var: dashboard}
tags: [dashboard, molecule-idempotence-notest]
+
+ - name: Manage silence
+ community.grafana.grafana_silence:
+ comment: "{{ silence.comment }}"
+ created_by: "{{ silence.created_by }}"
+ starts_at: "{{ silence.starts_at }}"
+ ends_at: "{{ silence.ends_at }}"
+ matchers: "{{ silence.matchers }}"
+ state: "{{ silence.state | default(omit) }}"
+ loop: "{{ grafana_silences }}"
+ loop_control: {loop_var: silence}
+ tags: silence
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 ff05b93f5..be5873574 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
@@ -286,6 +286,32 @@
- result.datasource.secureJsonFields.secureTest == true
- result.diff.after.secureJsonData is defined
+- name: Delete elasticsearch legacy datasource
+ register: result
+ community.grafana.grafana_datasource:
+ name: datasource/elasticLegacy
+ grafana_url: "{{ grafana_url }}"
+ grafana_user: "{{ grafana_username }}"
+ grafana_password: "{{ grafana_password }}"
+ state: absent
+
+- ansible.builtin.assert:
+ that:
+ - result.changed
+
+- name: Delete elasticsearch legacy datasource (idempotency)
+ register: result
+ community.grafana.grafana_datasource:
+ name: datasource/elasticLegacy
+ grafana_url: "{{ grafana_url }}"
+ grafana_user: "{{ grafana_username }}"
+ grafana_password: "{{ grafana_password }}"
+ state: absent
+
+- ansible.builtin.assert:
+ that:
+ - not result.changed
+
- name: Delete elasticsearch datasource
register: result
community.grafana.grafana_datasource:
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 b07bc752b..8945e2bdd 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
@@ -56,7 +56,7 @@
- result.datasource.jsonData.timescaledb == true
- result.datasource.name == 'datasource-postgres'
- result.datasource.orgId == 1
- - result.datasource.type == 'postgres'
+ - result.datasource.type in ['postgres', 'grafana-postgresql-datasource']
- result.datasource.url == 'postgres.company.com:5432'
- result.datasource.user == 'postgres'
- result.datasource.withCredentials == false
diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/defaults/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/defaults/main.yml
new file mode 100644
index 000000000..4abf9bb43
--- /dev/null
+++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+
+grafana_url: http://grafana:3000/
+grafana_username: admin
+grafana_password: admin
diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/runme.sh b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/runme.sh
new file mode 100755
index 000000000..867afb0d3
--- /dev/null
+++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/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_silence/site.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/site.yml
new file mode 100644
index 000000000..791582277
--- /dev/null
+++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/site.yml
@@ -0,0 +1,6 @@
+---
+- name: Run tests for grafana_silence
+ hosts: localhost
+ tasks:
+ - ansible.builtin.include_role:
+ name: ../../grafana_silence
diff --git a/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/tasks/main.yml b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/tasks/main.yml
new file mode 100644
index 000000000..ad4cfa25d
--- /dev/null
+++ b/ansible_collections/community/grafana/tests/integration/targets/grafana_silence/tasks/main.yml
@@ -0,0 +1,89 @@
+---
+- name: Create new silence
+ community.grafana.grafana_silence:
+ url: "{{ grafana_url }}"
+ url_username: "{{ grafana_username }}"
+ url_password: "{{ grafana_password }}"
+ comment: "a testcomment"
+ created_by: "me"
+ starts_at: "2029-07-29T08:45:45.000Z"
+ ends_at: "2029-07-29T08:55:45.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: true
+ name: environment
+ value: test
+ state: present
+ register: result
+- assert:
+ that:
+ - "result.changed == true"
+ - "result.failed == false"
+ - "result.silence.id != ''"
+
+- name: Check idempotency on silence creation
+ community.grafana.grafana_silence:
+ url: "{{ grafana_url }}"
+ url_username: "{{ grafana_username }}"
+ url_password: "{{ grafana_password }}"
+ comment: "a testcomment"
+ created_by: "me"
+ starts_at: "2029-07-29T08:45:45.000Z"
+ ends_at: "2029-07-29T08:55:45.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: true
+ name: environment
+ value: test
+ state: present
+ register: result
+- assert:
+ that:
+ - "result.changed == false"
+ - "result.msg != ''"
+
+- name: Delete the silence
+ community.grafana.grafana_silence:
+ url: "{{ grafana_url }}"
+ url_username: "{{ grafana_username }}"
+ url_password: "{{ grafana_password }}"
+ comment: "a testcomment"
+ created_by: "me"
+ starts_at: "2029-07-29T08:45:45.000Z"
+ ends_at: "2029-07-29T08:55:45.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: true
+ name: environment
+ value: test
+ state: absent
+ register: result
+- assert:
+ that:
+ - "result.changed == true"
+ - "result.failed == false"
+ - "result.silence.id != ''"
+ - - "result.silence.createdBy != 'me'"
+
+- name: Check idempotency on silence deletion
+ community.grafana.grafana_silence:
+ url: "{{ grafana_url }}"
+ url_username: "{{ grafana_username }}"
+ url_password: "{{ grafana_password }}"
+ comment: "a testcomment"
+ created_by: "me"
+ starts_at: "2029-07-29T08:45:45.000Z"
+ ends_at: "2029-07-29T08:55:45.000Z"
+ matchers:
+ - isEqual: true
+ isRegex: true
+ name: environment
+ value: test
+ state: absent
+ register: result
+ ignore_errors: yes
+- assert:
+ that:
+ - "result.changed == false"
+ - "result.failed == false"
+ - "result.msg == 'Silence does not exist'" \ No newline at end of file
diff --git a/ansible_collections/community/grafana/tests/sanity/ignore-2.15.txt b/ansible_collections/community/grafana/tests/sanity/ignore-2.15.txt
index 5c82494f9..0a40a23d5 100644
--- a/ansible_collections/community/grafana/tests/sanity/ignore-2.15.txt
+++ b/ansible_collections/community/grafana/tests/sanity/ignore-2.15.txt
@@ -1,4 +1 @@
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.16.txt b/ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt
index 5c82494f9..0a40a23d5 100644
--- a/ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt
+++ b/ansible_collections/community/grafana/tests/sanity/ignore-2.16.txt
@@ -1,4 +1 @@
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.18.txt b/ansible_collections/community/grafana/tests/sanity/ignore-2.18.txt
new file mode 100644
index 000000000..0a40a23d5
--- /dev/null
+++ b/ansible_collections/community/grafana/tests/sanity/ignore-2.18.txt
@@ -0,0 +1 @@
+plugins/modules/grafana_dashboard.py validate-modules:invalid-argument-name
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 ec691a467..7cc04a99a 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
@@ -21,7 +21,7 @@ Restart grafana after installing plugins . <service grafana-server restart>
def run_command_install_zip():
STDERR = ""
STDOUT = """
-installing alexanderzobnin-grafana-zabbix @
+installing alexanderzobnin-grafana-zabbix @
from: /home/grafana//alexanderzobnin-grafana-zabbix-v3.10.5-1-g2219691.zip
into: /var/lib/grafana/plugins
diff --git a/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_silence/test_grafana_silence.py b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_silence/test_grafana_silence.py
new file mode 100644
index 000000000..96522c2af
--- /dev/null
+++ b/ansible_collections/community/grafana/tests/unit/modules/grafana/grafana_silence/test_grafana_silence.py
@@ -0,0 +1,211 @@
+from __future__ import absolute_import, division, print_function
+
+from unittest import TestCase
+from unittest.mock import patch
+from ansible_collections.community.grafana.plugins.modules import grafana_silence
+from ansible.module_utils._text import to_bytes
+from ansible.module_utils import basic
+from ansible.module_utils.urls import basic_auth_header
+import json
+
+__metaclass__ = type
+
+
+class MockedReponse(object):
+ def __init__(self, data):
+ self.data = data
+
+ def read(self):
+ return self.data
+
+
+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
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs):
+ """function to patch over fail_json; package return data into an exception"""
+ 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})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+def silence_deleted_resp():
+ server_response = json.dumps({"message": "silence deleted"})
+ return (MockedReponse(server_response), {"status": 200})
+
+
+def silence_created_resp():
+ server_response = json.dumps({"silenceID": "470b7116-8f06-4bb6-9e6c-6258aa92218e"})
+ return (MockedReponse(server_response), {"status": 200})
+
+
+def silence_get_resp():
+ server_response = json.dumps([], sort_keys=True)
+ return (MockedReponse(server_response), {"status": 200})
+
+
+def get_silence_by_id_resp():
+ server_response = json.dumps([], sort_keys=True)
+ return (MockedReponse(server_response), {"status": 200})
+
+
+def get_version_resp():
+ return {"major": 10, "minor": 0, "rev": 0}
+
+
+class GrafanaSilenceTest(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.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ # create a new silence
+ @patch(
+ "ansible_collections.community.grafana.plugins.modules.grafana_silence.GrafanaSilenceInterface.get_silence"
+ )
+ @patch(
+ "ansible_collections.community.grafana.plugins.modules.grafana_silence.GrafanaSilenceInterface.get_version"
+ )
+ @patch(
+ "ansible_collections.community.grafana.plugins.modules.grafana_silence.fetch_url"
+ )
+ def test_create_silence_new_silence(
+ self, mock_fetch_url, mock_get_version, mock_get_silence
+ ):
+ set_module_args(
+ {
+ "url": "https://grafana.example.com",
+ "url_username": "admin",
+ "url_password": "changeme",
+ "comment": "a testcomment",
+ "created_by": "me",
+ "starts_at": "2029-07-29T08:45:45.000Z",
+ "ends_at": "2029-07-29T08:55:45.000Z",
+ "matchers": [
+ {
+ "isEqual": True,
+ "isRegex": True,
+ "name": "environment",
+ "value": "test",
+ }
+ ],
+ "state": "present",
+ }
+ )
+ module = grafana_silence.setup_module_object()
+ mock_get_version.return_value = get_version_resp()
+ mock_fetch_url.return_value = silence_created_resp()
+ mock_get_silence.return_value = silence_get_resp()
+
+ grafana_iface = grafana_silence.GrafanaSilenceInterface(module)
+ result = grafana_iface.create_silence(
+ "a testcomment",
+ "me",
+ "2029-07-29T08:45:45.000Z",
+ "2029-07-29T08:55:45.000Z",
+ [
+ {
+ "isEqual": True,
+ "isRegex": True,
+ "name": "environment",
+ "value": "test",
+ }
+ ],
+ )
+ mock_fetch_url.assert_called_once_with(
+ module,
+ "https://grafana.example.com/api/alertmanager/grafana/api/v2/silences",
+ data=json.dumps(
+ {
+ "comment": "a testcomment",
+ "createdBy": "me",
+ "startsAt": "2029-07-29T08:45:45.000Z",
+ "endsAt": "2029-07-29T08:55:45.000Z",
+ "matchers": [
+ {
+ "isEqual": True,
+ "isRegex": True,
+ "name": "environment",
+ "value": "test",
+ }
+ ],
+ },
+ sort_keys=True,
+ ),
+ headers={
+ "Content-Type": "application/json",
+ "Authorization": self.authorization,
+ },
+ method="POST",
+ )
+ self.assertEquals(result, {"silenceID": "470b7116-8f06-4bb6-9e6c-6258aa92218e"})
+
+ @patch(
+ "ansible_collections.community.grafana.plugins.modules.grafana_silence.GrafanaSilenceInterface.get_version"
+ )
+ @patch(
+ "ansible_collections.community.grafana.plugins.modules.grafana_silence.fetch_url"
+ )
+ def test_delete_silence(self, mock_fetch_url, mock_get_version):
+ set_module_args(
+ {
+ "url": "https://grafana.example.com",
+ "url_username": "admin",
+ "url_password": "changeme",
+ "comment": "a testcomment",
+ "created_by": "me",
+ "ends_at": "2029-07-29T08:55:45.000Z",
+ "matchers": [
+ {
+ "isEqual": True,
+ "isRegex": True,
+ "name": "environment",
+ "value": "test",
+ }
+ ],
+ "starts_at": "2029-07-29T08:45:45.000Z",
+ "state": "present",
+ }
+ )
+ module = grafana_silence.setup_module_object()
+ mock_fetch_url.return_value = silence_deleted_resp()
+ mock_get_version.return_value = get_version_resp()
+
+ grafana_iface = grafana_silence.GrafanaSilenceInterface(module)
+ silence_id = "470b7116-8f06-4bb6-9e6c-6258aa92218e"
+ result = grafana_iface.delete_silence(silence_id)
+ mock_fetch_url.assert_called_once_with(
+ module,
+ "https://grafana.example.com/api/alertmanager/grafana/api/v2/silence/470b7116-8f06-4bb6-9e6c-6258aa92218e",
+ data=None,
+ headers={
+ "Content-Type": "application/json",
+ "Authorization": self.authorization,
+ },
+ method="DELETE",
+ )
+ self.assertEquals(result, {"message": "silence deleted"})