diff --git a/CHANGELOG.md b/CHANGELOG.md index 061559e0..df5b49e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ## Other changes - [Docs] Mention the two available Spike-rule metrics that are add into the match record - [#1542](https://github.com/jertel/elastalert2/pull/1542) - @ulmako +- [OpsGenie] Corrected spelling of the `opsgenie_default_receipients` configuration option to `opsgenie_default_recipients`. Both variations will continue to work and a warning message will notify affected users. [#1539](https://github.com/jertel/elastalert2/pull/1539) - @lstyles +- [OpsGenie] Prevent templated `opsgenie_teams` and `opsgenie_recipients` from being overwritten with evaluated values first time an alert is sent. [#1540](https://github.com/jertel/elastalert2/issues/1540) [#1539](https://github.com/jertel/elastalert2/pull/1539) - @lstyles # 2.20.0 diff --git a/docs/source/alerts.rst b/docs/source/alerts.rst index ad0d41b3..9b90f785 100644 --- a/docs/source/alerts.rst +++ b/docs/source/alerts.rst @@ -1790,7 +1790,7 @@ Optional: ``opsgenie_recipients_args``: Map of arguments used to format opsgenie_recipients. -``opsgenie_default_receipients``: List of default recipients to notify when the formatting of opsgenie_recipients is unsuccesful. +``opsgenie_default_recipients``: List of default recipients to notify when the formatting of opsgenie_recipients is unsuccesful. ``opsgenie_teams``: A list of OpsGenie teams to notify (useful for schedules with escalation). diff --git a/elastalert/alerters/opsgenie.py b/elastalert/alerters/opsgenie.py index 471ae87e..87969fb6 100644 --- a/elastalert/alerters/opsgenie.py +++ b/elastalert/alerters/opsgenie.py @@ -13,9 +13,14 @@ class OpsGenieAlerter(Alerter): def __init__(self, *args): super(OpsGenieAlerter, self).__init__(*args) + + default_recipients_deprecated = self.rule.get('opsgenie_default_receipients', None) + if default_recipients_deprecated: + elastalert_logger.warning("OpsGenieAlerter: `opsgenie_default_receipients` rule configuration option is deprecated and will be removed in the future. Please use `opsgenie_default_recipients` option instead.") + self.account = self.rule.get('opsgenie_account') self.api_key = self.rule.get('opsgenie_key', 'key') - self.default_reciepients = self.rule.get('opsgenie_default_receipients', None) + self.default_recipients = self.rule.get('opsgenie_default_recipients', default_recipients_deprecated) self.recipients = self.rule.get('opsgenie_recipients') self.recipients_args = self.rule.get('opsgenie_recipients_args') self.default_teams = self.rule.get('opsgenie_default_teams', None) @@ -35,25 +40,29 @@ def __init__(self, *args): self.source = self.rule.get('opsgenie_source', 'ElastAlert') def _parse_responders(self, responders, responder_args, matches, default_responders): - if responder_args: - formated_responders = list() - responders_values = dict((k, lookup_es_key(matches[0], v)) for k, v in responder_args.items()) - responders_values = dict((k, v) for k, v in responders_values.items() if v) - - for responder in responders: - responder = str(responder) - try: - formated_responders.append(responder.format(**responders_values)) - except KeyError as error: - elastalert_logger.warning("OpsGenieAlerter: Cannot create responder for OpsGenie Alert. Key not foud: %s. " % (error)) - if not formated_responders: - elastalert_logger.warning("OpsGenieAlerter: no responders can be formed. Trying the default responder ") - if not default_responders: - elastalert_logger.warning("OpsGenieAlerter: default responder not set. Falling back") - formated_responders = responders - else: - formated_responders = default_responders - responders = formated_responders + if responders is None: + return None + if responder_args is None: + responder_args = dict() + + formated_responders = list() + responders_values = dict((k, lookup_es_key(matches[0], v)) for k, v in responder_args.items()) + responders_values = dict((k, v) for k, v in responders_values.items() if v) + for responder in responders: + responder = str(responder) + try: + formated_responders.append(responder.format(**responders_values)) + except KeyError as error: + elastalert_logger.warning("OpsGenieAlerter: Cannot create responder for OpsGenie Alert. Key not found: %s. " % (error)) + if not formated_responders: + elastalert_logger.warning("OpsGenieAlerter: no responders can be formed. Trying the default responder ") + if not default_responders: + elastalert_logger.warning("OpsGenieAlerter: default responder not set. Falling back") + formated_responders = responders + else: + formated_responders = default_responders + responders = formated_responders + return responders def alert(self, matches): @@ -68,16 +77,16 @@ def alert(self, matches): self.message = self.create_title(matches) else: self.message = self.custom_message.format(**matches[0]) - self.recipients = self._parse_responders(self.recipients, self.recipients_args, matches, self.default_reciepients) - self.teams = self._parse_responders(self.teams, self.teams_args, matches, self.default_teams) + recipients = self._parse_responders(self.recipients, self.recipients_args, matches, self.default_recipients) + teams = self._parse_responders(self.teams, self.teams_args, matches, self.default_teams) post = {} post['message'] = self.message if self.account: post['user'] = self.account - if self.recipients: - post['responders'] = [{'username': r, 'type': 'user'} for r in self.recipients] - if self.teams: - post['teams'] = [{'name': r, 'type': 'team'} for r in self.teams] + if recipients: + post['responders'] = [{'username': r, 'type': 'user'} for r in recipients] + if teams: + post['teams'] = [{'name': r, 'type': 'team'} for r in teams] if self.description: post['description'] = self.description.format(**matches[0]) else: diff --git a/tests/alerters/opsgenie_test.py b/tests/alerters/opsgenie_test.py index 9db83b28..bbcb6ca8 100644 --- a/tests/alerters/opsgenie_test.py +++ b/tests/alerters/opsgenie_test.py @@ -105,21 +105,23 @@ def test_opsgenie_alert_routing(): 'opsgenie_key': 'ogkey', 'opsgenie_account': 'genies', 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', - 'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'], - 'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'}, + 'opsgenie_recipients': ['{RECIPIENT_PREFIX}'], + 'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'}, 'type': mock_rule(), 'filter': [{'query': {'query_string': {'query': '*hihi*'}}}], 'alert': 'opsgenie', 'opsgenie_teams': ['{TEAM_PREFIX}-Team'], 'opsgenie_teams_args': {'TEAM_PREFIX': 'team'} } - with mock.patch('requests.post'): - + with mock.patch('requests.post') as mock_post: alert = OpsGenieAlerter(rule) alert.alert([{'@timestamp': '2014-10-31T00:00:00', 'team': "Test", 'recipient': "lytics"}]) - assert alert.get_info()['teams'] == ['Test-Team'] - assert alert.get_info()['recipients'] == ['lytics'] + _, kwargs = mock_post.call_args + payload = kwargs['json'] + + assert payload['teams'][0]['name'] == 'Test-Team' + assert payload['responders'][0]['username'] == 'lytics' def test_opsgenie_default_alert_routing(): @@ -128,22 +130,24 @@ def test_opsgenie_default_alert_routing(): 'opsgenie_key': 'ogkey', 'opsgenie_account': 'genies', 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', - 'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'], - 'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'}, + 'opsgenie_recipients': ['{RECIPIENT_PREFIX}'], + 'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'}, 'type': mock_rule(), 'filter': [{'query': {'query_string': {'query': '*hihi*'}}}], 'alert': 'opsgenie', 'opsgenie_teams': ['{TEAM_PREFIX}-Team'], - 'opsgenie_default_receipients': ["devops@test.com"], - 'opsgenie_default_teams': ["Test"] + 'opsgenie_default_recipients': ["devops@test.com"], + 'opsgenie_default_teams': ["Default-Team"] } - with mock.patch('requests.post'): + with mock.patch('requests.post') as mock_post: alert = OpsGenieAlerter(rule) alert.alert([{'@timestamp': '2014-10-31T00:00:00', 'team': "Test"}]) - assert alert.get_info()['teams'] == ['{TEAM_PREFIX}-Team'] - assert alert.get_info()['recipients'] == ['devops@test.com'] + _, kwargs = mock_post.call_args + payload = kwargs['json'] + assert payload['teams'][0]['name'] == 'Default-Team' + assert payload['responders'][0]['username'] == 'devops@test.com' def test_opsgenie_details_with_constant_value(): @@ -1006,8 +1010,8 @@ def test_opsgenie_parse_responders(caplog): 'opsgenie_key': 'ogkey', 'opsgenie_account': 'genies', 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', - 'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'], - 'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'}, + 'opsgenie_recipients': ['{RECIPIENT_PREFIX}'], + 'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'}, 'type': mock_rule(), 'filter': [{'query': {'query_string': {'query': '*hihi*'}}}], 'alert': 'opsgenie', @@ -1040,7 +1044,7 @@ def test_opsgenie_parse_responders(caplog): assert expected == actual user, level, message = caplog.record_tuples[0] assert logging.WARNING == level - assert "Cannot create responder for OpsGenie Alert. Key not foud: 'RECEIPIENT_PREFIX'." in message + assert "Cannot create responder for OpsGenie Alert. Key not found: 'RECIPIENT_PREFIX'." in message user, level, message = caplog.record_tuples[1] assert logging.WARNING == level assert 'no responders can be formed. Trying the default responder' in message @@ -1049,12 +1053,79 @@ def test_opsgenie_parse_responders(caplog): assert 'default responder not set. Falling back' in message user, level, message = caplog.record_tuples[3] assert logging.WARNING == level - assert "Cannot create responder for OpsGenie Alert. Key not foud: 'TEAM_PREFIX'." in message + assert "Cannot create responder for OpsGenie Alert. Key not found: 'TEAM_PREFIX'." in message user, level, message = caplog.record_tuples[4] assert logging.WARNING == level assert 'no responders can be formed. Trying the default responder' in message +def test_opsgenie_parse_opsgenie_teams(): + rule = { + 'name': 'testOGalert', + 'opsgenie_key': 'ogkey', + 'opsgenie_account': 'genies', + 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', + 'type': mock_rule(), + 'filter': [{'query': {'query_string': {'query': '*hihi*'}}}], + 'alert': 'opsgenie', + 'opsgenie_teams': ['{TEAM_PREFIX}-Team'], + 'opsgenie_teams_args': {'TEAM_PREFIX': 'team'}, + 'opsgenie_default_teams': ["Test"] + } + match = [ + { + '@timestamp': '2014-10-10T00:00:00', + 'sender_ip': '1.1.1.1', + 'hostname': 'aProbe', + 'team': 'Test' + }, + { + '@timestamp': '2014-10-10T00:00:00', + 'sender_ip': '1.1.1.1', + 'hostname2': 'aProbe', + 'team': 'Test' + } + ] + + with mock.patch('requests.post'): + alert = OpsGenieAlerter(rule) + alert.alert(match) + assert alert.teams == rule['opsgenie_teams'] + + +def test_opsgenie_parse_opsgenie_recipients(): + rule = { + 'name': 'testOGalert', + 'opsgenie_key': 'ogkey', + 'opsgenie_account': 'genies', + 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', + 'opsgenie_recipients': ['{RECIPIENT_PREFIX}'], + 'opsgenie_recipients_args': {'RECIPIENT_PREFIX': 'recipient'}, + 'type': mock_rule(), + 'filter': [{'query': {'query_string': {'query': '*hihi*'}}}], + 'alert': 'opsgenie' + } + match = [ + { + '@timestamp': '2014-10-10T00:00:00', + 'sender_ip': '1.1.1.1', + 'hostname': 'aProbe', + 'recipient': 'Test' + }, + { + '@timestamp': '2014-10-10T00:00:00', + 'sender_ip': '1.1.1.1', + 'hostname2': 'aProbe', + 'recipient': 'Test' + } + ] + + with mock.patch('requests.post'): + alert = OpsGenieAlerter(rule) + alert.alert(match) + assert alert.recipients == rule['opsgenie_recipients'] + + def test_opsgenie_create_custom_title(): rule = { 'name': 'Opsgenie Details',