From d239cbe040df28b5f293660b9029a10c37c5b407 Mon Sep 17 00:00:00 2001 From: Aliaksandr Mianzhynski Date: Fri, 13 Nov 2020 18:07:26 +0300 Subject: [PATCH] Add initial notification channel support --- .../modules/grafana_notification_channel.py | 815 ++++++++++++++++++ .../defaults/main.yml | 7 + .../tasks/dingding.yml | 36 + .../tasks/discord.yml | 36 + .../tasks/email.yml | 38 + .../tasks/googlechat.yml | 36 + .../tasks/hipchat.yml | 36 + .../tasks/kafka.yml | 37 + .../tasks/line.yml | 36 + .../tasks/main.yml | 20 + .../tasks/opsgenie.yml | 37 + .../tasks/pagerduty.yml | 36 + .../tasks/prometheus.yml | 36 + .../tasks/pushover.yml | 37 + .../tasks/sensu.yml | 36 + .../tasks/slack-and-beyond.yml | 72 ++ .../tasks/teams.yml | 36 + .../tasks/telegram.yml | 37 + .../tasks/threema.yml | 38 + .../tasks/victorops.yml | 36 + .../tasks/webhook.yml | 36 + 21 files changed, 1534 insertions(+) create mode 100644 plugins/modules/grafana_notification_channel.py create mode 100644 tests/integration/targets/grafana_notification_channel/defaults/main.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/dingding.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/discord.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/email.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/kafka.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/line.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/main.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/pushover.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/sensu.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/teams.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/telegram.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/threema.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/victorops.yml create mode 100644 tests/integration/targets/grafana_notification_channel/tasks/webhook.yml diff --git a/plugins/modules/grafana_notification_channel.py b/plugins/modules/grafana_notification_channel.py new file mode 100644 index 00000000..11a0f390 --- /dev/null +++ b/plugins/modules/grafana_notification_channel.py @@ -0,0 +1,815 @@ +#!/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 . + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: grafana_notification_channel +author: + - Aliaksandr Mianzhynski (@amenzhinsky) +short_description: Manage Grafana Notification Channels +description: + - Create/Update/Delete Grafana Notification Channels via API. +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.. + default: 1 + type: int + state: + type: str + default: present + choices: + - present + - absent + description: + - Status of the notification channel. + uid: + type: str + description: + - The channel unique identifier. + name: + type: str + description: + - The name of the notification channel. + - Required when I(state) is C(present). + type: + type: str + choices: + - dingding + - discord + - email + - googlechat + - hipchat + - kafka + - line + - teams + - opsgenie + - pagerduty + - prometheus + - pushover + - sensu + - slack + - telegram + - threema + - victorops + - webhook + description: + - The channel notification type. + - Required when I(state) is C(present). + is_default: + type: bool + default: 'no' + description: + - Use this channel for all alerts. + include_image: + type: bool + default: 'no' + description: + - Capture a visualization image and attach it to notifications. + disable_resolve_message: + type: bool + default: 'no' + description: + - Disable the resolve message. + reminder_frequency: + type: str + description: + - Additional notifications interval for triggered alerts. + - For example C(15m). + dingding_url: + type: str + description: + - DingDing webhook URL. + dingding_message_type: + type: list + elements: str + choices: + - link + - action_card + description: + - DingDing message type. + discord_url: + type: str + description: + - Discord webhook URL. + discord_message_content: + type: str + description: + - Overrides message content. + email_addresses: + type: list + elements: str + description: + - List of recipients. + email_single: + type: bool + description: + - Send single email to all recipients. + googlechat_url: + type: str + description: + - Google Hangouts webhook URL. + hipchat_url: + type: str + description: + - HipChat webhook URL. + hipchat_api_key: + type: str + description: + - HipChat API key. + hipchat_room_id: + type: str + description: + - HipChat room ID. + kafka_url: + type: str + description: + - Kafka REST proxy URL. + kafka_topic: + type: str + description: + - Kafka topic name. + line_token: + type: str + description: + - LINE token. + teams_url: + type: str + description: + - Microsoft Teams webhook URL. + opsgenie_url: + type: str + description: + - OpsGenie webhook URL. + opsgenie_api_key: + type: str + description: + - OpsGenie API key. + opsgenie_auto_close: + type: bool + description: + - Automatically close alerts in OpsGenie once the alert goes back to ok. + opsgenie_override_priority: + type: bool + description: + - Allow the alert priority to be set using the og_priority tag. + pagerduty_integration_key: + type: str + description: + - PagerDuty integration key. + pagerduty_severity: + type: list + elements: str + choices: + - critical + - error + - warning + - info + description: + - Alert severity in PagerDuty. + pagerduty_auto_resolve: + type: bool + description: + - Resolve incidents in PagerDuty once the alert goes back to ok. + pagerduty_message_in_details: + type: bool + description: + - Move the alert message from the PD summary into the custom details. + - This changes the custom details object and may break event rules you have configured. + prometheus_url: + type: str + description: + - Prometheus API URL. + prometheus_username: + type: str + description: + - Prometheus username. + prometheus_password: + type: str + description: + - Prometheus password. + pushover_api_token: + type: str + description: + - Pushover API token. + pushover_user_key: + type: str + description: + - Pushover user key. + pushover_devices: + type: list + elements: str + description: + - Devices list in Pushover. + pushover_priority: + type: list + elements: str + choices: + - emergency + - high + - normal + - low + - lowest + description: + - Alert priority in Pushover. + pushover_retry: + type: int + description: + - Retry in C(n) minutes. + - Only when priority is C(emergency). + pushover_expire: + type: int + description: + - Expire alert in C(n) minutes. + - Only when priority is C(emergency). + pushover_alert_sound: + type: str + description: + - Alert sound in Pushover. + - L(https://pushover.net/api#sounds) + pushover_ok_sound: + type: str + description: + - OK sound in Pushover. + - L(https://pushover.net/api#sounds) + sensu_url: + type: str + description: + - Sensu webhook URL. + sensu_source: + type: str + description: + - Source in Sensu. + sensu_handler: + type: str + description: + - Sensu handler name. + sensu_username: + type: str + description: + - Sensu user. + sensu_password: + type: str + description: + - Sensu password. + slack_url: + type: str + description: + - Slack webhook URL. + slack_recipient: + type: str + description: + - Override default Slack channel or user. + slack_username: + type: str + description: + - Set the username for the bot's message. + slack_icon_emoji: + type: str + description: + - An emoji to use for the bot's message. + slack_icon_url: + type: str + description: + - URL to an image to use as the icon for the bot's message + slack_mention_users: + type: list + elements: str + description: + - Mention users list. + slack_mention_groups: + type: list + elements: str + description: + - Mention groups list. + slack_mention_channel: + type: list + elements: str + choices: + - here + - channel + description: + - Mention whole channel or just active members. + slack_token: + type: str + description: + - Slack token. + telegram_bot_token: + type: str + description: + - Telegram bot token; + telegram_chat_id: + type: str + description: + - Telegram chat id. + threema_gateway_id: + type: str + description: + - 8 character Threema Gateway ID (starting with a *). + threema_recepient_id: + type: str + description: + - 8 character Threema ID that should receive the alerts. + threema_api_secret: + type: str + description: + - Threema Gateway API secret. + victorops_url: + type: str + description: + - VictorOps webhook URL. + victorops_auto_resolve: + type: bool + description: + - Resolve incidents in VictorOps once the alert goes back to ok. + webhook_url: + type: str + description: + - Webhook URL + webhook_username: + type: str + description: + - Webhook username. + webhook_password: + type: str + description: + - Webhook password. + webhook_http_method: + type: list + elements: str + choices: + - POST + - PUT + description: + - Webhook HTTP verb to use. + +extends_documentation_fragment: + - community.grafana.basic_auth + - community.grafana.api_key +''' + + +EXAMPLES = ''' +- name: Create slack notification channel + register: result + 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}}" + +- name: Delete slack notification channel + register: result + grafana_notification_channel: + state: absent + uid: slack + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" +''' + +RETURN = ''' +notification_channel: + description: Notification channel created or updated by the module. + returned: changed + type: dict + sample: | + { + "created": "2020-11-10T21:10:19.675308051+03:00", + "disableResolveMessage": false, + "frequency": "", + "id": 37, + "isDefault": false, + "name": "Oops", + "secureFields": {}, + "sendReminder": false, + "settings": { + "uploadImage": false, + "url": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER" + }, + "type": "slack", + "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 + + +class GrafanaAPIException(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_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'] + else: + module.params['force_basic_auth'] = True + grafana_switch_organisation(module, data['grafana_url'], data['org_id'], headers) + return headers + + +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'] + } + } + + if data.get('reminder_frequency'): + payload['sendReminder'] = True + payload['frequency'] = data['reminder_frequency'] + + if data['type'] == 'dingding': + payload['settings']['url'] = data['dingding_url'] + if data.get('dingding_message_type'): + payload['settings']['msgType'] = { + 'link': 'link', + 'action_card': 'actionCard', + }[data['dingding_message_type']] + + elif data['type'] == 'discord': + payload['settings']['url'] = data['discord_url'] + if data.get('discord_message_content'): + payload['settings']['content'] = data['discord_message_content'] + + elif data['type'] == 'email': + payload['settings']['addresses'] = ';'.join(data['email_addresses']) + if data.get('email_single'): + payload['settings']['singleEmail'] = data['email_single'] + + elif data['type'] == 'googlechat': + payload['settings']['url'] = data['googlechat_url'] + + elif data['type'] == 'hipchat': + 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'] + + 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': + 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'] + + elif data['type'] == 'prometheus': + 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'] + + elif data['type'] == 'pushover': + 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'] + + elif data['type'] == 'sensu': + 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'] + + elif data['type'] == 'slack': + 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'] + + elif data['type'] == 'telegram': + payload['settings']['bottoken'] = data['telegram_bot_token'] + payload['settings']['chatid'] = data['telegram_chat_id'] + + elif data['type'] == 'treema': + payload['settings']['gateway_id'] = data['settings_gateway_id'] + payload['settings']['recipient_id'] = data['settings_recipient_id'] + payload['settings']['api_secret'] = data['settings_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': + 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'] + + return payload + + +def grafana_create_notification_channel(module, data, headers, payload): + r, info = fetch_url(module, '%s/api/alert-notifications' % data['grafana_url'], + data=json.dumps(payload), headers=headers, method='POST') + if info['status'] == 200: + return { + 'state': 'present', + 'changed': True, + 'channel': json.loads(to_text(r.read())), + } + else: + raise GrafanaAPIException("Unable to create notification channel: %s" % info) + + +def grafana_update_notification_channel(module, data, headers, payload, before): + r, info = fetch_url(module, '%s/api/alert-notifications/uid/%s' % + (data['grafana_url'], data['uid']), + data=json.dumps(payload), headers=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'] + + if before == after: + return { + 'changed': False + } + else: + return { + 'changed': True, + 'diff': { + 'before': before, + 'after': after, + }, + 'channel': channel, + } + else: + raise GrafanaAPIException("Unable to update notification channel %s : %s" % + (data['uid'], info)) + + +def grafana_create_or_update_notification_channel(module, data): + headers = grafana_headers(module, data) + payload = grafana_notification_channel_payload(data) + r, info = fetch_url(module, '%s/api/alert-notifications/uid/%s' % + (data['grafana_url'], data['uid']), headers=headers) + if info['status'] == 200: + before = json.loads(to_text(r.read())) + return grafana_update_notification_channel(module, data, headers, payload, before) + elif info['status'] == 404: + return grafana_create_notification_channel(module, data, headers, payload) + else: + raise GrafanaAPIException("Unable to get notification channel %s : %s" % + (data['uid'], info)) + + +def grafana_delete_notification_channel(module, data): + headers = grafana_headers(module, data) + r, info = fetch_url(module, '%s/api/alert-notifications/uid/%s' % + (data['grafana_url'], data['uid']), + headers=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)) + + +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'), + telegram_chat_id=dict(type='str'), + + threema_gateway_id=dict(type='str'), + threema_recepient_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_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_recepient_id', + 'threema_api_secret']], + ['type', 'victorops', ['victorops_url']], + ['type', 'webhook', ['webhook_url']] + ] + ) + + if module.params['state'] == 'present': + result = grafana_create_or_update_notification_channel(module, module.params) + module.exit_json(failed=False, **result) + else: + result = grafana_delete_notification_channel(module, module.params) + module.exit_json(failed=False, **result) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/grafana_notification_channel/defaults/main.yml b/tests/integration/targets/grafana_notification_channel/defaults/main.yml new file mode 100644 index 00000000..500c1bb8 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/defaults/main.yml @@ -0,0 +1,7 @@ +--- + +grafana_url: "http://grafana:3000" +grafana_username: "admin" +grafana_password: "admin" + +... diff --git a/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml b/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml new file mode 100644 index 00000000..58cffbb0 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/dingding.yml @@ -0,0 +1,36 @@ +--- +- name: Create dingding notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete dingding notification channel + register: result + grafana_notification_channel: + uid: dingding + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/discord.yml b/tests/integration/targets/grafana_notification_channel/tasks/discord.yml new file mode 100644 index 00000000..917d96d9 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/discord.yml @@ -0,0 +1,36 @@ +--- +- name: Create discord notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete discord notification channel + register: result + grafana_notification_channel: + uid: discord + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/email.yml b/tests/integration/targets/grafana_notification_channel/tasks/email.yml new file mode 100644 index 00000000..d224cf50 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/email.yml @@ -0,0 +1,38 @@ +--- +- name: Create email notification channel + register: result + grafana_notification_channel: + uid: email + name: email + type: email + email_addresses: + - foo@example.org + - bar@example.org + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete discord notification channel + register: result + grafana_notification_channel: + uid: email + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml b/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml new file mode 100644 index 00000000..f45516cf --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/googlechat.yml @@ -0,0 +1,36 @@ +--- +- name: Create googlechat notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete googlechat notification channel + register: result + grafana_notification_channel: + uid: googlechat + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml b/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml new file mode 100644 index 00000000..af8fed7f --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/hipchat.yml @@ -0,0 +1,36 @@ +--- +- name: Create hipchat notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete hipchat notification channel + register: result + grafana_notification_channel: + uid: hipchat + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml b/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml new file mode 100644 index 00000000..ca950414 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/kafka.yml @@ -0,0 +1,37 @@ +--- +- name: Create kafka notification channel + register: result + grafana_notification_channel: + uid: kafka + name: kafka + type: kafka + kafka_url: https://example.org + kafka_topic: test + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete kafka notification channel + register: result + grafana_notification_channel: + uid: kafka + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/line.yml b/tests/integration/targets/grafana_notification_channel/tasks/line.yml new file mode 100644 index 00000000..e8ba02ca --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/line.yml @@ -0,0 +1,36 @@ +--- +- name: Create line notification channel + register: result + grafana_notification_channel: + uid: line + name: line + type: line + line_token: xxx + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete line notification channel + register: result + grafana_notification_channel: + uid: line + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/main.yml b/tests/integration/targets/grafana_notification_channel/tasks/main.yml new file mode 100644 index 00000000..5ea36676 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/main.yml @@ -0,0 +1,20 @@ +--- +- 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 diff --git a/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml b/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml new file mode 100644 index 00000000..bbf8617e --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/opsgenie.yml @@ -0,0 +1,37 @@ +--- +- name: Create opsgenie notification channel + register: result + grafana_notification_channel: + uid: opsgenie + name: opsgenie + type: opsgenie + opsgenie_url: https://example.org + opsgenie_api_key: xxx + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete opsgenie notification channel + register: result + grafana_notification_channel: + uid: opsgenie + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml b/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml new file mode 100644 index 00000000..d2b09f1a --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/pagerduty.yml @@ -0,0 +1,36 @@ +--- +- name: Create pagerduty notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete pagerduty notification channel + register: result + grafana_notification_channel: + uid: pagerduty + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml b/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml new file mode 100644 index 00000000..e42868e5 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/prometheus.yml @@ -0,0 +1,36 @@ +--- +- name: Create prometheus notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete prometheus notification channel + register: result + grafana_notification_channel: + uid: prometheus + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml b/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml new file mode 100644 index 00000000..0fe04d55 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/pushover.yml @@ -0,0 +1,37 @@ +--- +- name: Create pushover notification channel + register: result + grafana_notification_channel: + uid: pushover + name: pushover + type: pushover + pushover_api_token: xxx + pushover_user_key: yyy + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete pushover notification channel + register: result + grafana_notification_channel: + uid: pushover + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml b/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml new file mode 100644 index 00000000..eaf7ad8a --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/sensu.yml @@ -0,0 +1,36 @@ +--- +- name: Create sensu notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete sensu notification channel + register: result + grafana_notification_channel: + uid: sensu + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml b/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml new file mode 100644 index 00000000..db9efe3b --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/slack-and-beyond.yml @@ -0,0 +1,72 @@ +--- +- name: Create slack notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Check slack notification channel idempotency + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == False" + +- name: Update slack notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + +- name: Delete slack notification channel + register: result + grafana_notification_channel: + state: absent + uid: slack + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/teams.yml b/tests/integration/targets/grafana_notification_channel/tasks/teams.yml new file mode 100644 index 00000000..5f08ee9b --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/teams.yml @@ -0,0 +1,36 @@ +--- +- name: Create teams notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete teams notification channel + register: result + grafana_notification_channel: + uid: teams + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml b/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml new file mode 100644 index 00000000..2841b336 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/telegram.yml @@ -0,0 +1,37 @@ +--- +- name: Create telegram notification channel + register: result + grafana_notification_channel: + uid: telegram + name: telegram + type: telegram + telegram_bot_token: xxx + telegram_chat_id: yyy + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete telegram notification channel + register: result + grafana_notification_channel: + uid: telegram + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/threema.yml b/tests/integration/targets/grafana_notification_channel/tasks/threema.yml new file mode 100644 index 00000000..1a21ff32 --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/threema.yml @@ -0,0 +1,38 @@ +--- +- name: Create threema notification channel + register: result + grafana_notification_channel: + uid: threema + name: threema + type: threema + threema_gateway_id: xxx + threema_recepient_id: yyy + threema_api_secret: zzz + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete threema notification channel + register: result + grafana_notification_channel: + uid: threema + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml b/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml new file mode 100644 index 00000000..b043d2cf --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/victorops.yml @@ -0,0 +1,36 @@ +--- +- name: Create victorops notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete victorops notification channel + register: result + grafana_notification_channel: + uid: victorops + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'" diff --git a/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml b/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml new file mode 100644 index 00000000..fc1cb56a --- /dev/null +++ b/tests/integration/targets/grafana_notification_channel/tasks/webhook.yml @@ -0,0 +1,36 @@ +--- +- name: Create webhook notification channel + register: result + 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}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'present'" + +- name: Delete webhook notification channel + register: result + grafana_notification_channel: + uid: webhook + state: absent + grafana_url: "{{ grafana_url }}" + grafana_user: "{{ grafana_username }}" + grafana_password: "{{ grafana_password}}" + +- debug: + var: result + +- assert: + that: + - "result.changed == True" + - "result.state == 'absent'"