Skip to content

Commit

Permalink
Merge pull request #315 from konstantin-kornienko/opsgenie_source_entity
Browse files Browse the repository at this point in the history
[Opsgenie] Added possibility to specify `source` and `entity` attrs
  • Loading branch information
jertel authored Jul 1, 2021
2 parents 3856219 + d3f3526 commit 96c0066
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 11 deletions.
6 changes: 5 additions & 1 deletion docs/source/ruletypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2340,12 +2340,16 @@ Optional:

``opsgenie_subject_args``: A list of fields to use to format ``opsgenie_subject`` if it contains formaters.

``opsgenie_priority``: Set the OpsGenie priority level. Possible values are P1, P2, P3, P4, P5.
``opsgenie_priority``: Set the OpsGenie priority level. Possible values are P1, P2, P3, P4, P5. Can be formatted with fields from the first match e.g "P{level}"

``opsgenie_details``: Map of custom key/value pairs to include in the alert's details. The value can sourced from either fields in the first match, environment variables, or a constant value.

``opsgenie_proxy``: By default ElastAlert will not use a network proxy to send notifications to OpsGenie. Set this option using ``hostname:port`` if you need to use a proxy. only supports https.

``opsgenie_source``: Set the OpsGenie source, default is `ElastAlert`. Can be formatted with fields from the first match e.g "{source} {region}"

``opsgenie_entity``: Set the OpsGenie entity. Can be formatted with fields from the first match e.g "{host_name}"

Example usage::

opsgenie_details:
Expand Down
16 changes: 12 additions & 4 deletions elastalert/alerters/opsgenie.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def __init__(self, *args):
self.opsgenie_proxy = self.rule.get('opsgenie_proxy', None)
self.priority = self.rule.get('opsgenie_priority')
self.opsgenie_details = self.rule.get('opsgenie_details', {})
self.entity = self.rule.get('opsgenie_entity', None)
self.source = self.rule.get('opsgenie_source', 'ElastAlert')

def _parse_responders(self, responders, responder_args, matches, default_responders):
if responder_args:
Expand Down Expand Up @@ -76,17 +78,23 @@ def alert(self, matches):
if self.teams:
post['teams'] = [{'name': r, 'type': 'team'} for r in self.teams]
post['description'] = body
post['source'] = 'ElastAlert'
if self.entity:
post['entity'] = self.entity.format(**matches[0])
if self.source:
post['source'] = self.source.format(**matches[0])

for i, tag in enumerate(self.tags):
self.tags[i] = tag.format(**matches[0])
post['tags'] = self.tags

if self.priority and self.priority not in ('P1', 'P2', 'P3', 'P4', 'P5'):
priority = self.priority
if priority:
priority = priority.format(**matches[0])
if priority and priority not in ('P1', 'P2', 'P3', 'P4', 'P5'):
elastalert_logger.warning("Priority level does not appear to be specified correctly. \
Please make sure to set it to a value between P1 and P5")
Please make sure to set it to a value between P1 and P5")
else:
post['priority'] = self.priority
post['priority'] = priority

if self.alias is not None:
post['alias'] = self.alias.format(**matches[0])
Expand Down
2 changes: 2 additions & 0 deletions elastalert/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,8 @@ properties:
opsgenie_subject: {type: string}
opsgenie_priority: {type: string}
opsgenie_proxy: {type: string}
opsgenie_source: {type: string}
opsgenie_entity: {type: string}
opsgenie_details:
type: object
minProperties: 1
Expand Down
10 changes: 8 additions & 2 deletions examples/rules/example_opsgenie_frequency.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ opsgenie_key: ogkey
# (Optional)
# OpsGenie recipients with args
# opsgenie_recipients:
# - {recipient}
# - {recipient}
# opsgenie_recipients_args:
# team_prefix:'user.email'

Expand All @@ -36,7 +36,7 @@ opsgenie_key: ogkey
# (Optional)
# OpsGenie teams with args
# opsgenie_teams:
# - {team_prefix}-Team
# - {team_prefix}-Team
# opsgenie_teams_args:
# team_prefix:'team'

Expand All @@ -45,6 +45,12 @@ opsgenie_key: ogkey
opsgenie_tags:
- "Production"

# (Optional) OpsGenie source
# opsgenie_source: ElastAlert_EMEA

# (Optional) OpsGenie entity
# opsgenie_entity: '{hostname}'

# (OptionaL) Connect with SSL to Elasticsearch
#use_ssl: True

Expand Down
38 changes: 34 additions & 4 deletions tests/alerters/opsgenie_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_opsgenie_basic(caplog):
assert mock_post.called

assert mcal[0][1]['headers']['Authorization'] == 'GenieKey ogkey'
assert mcal[0][1]['json']['source'] == 'ElastAlert'
# Should be default source 'ElastAlert', because 'opsgenie_source' param isn't set in rule
assert mcal[0][1]['json']['source'] == 'ElastAlert'
user, level, message = caplog.record_tuples[0]
assert "Error response from https://api.opsgenie.com/v2/alerts \n API Response: <MagicMock name='post()' id=" not in message
Expand Down Expand Up @@ -66,7 +66,6 @@ def test_opsgenie_basic_not_status_code_202(caplog):
assert mcal[0][1]['headers']['Authorization'] == 'GenieKey ogkey'
assert mcal[0][1]['json']['source'] == 'ElastAlert'
assert mcal[0][1]['json']['responders'] == [{'username': 'lytics', 'type': 'user'}]
assert mcal[0][1]['json']['source'] == 'ElastAlert'
user, level, message = caplog.record_tuples[0]
assert "Error response from https://api.opsgenie.com/v2/alerts \n API Response: <MagicMock name='post()' id=" in message
assert ('elastalert', logging.INFO, 'Alert sent to OpsGenie') == caplog.record_tuples[1]
Expand Down Expand Up @@ -98,8 +97,6 @@ def test_opsgenie_frequency():
assert mcal[0][1]['headers']['Authorization'] == 'GenieKey ogkey'
assert mcal[0][1]['json']['source'] == 'ElastAlert'
assert mcal[0][1]['json']['responders'] == [{'username': 'lytics', 'type': 'user'}]
assert mcal[0][1]['json']['source'] == 'ElastAlert'
assert mcal[0][1]['json']['source'] == 'ElastAlert'


def test_opsgenie_alert_routing():
Expand Down Expand Up @@ -878,3 +875,36 @@ def test_opsgenie_required_error(opsgenie_key, expected_data):
assert expected_data == actual_data
except Exception as ea:
assert expected_data in str(ea)


@pytest.mark.parametrize('opsgenie_entity, expected_entity, opsgenie_priority, expected_priority', [
('const host', 'const host', 'P1', 'P1'),
('host {hostname}', 'host server_a', 'P{level}', 'P2'),
('Elastalert {source}', 'Elastalert EMEA', '{priority}', 'P3'),
])
def test_opsgenie_substitution(opsgenie_entity, expected_entity, opsgenie_priority, expected_priority):
rule = {
'name': 'Opsgenie Details',
'type': mock_rule(),
'opsgenie_entity': opsgenie_entity,
'opsgenie_priority': opsgenie_priority,
}
matches = [{
'message': 'Testing',
'hostname': 'server_a',
'source': 'EMEA',
'level': '2',
'priority': 'P3',
'@timestamp': '2014-10-31T00:00:00'
}]
alert = OpsGenieAlerter(rule)

with mock.patch('requests.post') as mock_post:
alert = OpsGenieAlerter(rule)
alert.alert(matches)

mcal = mock_post._mock_call_args_list
assert mock_post.called

assert mcal[0][1]['json']['entity'] == expected_entity
assert mcal[0][1]['json']['priority'] == expected_priority

0 comments on commit 96c0066

Please sign in to comment.