def test_http_post_post_data_nested_dict(): # Posting - nested post data structures (new style) alert = None rule = { 'type': mock_rule(), 'name': 'Test HTTP Post Rule', 'http_post_url' : 'http://www.example.com/endpoint', 'http_post_headers' : { 'name1':'value1' }, 'http_post_data' : { '@timestamp' : '{@timestamp}', 'data': { 'somefield1' : '{somefield}', 'nestedfield' : '{nested[field]}' } }, 'new_style_string_format': True } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = HttpPostAlerter(rule) expected_data = '{"@timestamp": "2014-01-01T00:00:00", "data": {"nestedfield": "1", "somefield1": "foobarbaz"}}' expected_headers = {'Content-Type': 'application/json', 'Accept': 'application/json;charset=utf-8', 'name1': 'value1'} with mock.patch("elastalert_modules.cx_alerters.requests.post") as mock_request: alert.alert([match]) mock_request.assert_called_with('http://www.example.com/endpoint', data=expected_data, headers=expected_headers)
def test_msend_service_slotsetvalues_mc_notes(): rule = { 'type': mock_rule(), 'new_style_string_format': True, 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_service_url' : 'http://www.example.com/', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', 'msend_slotsetvalues' : { 'mc_host': 'testhost', 'mc_tool': 'Elasticsearch', 'mc_long_msg': '{match[somefield]}', 'mc_more_msg': '{match[nested][field]}', 'mc_notes': ['{match[@timestamp]}', 'ElasticSearch', 'notes #1', '{match[@timestamp]}', 'ElasticSearch','notes #2', '{match[@timestamp]}', 'ElasticSearch','more notes'] } } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = MSendServiceAlerter(rule) expected_headers = {'Content-Type': 'application/json', 'Accept': 'application/json;charset=utf-8'} expected_data = '{"msend_event_severity": "WARNING", "msend_event_slotvalues": {"mc_long_msg": "foobarbaz", "mc_more_msg": "1", "mc_notes": "[\'0x52c35a80\',\'ElasticSearch\',\'notes #1\',\'0x52c35a80\',\'ElasticSearch\',\'notes #2\',\'0x52c35a80\',\'ElasticSearch\',\'more notes\']", "mc_host": "testhost", "mc_tool": "Elasticsearch"}, "msend_cell_name": "somecellname", "msend_event_message": "Alert Subject", "msend_event_class": "EVENT"}' with mock.patch("elastalert_modules.cx_alerters.requests.post") as mock_post: alert.alert([match]) mock_post.assert_called_with('http://www.example.com/', headers = expected_headers, data = expected_data)
def test_http_post_old_style_post_data(): # Posting - Data formatting (old style) alert = None rule = { 'type': mock_rule(), 'name': 'Test HTTP Post Rule', 'http_post_url' : 'http://www.example.com/endpoint', 'http_post_headers' : { 'name1':'value1' }, 'http_post_data' : { '@timestamp' : '%(@timestamp)s', 'somefield1' : '%(somefield)s' }, 'new_style_string_format': False } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = HttpPostAlerter(rule) expected_data = '{"@timestamp": "2014-01-01T00:00:00", "somefield1": "foobarbaz"}' expected_headers = {'Content-Type': 'application/json', 'Accept': 'application/json;charset=utf-8', 'name1': 'value1'} with mock.patch("elastalert_modules.cx_alerters.requests.post") as mock_request: alert.alert([match]) mock_request.assert_called_with('http://www.example.com/endpoint', data=expected_data, headers=expected_headers)
def test_opsgenie_details_with_constant_value_matchs(): rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), 'opsgenie_account': 'genies', 'opsgenie_key': 'ogkey', 'opsgenie_details': { 'Foo': 'Bar' } } match = {'@timestamp': '2014-10-31T00:00:00'} alert = OpsGenieAlerter(rule) with mock.patch('requests.post') as mock_post_request: alert.alert([match, match]) mock_post_request.assert_called_once_with( 'https://api.opsgenie.com/v2/alerts', headers={ 'Content-Type': 'application/json', 'Authorization': 'GenieKey ogkey' }, json=mock.ANY, proxies=None) expected_json = { 'description': 'Opsgenie Details\n' '\n' "{'@timestamp': '2014-10-31T00:00:00'}\n" '\n' '@timestamp: 2014-10-31T00:00:00\n' '\n' '----------------------------------------\n' 'Opsgenie Details\n' '\n' "{'@timestamp': '2014-10-31T00:00:00'}\n" '\n' '@timestamp: 2014-10-31T00:00:00\n' '\n' '----------------------------------------\n', 'details': { 'Foo': 'Bar' }, 'message': 'ElastAlert: Opsgenie Details', 'priority': None, 'source': 'ElastAlert', 'tags': ['ElastAlert', 'Opsgenie Details'], 'user': '******' } actual_json = mock_post_request.call_args_list[0][1]['json'] assert expected_json == actual_json
def test_opsgenie_parse_responders(caplog): caplog.set_level(logging.WARNING) rule = { 'name': 'testOGalert', 'opsgenie_key': 'ogkey', 'opsgenie_account': 'genies', 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', 'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'], 'opsgenie_recipients_args': {'RECEIPIENT_PREFIX': 'recipient'}, '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' }, { '@timestamp': '2014-10-10T00:00:00', 'sender_ip': '1.1.1.1', 'hostname2': 'aProbe' } ] with mock.patch('requests.post'): alert = OpsGenieAlerter(rule) alert.alert([{'@timestamp': '2014-10-31T00:00:00', 'team': "Test"}]) actual = alert._parse_responders( rule['opsgenie_teams'], rule['opsgenie_teams_args'], match, rule['opsgenie_default_teams'] ) excepted = ['Test'] assert excepted == 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 user, level, message = caplog.record_tuples[1] assert logging.WARNING == level assert 'no responders can be formed. Trying the default responder' in message user, level, message = caplog.record_tuples[2] assert logging.WARNING == level 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 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_email_with_args(): rule = { 'name': 'test alert', 'email': ['*****@*****.**', '*****@*****.**'], 'from_addr': '*****@*****.**', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_reply_to': '*****@*****.**', 'alert_subject': 'Test alert for {0} {1}', 'alert_subject_args': ['test_term', 'test.term'], 'alert_text': 'Test alert for {0} and {1} {2}', 'alert_text_args': ['test_arg1', 'test_arg2', 'test.arg3'], 'alert_missing_value': '<CUSTOM MISSING VALUE>' } with mock.patch('elastalert.alerters.email.SMTP') as mock_smtp: mock_smtp.return_value = mock.Mock() alert = EmailAlerter(rule) alert.alert([{ 'test_term': 'test_value', 'test_arg1': 'testing', 'test': { 'term': ':)', 'arg3': '☃' } }]) expected = [ mock.call('localhost'), mock.call().ehlo(), mock.call().has_extn('STARTTLS'), mock.call().starttls(certfile=None, keyfile=None), mock.call().sendmail(mock.ANY, ['*****@*****.**', '*****@*****.**'], mock.ANY), mock.call().quit() ] assert mock_smtp.mock_calls == expected body = mock_smtp.mock_calls[4][1][2] # Extract the MIME encoded message body body_text = base64.b64decode( body.split('\n\n')[-1][:-1]).decode('utf-8') assert 'testing' in body_text assert '<CUSTOM MISSING VALUE>' in body_text assert '☃' in body_text assert 'Reply-To: [email protected]' in body assert 'To: [email protected]' in body assert 'From: [email protected]' in body assert 'Subject: Test alert for test_value :)' in body
def test_opsgenie_getinfo(opsgenie_account, opsgenie_recipients, opsgenie_teams, expected_data): rule = {'name': 'Opsgenie Details', 'type': mock_rule()} if opsgenie_account: rule['opsgenie_account'] = opsgenie_account if opsgenie_recipients: rule['opsgenie_recipients'] = opsgenie_recipients if opsgenie_teams: rule['opsgenie_teams'] = opsgenie_teams alert = OpsGenieAlerter(rule) actual_data = alert.get_info() assert expected_data == actual_data
def test_opsgenie_details_with_non_string_field(): rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), 'opsgenie_account': 'genies', 'opsgenie_key': 'ogkey', 'opsgenie_details': { 'Age': { 'field': 'age' }, 'Message': { 'field': 'message' } } } match = { 'age': 10, 'message': { 'format': 'The cow goes %s!', 'arg0': 'moo' } } alert = OpsGenieAlerter(rule) with mock.patch('requests.post') as mock_post_request: alert.alert([match]) mock_post_request.assert_called_once_with( 'https://api.opsgenie.com/v2/alerts', headers={ 'Content-Type': 'application/json', 'Authorization': 'GenieKey ogkey' }, json=mock.ANY, proxies=None) expected_json = { 'description': BasicMatchString(rule, match).__str__(), 'details': { 'Age': '10', 'Message': "{'format': 'The cow goes %s!', 'arg0': 'moo'}" }, 'message': 'ElastAlert: Opsgenie Details', 'priority': None, 'source': 'ElastAlert', 'tags': ['ElastAlert', 'Opsgenie Details'], 'user': '******' } actual_json = mock_post_request.call_args_list[0][1]['json'] assert expected_json == actual_json
def test_msend_service_slotsetvalues_string_type(): rule = { 'type': mock_rule(), 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_service_url' : 'http://www.example.com/', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', 'msend_slotsetvalues' : 'mc_host=testhost;mc_tool=Elasticsearch;mc_object=test object;mc_object_class=test object class;mc_parameter=test parameter;mc_parameter_value=test parameter value', } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} with pytest.raises(EAException): alert = MSendServiceAlerter(rule)
def test_msend_mandatory_params(): rule = { 'type': mock_rule(), 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', } alert = MSendAlerter(rule) match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} with mock.patch("elastalert_modules.cx_alerters.subprocess.Popen") as mock_popen: alert.alert([match]) mock_popen.assert_called_with("/opt/msend/bin/msend -l /opt/msend -n somecellname -a EVENT -r WARNING -m 'Alert Subject'", stdin=subprocess.PIPE, shell=True)
def test_opsgenie_tags(): rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), 'opsgenie_account': 'genies', 'opsgenie_key': 'ogkey', 'opsgenie_details': { 'Message': {'field': 'message'}, 'Missing': {'field': 'missing'} }, 'opsgenie_tags': ['{somefield}', 'test2'] } alert = OpsGenieAlerter(rule) validateAlertTag(alert, rule, 'somevalue') validateAlertTag(alert, rule, 'anothervalue')
def test_opsgenie_required_error(opsgenie_key, expected_data): try: rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), } if opsgenie_key: rule['opsgenie_key'] = opsgenie_key alert = OpsGenieAlerter(rule) actual_data = alert.get_info() assert expected_data == actual_data except Exception as ea: assert expected_data in str(ea)
def test_msend_slotsetvalues_string_type(): rule = { 'type': mock_rule(), 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', 'msend_slotsetvalues' : 'mc_host=testhost;mc_tool=Elasticsearch;mc_object=test object;mc_object_class=test object class;mc_parameter=test parameter;mc_parameter_value=test parameter value', } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = MSendAlerter(rule) with mock.patch("elastalert_modules.cx_alerters.subprocess.Popen") as mock_popen: alert.alert([match]) mock_popen.assert_called_with("/opt/msend/bin/msend -l /opt/msend -n somecellname -a EVENT -r WARNING -m 'Alert Subject' -b 'mc_host=testhost;mc_tool=Elasticsearch;mc_object=test object;mc_object_class=test object class;mc_parameter=test parameter;mc_parameter_value=test parameter value'", stdin=subprocess.PIPE, shell=True)
def test_opsgenie_subject_args(): rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), 'opsgenie_account': 'genies', 'opsgenie_key': 'ogkey', 'opsgenie_details': { 'Message': { 'field': 'message' }, 'Missing': { 'field': 'missing' } }, 'opsgenie_subject': 'test', 'opsgenie_subject_args': ['Testing', 'message'] } match = {'message': 'Testing', '@timestamp': '2014-10-31T00:00:00'} alert = OpsGenieAlerter(rule) with mock.patch('requests.post') as mock_post_request: alert.alert([match]) mock_post_request.assert_called_once_with( 'https://api.opsgenie.com/v2/alerts', headers={ 'Content-Type': 'application/json', 'Authorization': 'GenieKey ogkey' }, json=mock.ANY, proxies=None) expected_json = { 'description': BasicMatchString(rule, match).__str__(), 'details': { 'Message': 'Testing' }, 'message': 'test', 'priority': None, 'source': 'ElastAlert', 'tags': ['ElastAlert', 'Opsgenie Details'], 'user': '******' } actual_json = mock_post_request.call_args_list[0][1]['json'] assert expected_json == actual_json
def test_http_post_mandatory_params(): # Posting - only mandatory parameters alert = None rule = { 'type': mock_rule(), 'name': 'Test HTTP Post Rule', 'http_post_url' : 'http://www.example.com/endpoint', } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = HttpPostAlerter(rule) expected_data = '{"matches": [{"@timestamp": "2014-01-01T00:00:00", "somefield": "foobarbaz", "nested": {"field": 1}}], "rule": "Test HTTP Post Rule"}' expected_headers = {'Content-Type': 'application/json', 'Accept': 'application/json;charset=utf-8'} with mock.patch("elastalert_modules.cx_alerters.requests.post") as mock_request: alert.alert([match]) mock_request.assert_called_with('http://www.example.com/endpoint', data=expected_data, headers=expected_headers)
def test_esalerter_init_validation(): # All mandatory fields present rule = { 'type': mock_rule(), 'name': 'Test ElastSearchAlerter Rule', 'es_host': '127.0.0.1', 'es_port': '9200', 'esalerter_index' : 'logstash-example', 'esalerter_document_type': 'example', 'esalerter_data' : { 'field1' : '{field1}', 'field2' : '{field2}' } } alert = ElasticSearchAlerter(rule) assert alert is not None
def test_msend_service_mandatory_params(): rule = { 'type': mock_rule(), 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_service_url' : 'http://www.example.com/', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', } alert = MSendServiceAlerter(rule) match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} expected_headers = {'Content-Type': 'application/json', 'Accept': 'application/json;charset=utf-8'} expected_data = '{"msend_event_severity": "WARNING", "msend_cell_name": "somecellname", "msend_event_message": "Alert Subject", "msend_event_class": "EVENT"}' with mock.patch("elastalert_modules.cx_alerters.requests.post") as mock_post: alert.alert([match]) mock_post.assert_called_with('http://www.example.com/', headers = expected_headers, data = expected_data)
def test_email_from_field(email_from_field, email_add_domain, match_data, expected_data): rule = { 'name': 'test alert', 'email': ['*****@*****.**'], 'email_add_domain': 'example.com', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_from_field': 'data.user', 'owner': 'owner_value' } if email_from_field: rule['email_from_field'] = email_from_field if email_add_domain: rule['email_add_domain'] = email_add_domain with mock.patch('elastalert.alerters.email.SMTP') as mock_smtp: mock_smtp.return_value = mock.Mock() alert = EmailAlerter(rule) alert.alert(match_data) assert mock_smtp.mock_calls[4][1][1] == expected_data
def test_opsgenie_alert_routing(): rule = { 'name': 'testOGalert', 'opsgenie_key': 'ogkey', 'opsgenie_account': 'genies', 'opsgenie_addr': 'https://api.opsgenie.com/v2/alerts', 'opsgenie_recipients': ['{RECEIPIENT_PREFIX}'], 'opsgenie_recipients_args': {'RECEIPIENT_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'): 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']
def test_msend_slotsetvalues_dict_variable_subst_oldstrformat(): rule = { 'type': mock_rule(), 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', 'msend_slotsetvalues' : { 'mc_host': 'testhost', 'mc_tool': 'Elasticsearch', 'mc_long_msg': '%(somefield)s', } } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = MSendAlerter(rule) with mock.patch("elastalert_modules.cx_alerters.subprocess.Popen") as mock_popen: alert.alert([match]) mock_popen.assert_called_with("/opt/msend/bin/msend -l /opt/msend -n somecellname -a EVENT -r WARNING -m 'Alert Subject' -b 'mc_long_msg=foobarbaz;mc_tool=Elasticsearch;mc_host=testhost'", stdin=subprocess.PIPE, shell=True)
def test_email_getinfo(): rule = { 'name': 'test alert', 'email': ['*****@*****.**', '*****@*****.**'], 'from_addr': '*****@*****.**', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_reply_to': '*****@*****.**', 'owner': 'owner_value', 'alert_subject': 'Test alert for {0}, owned by {1}', 'alert_subject_args': ['test_term', 'owner'], 'snowman': '☃' } alert = EmailAlerter(rule) expected_data = { 'type': 'email', 'recipients': ['*****@*****.**', '*****@*****.**'] } actual_data = alert.get_info() assert expected_data == actual_data
def test_email(caplog): caplog.set_level(logging.INFO) rule = { 'name': 'test alert', 'email': ['*****@*****.**', '*****@*****.**'], 'from_addr': '*****@*****.**', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_reply_to': '*****@*****.**', 'owner': 'owner_value', 'alert_subject': 'Test alert for {0}, owned by {1}', 'alert_subject_args': ['test_term', 'owner'], 'snowman': '☃' } with mock.patch('elastalert.alerters.email.SMTP') as mock_smtp: mock_smtp.return_value = mock.Mock() alert = EmailAlerter(rule) alert.alert([{'test_term': 'test_value'}]) expected = [ mock.call('localhost'), mock.call().ehlo(), mock.call().has_extn('STARTTLS'), mock.call().starttls(certfile=None, keyfile=None), mock.call().sendmail(mock.ANY, ['*****@*****.**', '*****@*****.**'], mock.ANY), mock.call().quit() ] assert mock_smtp.mock_calls == expected body = mock_smtp.mock_calls[4][1][2] assert 'Reply-To: [email protected]' in body assert 'To: [email protected]' in body assert 'From: [email protected]' in body assert 'Subject: Test alert for test_value, owned by owner_value' in body assert ('elastalert', logging.INFO, "Sent email to ['*****@*****.**', '*****@*****.**']" ) == caplog.record_tuples[0]
def test_opsgenie_details_with_environment_variable_replacement(environ): environ.update({ 'TEST_VAR': 'Bar' }) rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), 'opsgenie_account': 'genies', 'opsgenie_key': 'ogkey', 'opsgenie_details': {'Foo': '$TEST_VAR'} } match = { '@timestamp': '2014-10-31T00:00:00' } alert = OpsGenieAlerter(rule) with mock.patch('requests.post') as mock_post_request: alert.alert([match]) mock_post_request.assert_called_once_with( 'https://api.opsgenie.com/v2/alerts', headers={ 'Content-Type': 'application/json', 'Authorization': 'GenieKey ogkey' }, json=mock.ANY, proxies=None ) expected_json = { 'description': BasicMatchString(rule, match).__str__(), 'details': {'Foo': 'Bar'}, 'message': 'ElastAlert: Opsgenie Details', 'priority': None, 'source': 'ElastAlert', 'tags': ['ElastAlert', 'Opsgenie Details'], 'user': '******' } actual_json = mock_post_request.call_args_list[0][1]['json'] assert expected_json == actual_json
def test_email_smtp_exception(): with pytest.raises(EAException) as ea: rule = { 'name': 'test alert', 'email': ['*****@*****.**', '*****@*****.**'], 'from_addr': '*****@*****.**', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_reply_to': '*****@*****.**', 'alert_subject': 'Test alert for {0}', 'alert_subject_args': ['test_term'], 'smtp_auth_file': 'file.txt', 'rule_file': '/tmp/foo.yaml' } with mock.patch('elastalert.alerters.email.SMTP_SSL'): with mock.patch('elastalert.alerts.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } alert = EmailAlerter(rule) alert.alert([{'test_term': 'test_value'}]) assert 'Error connecting to SMTP host: ' in str(ea)
def test_email_key_error(email, expected_data): try: rule = { 'name': 'test alert', 'from_addr': '*****@*****.**', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_reply_to': '*****@*****.**', 'owner': 'owner_value', 'alert_subject': 'Test alert for {0}, owned by {1}', 'alert_subject_args': ['test_term', 'owner'], 'snowman': '☃' } if email: rule['email'] = email alert = EmailAlerter(rule) actual_data = alert.get_info() assert expected_data == actual_data except Exception: assert expected_data
def test_email_format_html(): rule = { 'name': 'test alert', 'email': ['*****@*****.**', '*****@*****.**'], 'smtp_ssl': True, 'smtp_port': 455, 'email_format': 'html', 'from_addr': '*****@*****.**', 'type': mock_rule(), 'timestamp_field': '@timestamp', 'email_reply_to': '*****@*****.**', 'owner': 'owner_value', 'alert_subject': 'Test alert for {0}, owned by {1}', 'alert_subject_args': ['test_term', 'owner'], 'snowman': '☃' } with mock.patch('elastalert.alerters.email.SMTP_SSL') as mock_smtp: mock_smtp.return_value = mock.Mock() alert = EmailAlerter(rule) alert.alert([{'test_term': 'test_value'}]) expected = [ mock.call('localhost', 455, certfile=None, keyfile=None), mock.call().sendmail(mock.ANY, ['*****@*****.**', '*****@*****.**'], mock.ANY), mock.call().quit() ] assert mock_smtp.mock_calls == expected body = mock_smtp.mock_calls[1][1][2] assert 'Reply-To: [email protected]' in body assert 'To: [email protected]' in body assert 'From: [email protected]' in body assert 'Subject: Test alert for test_value, owned by owner_value' in body assert 'Content-Type: text/html; charset="utf-8"' in body
def test_opsgenie_get_details2(): rule = { 'name': 'Opsgenie Details', 'type': mock_rule(), 'opsgenie_account': 'genies', 'opsgenie_key': 'ogkey', 'Testing': 'abc', 'opsgenie_subject': '{} {} {}', 'opsgenie_subject_args': ['Testing', 'message', '@timestamp'] } match = [ { 'message': 'Testing', '@timestamp': '2014-10-31T00:00:00' }, { 'message': 'Testing', '@timestamp': '2014-10-31T00:00:00' } ] alert = OpsGenieAlerter(rule) actual = alert.get_details(match) excepted = {} assert excepted == actual
def test_msend_service_slotsetvalues_dict_variable_subst_oldstrformat(): rule = { 'type': mock_rule(), 'alert_subject': 'Alert Subject', 'alert_text': 'Alert Text', 'msend_service_url' : 'http://www.example.com/', 'msend_cell_name': 'somecellname', 'msend_event_class': 'EVENT', 'msend_event_severity': 'WARNING', 'msend_slotsetvalues' : { 'mc_host': 'testhost', 'mc_tool': 'Elasticsearch', 'mc_long_msg': '%(somefield)s', } } match = {'@timestamp': '2014-01-01T00:00:00', 'somefield': 'foobarbaz', 'nested': {'field': 1}} alert = MSendServiceAlerter(rule) expected_headers = {'Content-Type': 'application/json', 'Accept': 'application/json;charset=utf-8'} expected_data = '{"msend_event_severity": "WARNING", "msend_event_slotvalues": {"mc_long_msg": "foobarbaz", "mc_host": "testhost", "mc_tool": "Elasticsearch"}, "msend_cell_name": "somecellname", "msend_event_message": "Alert Subject", "msend_event_class": "EVENT"}' with mock.patch("elastalert_modules.cx_alerters.requests.post") as mock_post: alert.alert([match]) mock_post.assert_called_with('http://www.example.com/', headers = expected_headers, data = expected_data)
def test_jira(caplog): caplog.set_level(logging.INFO) description_txt = "Description stuff goes here like a runbook link." rule = { 'name': 'test alert', 'jira_account_file': 'jirafile', 'type': mock_rule(), 'jira_project': 'testproject', 'jira_priority': 0, 'jira_issuetype': 'testtype', 'jira_server': 'jiraserver', 'jira_label': 'testlabel', 'jira_component': 'testcomponent', 'jira_description': description_txt, 'jira_watchers': ['testwatcher1', 'testwatcher2'], 'timestamp_field': '@timestamp', 'alert_subject': 'Issue {0} occurred at {1}', 'alert_subject_args': ['test_term', '@timestamp'], 'rule_file': '/tmp/foo.yaml' } mock_priority = mock.Mock(id='5') with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = [] alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) expected = [ mock.call('jiraserver', basic_auth=('jirauser', 'jirapassword')), mock.call().priorities(), mock.call().fields(), mock.call().create_issue( issuetype={'name': 'testtype'}, priority={'id': '5'}, project={'key': 'testproject'}, labels=['testlabel'], components=[{ 'name': 'testcomponent' }], description=mock.ANY, summary='Issue test_value occurred at 2014-10-31T00:00:00', ), mock.call().add_watcher(mock.ANY, 'testwatcher1'), mock.call().add_watcher(mock.ANY, 'testwatcher2'), ] # We don't care about additional calls to mock_jira, such as __str__ assert mock_jira.mock_calls[:6] == expected assert mock_jira.mock_calls[3][2]['description'].startswith( description_txt) user, level, message = caplog.record_tuples[0] assert 'elastalert' == user assert logging.INFO == level assert 'pened Jira ticket: ' in message # Search called if jira_bump_tickets rule['jira_bump_tickets'] = True with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value = mock.Mock() mock_jira.return_value.search_issues.return_value = [] mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = [] alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) expected.insert(3, mock.call().search_issues(mock.ANY)) assert mock_jira.mock_calls == expected # Remove a field if jira_ignore_in_title set rule['jira_ignore_in_title'] = 'test_term' with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value = mock.Mock() mock_jira.return_value.search_issues.return_value = [] mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = [] alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) assert 'test_value' not in mock_jira.mock_calls[3][1][0] # Issue is still created if search_issues throws an exception with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value = mock.Mock() mock_jira.return_value.search_issues.side_effect = JIRAError mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = [] alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) assert mock_jira.mock_calls == expected user, level, message = caplog.record_tuples[3] assert 'elastalert' in user assert logging.ERROR == level assert 'Error while searching for Jira ticket using jql' in message # Only bump after 3d of inactivity rule['jira_bump_after_inactivity'] = 3 mock_issue = mock.Mock() # Check ticket is bumped if it is updated 4 days ago mock_issue.fields.updated = str(ts_now() - datetime.timedelta(days=4)) with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value = mock.Mock() mock_jira.return_value.search_issues.return_value = [mock_issue] mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = [] alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) # Check add_comment is called assert len(mock_jira.mock_calls) == 5 assert '().add_comment' == mock_jira.mock_calls[4][0] # Check ticket is bumped is not bumped if ticket is updated right now mock_issue.fields.updated = str(ts_now()) with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value = mock.Mock() mock_jira.return_value.search_issues.return_value = [mock_issue] mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = [] alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) # Only 4 calls for mock_jira since add_comment is not called assert len(mock_jira.mock_calls) == 4 # Test match resolved values rule = { 'name': 'test alert', 'jira_account_file': 'jirafile', 'type': mock_rule(), 'owner': 'the_owner', 'jira_project': 'testproject', 'jira_issuetype': 'testtype', 'jira_server': 'jiraserver', 'jira_label': 'testlabel', 'jira_component': 'testcomponent', 'jira_description': "DESC", 'jira_watchers': ['testwatcher1', 'testwatcher2'], 'timestamp_field': '@timestamp', 'jira_affected_user': "******", 'rule_file': '/tmp/foo.yaml' } mock_issue = mock.Mock() mock_issue.fields.updated = str(ts_now() - datetime.timedelta(days=4)) mock_fields = [{ 'name': 'affected user', 'id': 'affected_user_id', 'schema': { 'type': 'string' } }] with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value = mock.Mock() mock_jira.return_value.search_issues.return_value = [mock_issue] mock_jira.return_value.fields.return_value = mock_fields mock_jira.return_value.priorities.return_value = [mock_priority] alert = JiraAlerter(rule) alert.alert([{ 'gmail.the_user': '******', '@timestamp': '2014-10-31T00:00:00' }]) assert mock_jira.mock_calls[4][2]['affected_user_id'] == "jdoe"
def test_jira_arbitrary_field_support(): description_txt = "Description stuff goes here like a runbook link." rule = { 'name': 'test alert', 'jira_account_file': 'jirafile', 'type': mock_rule(), 'owner': 'the_owner', 'jira_project': 'testproject', 'jira_issuetype': 'testtype', 'jira_server': 'jiraserver', 'jira_label': 'testlabel', 'jira_component': 'testcomponent', 'jira_description': description_txt, 'jira_watchers': ['testwatcher1', 'testwatcher2'], 'jira_arbitrary_reference_string_field': '$owner$', 'jira_arbitrary_string_field': 'arbitrary_string_value', 'jira_arbitrary_string_array_field': ['arbitrary_string_value1', 'arbitrary_string_value2'], 'jira_arbitrary_string_array_field_provided_as_single_value': 'arbitrary_string_value_in_array_field', 'jira_arbitrary_number_field': 1, 'jira_arbitrary_number_array_field': [2, 3], 'jira_arbitrary_number_array_field_provided_as_single_value': 1, 'jira_arbitrary_complex_field': 'arbitrary_complex_value', 'jira_arbitrary_complex_array_field': ['arbitrary_complex_value1', 'arbitrary_complex_value2'], 'jira_arbitrary_complex_array_field_provided_as_single_value': 'arbitrary_complex_value_in_array_field', 'timestamp_field': '@timestamp', 'alert_subject': 'Issue {0} occurred at {1}', 'alert_subject_args': ['test_term', '@timestamp'], 'rule_file': '/tmp/foo.yaml' } mock_priority = mock.MagicMock(id='5') mock_fields = [ { 'name': 'arbitrary reference string field', 'id': 'arbitrary_reference_string_field', 'schema': { 'type': 'string' } }, { 'name': 'arbitrary string field', 'id': 'arbitrary_string_field', 'schema': { 'type': 'string' } }, { 'name': 'arbitrary string array field', 'id': 'arbitrary_string_array_field', 'schema': { 'type': 'array', 'items': 'string' } }, { 'name': 'arbitrary string array field provided as single value', 'id': 'arbitrary_string_array_field_provided_as_single_value', 'schema': { 'type': 'array', 'items': 'string' } }, { 'name': 'arbitrary number field', 'id': 'arbitrary_number_field', 'schema': { 'type': 'number' } }, { 'name': 'arbitrary number array field', 'id': 'arbitrary_number_array_field', 'schema': { 'type': 'array', 'items': 'number' } }, { 'name': 'arbitrary number array field provided as single value', 'id': 'arbitrary_number_array_field_provided_as_single_value', 'schema': { 'type': 'array', 'items': 'number' } }, { 'name': 'arbitrary complex field', 'id': 'arbitrary_complex_field', 'schema': { 'type': 'ArbitraryType' } }, { 'name': 'arbitrary complex array field', 'id': 'arbitrary_complex_array_field', 'schema': { 'type': 'array', 'items': 'ArbitraryType' } }, { 'name': 'arbitrary complex array field provided as single value', 'id': 'arbitrary_complex_array_field_provided_as_single_value', 'schema': { 'type': 'array', 'items': 'ArbitraryType' } }, ] with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = mock_fields alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) expected = [ mock.call('jiraserver', basic_auth=('jirauser', 'jirapassword')), mock.call().priorities(), mock.call().fields(), mock.call().create_issue( issuetype={'name': 'testtype'}, project={'key': 'testproject'}, labels=['testlabel'], components=[{ 'name': 'testcomponent' }], description=mock.ANY, summary='Issue test_value occurred at 2014-10-31T00:00:00', arbitrary_reference_string_field='the_owner', arbitrary_string_field='arbitrary_string_value', arbitrary_string_array_field=[ 'arbitrary_string_value1', 'arbitrary_string_value2' ], arbitrary_string_array_field_provided_as_single_value=[ 'arbitrary_string_value_in_array_field' ], arbitrary_number_field=1, arbitrary_number_array_field=[2, 3], arbitrary_number_array_field_provided_as_single_value=[1], arbitrary_complex_field={'name': 'arbitrary_complex_value'}, arbitrary_complex_array_field=[{ 'name': 'arbitrary_complex_value1' }, { 'name': 'arbitrary_complex_value2' }], arbitrary_complex_array_field_provided_as_single_value=[{ 'name': 'arbitrary_complex_value_in_array_field' }], ), mock.call().add_watcher(mock.ANY, 'testwatcher1'), mock.call().add_watcher(mock.ANY, 'testwatcher2'), ] # We don't care about additional calls to mock_jira, such as __str__ assert mock_jira.mock_calls[:6] == expected assert mock_jira.mock_calls[3][2]['description'].startswith( description_txt) # Reference an arbitrary string field that is not defined on the Jira server rule['jira_nonexistent_field'] = 'nonexistent field value' with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = mock_fields with pytest.raises(Exception) as exception: alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) assert "Could not find a definition for the jira field 'nonexistent field'" in str( exception) del rule['jira_nonexistent_field'] # Reference a watcher that does not exist rule['jira_watchers'] = 'invalid_watcher' with mock.patch('elastalert.alerters.jira.JIRA') as mock_jira, \ mock.patch('elastalert.alerters.jira.read_yaml') as mock_open: mock_open.return_value = { 'user': '******', 'password': '******' } mock_jira.return_value.priorities.return_value = [mock_priority] mock_jira.return_value.fields.return_value = mock_fields # Cause add_watcher to raise, which most likely means that the user did not exist mock_jira.return_value.add_watcher.side_effect = Exception() with pytest.raises(Exception) as exception: alert = JiraAlerter(rule) alert.alert([{ 'test_term': 'test_value', '@timestamp': '2014-10-31T00:00:00' }]) assert "Exception encountered when trying to add 'invalid_watcher' as a watcher. Does the user exist?" in str( exception)