Skip to content

Commit

Permalink
improve code quality and style guide
Browse files Browse the repository at this point in the history
  • Loading branch information
flkhndlr committed Aug 1, 2023
1 parent 353f69f commit 2fee8ab
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 138 deletions.
152 changes: 98 additions & 54 deletions plugins/modules/grafana_silence.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@

from __future__ import absolute_import, division, print_function

DOCUMENTATION = '''
DOCUMENTATION = """
---
module: grafana_silence
author:
- flkhndlr (@flkhndlr)
version_added: "1.5.5"
version_added: "1.6.0"
short_description: Manage Grafana Silences
description:
- Create/delete Grafana Silences through the Alertmanager Silence API.
Expand All @@ -45,15 +45,18 @@
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: str
elements: dict
required: true
state:
description:
- Delete the first occurrence of a silence with the same settings.
Expand All @@ -67,13 +70,12 @@
required: False
type: bool
default: False
version_added: "1.2.0"
extends_documentation_fragment:
- community.grafana.basic_auth
- community.grafana.api_key
'''
"""

EXAMPLES = '''
EXAMPLES = """
---
- name: Create a silence
community.grafana.grafana_silence:
Expand Down Expand Up @@ -104,9 +106,9 @@
name: environment
value: test
state: absent
'''
"""

RETURN = '''
RETURN = """
---
silence:
description: Information about the Silence
Expand Down Expand Up @@ -152,7 +154,7 @@
status:
description: The status of the Silence
returned: success
type: object
type: dict
sample:
- {"state": "pending"}
updatedAt:
Expand All @@ -161,7 +163,7 @@
type: str
sample:
- "2023-07-27T13:27:33.042Z"
'''
"""

import json

Expand All @@ -178,25 +180,31 @@ class GrafanaError(Exception):


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']
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.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"] < 5:
self._module.fail_json(failed=True, msg="Silences API is available starting Grafana v8")
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:
Expand All @@ -205,12 +213,17 @@ def _send_request(self, url, data=None, headers=None, method="GET"):
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 == 202:
Expand All @@ -219,11 +232,15 @@ def _send_request(self, url, data=None, headers=None, method="GET"):
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)
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")
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(".")
Expand All @@ -232,8 +249,16 @@ def get_version(self):

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, startsAt=starts_at, endsAt=ends_at, matchers=matchers)
response = self._send_request(url, data=silence, headers=self.headers, method="POST")
silence = dict(
comment=comment,
createdBy=created_by,
startsAt=starts_at,
endsAt=ends_at,
matchers=matchers,
)
response = self._send_request(
url, data=silence, headers=self.headers, method="POST"
)
return response

def get_silence(self, comment, created_by, starts_at, ends_at, matchers):
Expand All @@ -242,16 +267,22 @@ def get_silence(self, comment, created_by, starts_at, ends_at, matchers):
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:
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
else:
return None
return None

def get_silence_by_id(self, silence_id):
url = "/api/alertmanager/grafana/api/v2/silence/{SilenceId}".format(SilenceId=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

Expand All @@ -261,7 +292,9 @@ def get_silences(self):
return response

def delete_silence(self, silence_id):
url = "/api/alertmanager/grafana/api/v2/silence/{SilenceId}".format(SilenceId=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

Expand All @@ -278,50 +311,61 @@ def setup_module_object():

argument_spec = base.grafana_argument_spec()
argument_spec.update(
comment=dict(type='str', required=True),
state=dict(type='str', required=True),
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),
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']
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
silence = ""
grafana_iface = GrafanaSilenceInterface(module)

if state == 'present':
silence = grafana_iface.get_silence(comment, created_by, starts_at, ends_at, matchers)
if silence is None:
silence = grafana_iface.create_silence(comment, created_by, starts_at, ends_at, matchers)
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':
silence = grafana_iface.get_silence(comment, created_by, starts_at, ends_at, matchers)
if silence is not None:
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:
failed = True
module.exit_json(failed=failed, changed=changed,
msg="No Silence with the same parameters found! Deletion not possible")
module.exit_json(
failed=failed,
changed=changed,
msg="No Silence with the same parameters found! Deletion not possible",
)
module.exit_json(failed=failed, changed=changed, silence=silence)


if __name__ == '__main__':
if __name__ == "__main__":
main()
3 changes: 1 addition & 2 deletions tests/integration/targets/grafana_silence/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,4 @@
that:
- "result.changed == false"
- "result.failed == true"
- "result.msg == 'No Silence with the same parameters found! Deletion not possible'"

- "result.msg == 'No Silence with the same parameters found! Deletion not possible'"
Loading

0 comments on commit 2fee8ab

Please sign in to comment.