elastalert2 2.29.0__tar.gz → 2.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {elastalert2-2.29.0/elastalert2.egg-info → elastalert2-2.30.0}/PKG-INFO +13 -13
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/datadog.py +2 -4
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerts.py +24 -9
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/elastalert.py +2 -2
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/loaders.py +0 -2
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/schema.yaml +12 -28
- {elastalert2-2.29.0 → elastalert2-2.30.0/elastalert2.egg-info}/PKG-INFO +13 -13
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert2.egg-info/SOURCES.txt +0 -2
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert2.egg-info/requires.txt +12 -12
- {elastalert2-2.29.0 → elastalert2-2.30.0}/setup.py +13 -13
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/datadog_test.py +67 -0
- elastalert2-2.29.0/elastalert/alerters/teams.py +0 -120
- elastalert2-2.29.0/tests/alerters/teams_test.py +0 -650
- {elastalert2-2.29.0 → elastalert2-2.30.0}/LICENSE +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/README.md +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/__init__.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/__init__.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/alerta.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/alertmanager.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/chatwork.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/command.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/debug.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/dingtalk.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/discord.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/email.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/exotel.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/flashduty.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/gelf.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/gitter.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/googlechat.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/httppost.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/httppost2.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/indexer.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/iris.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/jira.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/lark.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/line.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/matrixhookshot.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/mattermost.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/opsgenie.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/pagerduty.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/pagertree.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/powerautomate.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/rocketchat.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/servicenow.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/ses.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/slack.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/smseagle.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/sns.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/sqs.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/stomp.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/telegram.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/tencentsms.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/thehive.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/twilio.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/victorops.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/webex_webhook.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/workwechat.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/yzj.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/alerters/zabbix.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/auth.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/config.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/create_index.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/enhancements.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/eql.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/7/elastalert.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/7/elastalert_error.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/7/elastalert_status.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/7/past_elastalert.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/7/silence.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/8/elastalert.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/8/elastalert_error.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/8/elastalert_status.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/8/past_elastalert.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/es_mappings/8/silence.json +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/kibana_discover.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/kibana_external_url_formatter.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/opensearch_discover.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/opensearch_external_url_formatter.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/prometheus_wrapper.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/ruletypes.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/test_rule.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/util.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert/yaml.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert2.egg-info/dependency_links.txt +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert2.egg-info/entry_points.txt +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/elastalert2.egg-info/top_level.txt +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/setup.cfg +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/__init__.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/alerta_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/alertmanager_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/chatwork_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/command_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/debug_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/dingtalk_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/discord_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/email_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/exotel_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/flashduty_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/gelf_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/gitter_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/googlechat_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/httppost2_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/httppost_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/indexer_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/iris_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/jira_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/lark_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/line_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/matrixhookshot_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/mattermost_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/opsgenie_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/pagerduty_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/pagertree_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/powerautomate_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/rocketchat_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/servicenow_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/ses_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/slack_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/smseagle_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/sns_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/sqs_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/stomp_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/telegram_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/tencentsms_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/thehive_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/twilio_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/victorops_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/webex_webhook_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/workwechat_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/yzj_test.py +0 -0
- {elastalert2-2.29.0 → elastalert2-2.30.0}/tests/alerters/zabbix_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: elastalert2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.30.0
|
|
4
4
|
Summary: Automated rule-based alerting for Elasticsearch
|
|
5
5
|
Home-page: https://github.com/jertel/elastalert2
|
|
6
6
|
License: Apache 2.0
|
|
@@ -15,32 +15,32 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.12
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: apscheduler<4.0,>=3.11.
|
|
18
|
+
Requires-Dist: apscheduler<4.0,>=3.11.2
|
|
19
19
|
Requires-Dist: aws-requests-auth>=0.4.3
|
|
20
|
-
Requires-Dist: boto3>=1.
|
|
20
|
+
Requires-Dist: boto3>=1.42.89
|
|
21
21
|
Requires-Dist: cffi>=2.0.0
|
|
22
|
-
Requires-Dist: croniter>=6.
|
|
22
|
+
Requires-Dist: croniter>=6.2.2
|
|
23
23
|
Requires-Dist: elasticsearch==7.10.1
|
|
24
24
|
Requires-Dist: envparse>=0.2.0
|
|
25
25
|
Requires-Dist: exotel==0.1.5
|
|
26
26
|
Requires-Dist: Jinja2>=3.1.6
|
|
27
27
|
Requires-Dist: jira>=3.10.5
|
|
28
|
-
Requires-Dist: jsonpointer>=3.
|
|
29
|
-
Requires-Dist: jsonschema>=4.
|
|
30
|
-
Requires-Dist: prettytable>=3.
|
|
28
|
+
Requires-Dist: jsonpointer>=3.1.1
|
|
29
|
+
Requires-Dist: jsonschema>=4.26.0
|
|
30
|
+
Requires-Dist: prettytable>=3.17.0
|
|
31
31
|
Requires-Dist: prison>=0.2.1
|
|
32
|
-
Requires-Dist:
|
|
32
|
+
Requires-Dist: prometheus-client>=0.25.0
|
|
33
33
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
34
|
-
Requires-Dist: pytz>=2026.1
|
|
34
|
+
Requires-Dist: pytz>=2026.1.post1
|
|
35
35
|
Requires-Dist: PyYAML>=6.0.3
|
|
36
|
-
Requires-Dist: zabbix-utils>=2.0.
|
|
37
|
-
Requires-Dist: requests>=2.
|
|
36
|
+
Requires-Dist: zabbix-utils>=2.0.4
|
|
37
|
+
Requires-Dist: requests>=2.33.1
|
|
38
38
|
Requires-Dist: sortedcontainers>=2.4.0
|
|
39
39
|
Requires-Dist: statsd-tags==3.2.1.post1
|
|
40
40
|
Requires-Dist: stomp.py>=8.2.0
|
|
41
|
-
Requires-Dist: tencentcloud-sdk-python>=3.
|
|
41
|
+
Requires-Dist: tencentcloud-sdk-python>=3.1.79
|
|
42
42
|
Requires-Dist: texttable>=1.7.0
|
|
43
|
-
Requires-Dist: twilio>=9.
|
|
43
|
+
Requires-Dist: twilio>=9.10.5
|
|
44
44
|
Dynamic: classifier
|
|
45
45
|
Dynamic: description
|
|
46
46
|
Dynamic: description-content-type
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
|
-
|
|
3
2
|
import requests
|
|
4
3
|
from requests import RequestException
|
|
5
|
-
|
|
6
4
|
from elastalert.alerts import Alerter, DateTimeEncoder
|
|
7
5
|
from elastalert.util import EAException, elastalert_logger
|
|
8
6
|
|
|
@@ -15,9 +13,9 @@ class DatadogAlerter(Alerter):
|
|
|
15
13
|
super(DatadogAlerter, self).__init__(rule)
|
|
16
14
|
self.dd_api_key = self.rule.get('datadog_api_key', None)
|
|
17
15
|
self.dd_app_key = self.rule.get('datadog_app_key', None)
|
|
16
|
+
self.dd_api_url = self.rule.get('datadog_api_url', 'https://api.datadoghq.com/api/v1/events')
|
|
18
17
|
|
|
19
18
|
def alert(self, matches):
|
|
20
|
-
url = 'https://api.datadoghq.com/api/v1/events'
|
|
21
19
|
headers = {
|
|
22
20
|
'Content-Type': 'application/json',
|
|
23
21
|
'DD-API-KEY': self.dd_api_key,
|
|
@@ -28,7 +26,7 @@ class DatadogAlerter(Alerter):
|
|
|
28
26
|
'text': self.create_alert_body(matches)
|
|
29
27
|
}
|
|
30
28
|
try:
|
|
31
|
-
response = requests.post(
|
|
29
|
+
response = requests.post(self.dd_api_url, data=json.dumps(payload, cls=DateTimeEncoder), headers=headers)
|
|
32
30
|
response.raise_for_status()
|
|
33
31
|
except RequestException as e:
|
|
34
32
|
raise EAException('Error posting event to Datadog: %s' % e)
|
|
@@ -256,10 +256,25 @@ class Alerter(object):
|
|
|
256
256
|
if not isinstance(summary_table_fields, list):
|
|
257
257
|
summary_table_fields = [summary_table_fields]
|
|
258
258
|
|
|
259
|
+
# Each entry is either a string field path, or a dict with required 'path' and optional 'header'.
|
|
260
|
+
# Split into parallel lists so paths drive lookups/logging while headers drive rendered column names.
|
|
261
|
+
summary_table_paths = []
|
|
262
|
+
summary_table_headers = []
|
|
263
|
+
for entry in summary_table_fields:
|
|
264
|
+
if isinstance(entry, dict):
|
|
265
|
+
path = entry['path']
|
|
266
|
+
header = entry.get('header', path)
|
|
267
|
+
else:
|
|
268
|
+
path = entry
|
|
269
|
+
header = entry
|
|
270
|
+
summary_table_paths.append(path)
|
|
271
|
+
summary_table_headers.append(header)
|
|
272
|
+
|
|
259
273
|
# Include a count aggregation so that we can see at a glance how many of each aggregation_key were encountered
|
|
260
|
-
|
|
274
|
+
summary_table_paths_with_count = summary_table_paths + ['count']
|
|
275
|
+
summary_table_headers_with_count = summary_table_headers + ['count']
|
|
261
276
|
text += "Aggregation resulted in the following data for summary_table_fields ==> {0}:\n\n".format(
|
|
262
|
-
|
|
277
|
+
summary_table_paths_with_count
|
|
263
278
|
)
|
|
264
279
|
|
|
265
280
|
# Prepare match_aggregation used in both table types
|
|
@@ -267,7 +282,7 @@ class Alerter(object):
|
|
|
267
282
|
|
|
268
283
|
# Maintain an aggregate count for each unique key encountered in the aggregation period
|
|
269
284
|
for match in matches:
|
|
270
|
-
key_tuple = tuple([str(lookup_es_key(match, key)) for key in
|
|
285
|
+
key_tuple = tuple([str(lookup_es_key(match, key)) for key in summary_table_paths])
|
|
271
286
|
if key_tuple not in match_aggregation:
|
|
272
287
|
match_aggregation[key_tuple] = 1
|
|
273
288
|
else:
|
|
@@ -281,9 +296,9 @@ class Alerter(object):
|
|
|
281
296
|
# Type dependent table style
|
|
282
297
|
if summary_table_type == 'ascii':
|
|
283
298
|
text_table = Texttable(max_width=self.get_aggregation_summary_text__maximum_width())
|
|
284
|
-
text_table.header(
|
|
299
|
+
text_table.header(summary_table_headers_with_count)
|
|
285
300
|
# Format all fields as 'text' to avoid long numbers being shown as scientific notation
|
|
286
|
-
text_table.set_cols_dtype(['t' for i in
|
|
301
|
+
text_table.set_cols_dtype(['t' for i in summary_table_headers_with_count])
|
|
287
302
|
|
|
288
303
|
for keys, count in match_aggregation.items():
|
|
289
304
|
text_table.add_row([key for key in keys] + [count])
|
|
@@ -292,9 +307,9 @@ class Alerter(object):
|
|
|
292
307
|
elif summary_table_type == 'markdown':
|
|
293
308
|
# Adapted from https://github.com/codazoda/tomark/blob/master/tomark/tomark.py
|
|
294
309
|
# Create table header
|
|
295
|
-
text += '| ' + ' | '.join(map(str,
|
|
310
|
+
text += '| ' + ' | '.join(map(str, summary_table_headers_with_count)) + ' |\n'
|
|
296
311
|
# Create header separator
|
|
297
|
-
text += '|-----' * len(
|
|
312
|
+
text += '|-----' * len(summary_table_headers_with_count) + '|\n'
|
|
298
313
|
# Create table row
|
|
299
314
|
for keys, count in match_aggregation.items():
|
|
300
315
|
markdown_row = ""
|
|
@@ -302,12 +317,12 @@ class Alerter(object):
|
|
|
302
317
|
markdown_row += '| ' + str(key) + ' '
|
|
303
318
|
text += markdown_row + '| ' + str(count) + ' |\n'
|
|
304
319
|
text += '\n'
|
|
305
|
-
|
|
320
|
+
|
|
306
321
|
elif summary_table_type == 'html':
|
|
307
322
|
# Portions of the following block of HTML formatting code was taken from
|
|
308
323
|
# an abandoned PR (https://github.com/jertel/elastalert2/pull/1227).
|
|
309
324
|
text_table = PrettyTable()
|
|
310
|
-
text_table.field_names =
|
|
325
|
+
text_table.field_names = summary_table_headers_with_count
|
|
311
326
|
text_table.set_style(TableStyle.MSWORD_FRIENDLY)
|
|
312
327
|
text_table.border = True
|
|
313
328
|
text_table.header = True
|
|
@@ -1606,7 +1606,7 @@ class ElastAlerter(object):
|
|
|
1606
1606
|
|
|
1607
1607
|
# Delete it from the index
|
|
1608
1608
|
try:
|
|
1609
|
-
self.writeback_es.
|
|
1609
|
+
self.writeback_es.delete_by_query(index=self.writeback_index, body={'query': {'term': {'_id': _id}}})
|
|
1610
1610
|
except ElasticsearchException: # TODO: Give this a more relevant exception, try:except: is evil.
|
|
1611
1611
|
self.handle_error("Failed to delete alert %s at %s" % (_id, alert_time))
|
|
1612
1612
|
|
|
@@ -1640,7 +1640,7 @@ class ElastAlerter(object):
|
|
|
1640
1640
|
size=self.max_aggregation)
|
|
1641
1641
|
for match in res['hits']['hits']:
|
|
1642
1642
|
matches.append(match['_source'])
|
|
1643
|
-
self.writeback_es.
|
|
1643
|
+
self.writeback_es.delete_by_query(index=self.writeback_index, body={'query': {'term': {'_id': match['_id']}}})
|
|
1644
1644
|
except (KeyError, ElasticsearchException) as e:
|
|
1645
1645
|
self.handle_error("Error fetching aggregated matches: %s" % (e), {'id': _id})
|
|
1646
1646
|
return matches
|
|
@@ -53,7 +53,6 @@ from elastalert.alerters.slack import SlackAlerter
|
|
|
53
53
|
from elastalert.alerters.smseagle import SMSEagleAlerter
|
|
54
54
|
from elastalert.alerters.sns import SnsAlerter
|
|
55
55
|
from elastalert.alerters.sqs import SqsAlerter
|
|
56
|
-
from elastalert.alerters.teams import MsTeamsAlerter
|
|
57
56
|
from elastalert.alerters.powerautomate import MsPowerAutomateAlerter
|
|
58
57
|
from elastalert.alerters.yzj import YzjAlerter
|
|
59
58
|
from elastalert.alerters.zabbix import ZabbixAlerter
|
|
@@ -117,7 +116,6 @@ class RulesLoader(object):
|
|
|
117
116
|
'command': elastalert.alerters.command.CommandAlerter,
|
|
118
117
|
'sns': SnsAlerter,
|
|
119
118
|
'sqs': SqsAlerter,
|
|
120
|
-
'ms_teams': MsTeamsAlerter,
|
|
121
119
|
'ms_power_automate': MsPowerAutomateAlerter,
|
|
122
120
|
'slack': SlackAlerter,
|
|
123
121
|
'mattermost': MattermostAlerter,
|
|
@@ -51,13 +51,6 @@ definitions:
|
|
|
51
51
|
type: array
|
|
52
52
|
items: *slackField
|
|
53
53
|
|
|
54
|
-
msTeamsFact: &msTeamsFact
|
|
55
|
-
type: object
|
|
56
|
-
additionalProperties: false
|
|
57
|
-
properties:
|
|
58
|
-
name: {type: string}
|
|
59
|
-
value: {type: string}
|
|
60
|
-
|
|
61
54
|
msPowerAutomateFact: &msPowerAutomateFact
|
|
62
55
|
type: object
|
|
63
56
|
additionalProperties: false
|
|
@@ -65,10 +58,6 @@ definitions:
|
|
|
65
58
|
name: {type: string}
|
|
66
59
|
value: {type: string}
|
|
67
60
|
|
|
68
|
-
arrayOfMsTeamsFacts: &arrayOfMsTeamsFacts
|
|
69
|
-
type: array
|
|
70
|
-
items: *msTeamsFact
|
|
71
|
-
|
|
72
61
|
arrayOfMsPowerAutomateFacts: &arrayOfMsPowerAutomateFacts
|
|
73
62
|
type: array
|
|
74
63
|
items: *msPowerAutomateFact
|
|
@@ -319,7 +308,17 @@ properties:
|
|
|
319
308
|
notify_all_errors: {type: boolean}
|
|
320
309
|
|
|
321
310
|
### summary table
|
|
322
|
-
summary_table_fields:
|
|
311
|
+
summary_table_fields:
|
|
312
|
+
type: array
|
|
313
|
+
items:
|
|
314
|
+
oneOf:
|
|
315
|
+
- type: string
|
|
316
|
+
- type: object
|
|
317
|
+
additionalProperties: false
|
|
318
|
+
required: [path]
|
|
319
|
+
properties:
|
|
320
|
+
path: {type: string, minLength: 1}
|
|
321
|
+
header: {type: string}
|
|
323
322
|
summary_table_type: {type: string, enum: ['ascii', 'html', 'markdown']}
|
|
324
323
|
summary_table_max_rows: {type: integer, minimum: 0}
|
|
325
324
|
summary_prefix: {type: string}
|
|
@@ -468,6 +467,7 @@ properties:
|
|
|
468
467
|
### Datadog
|
|
469
468
|
datadog_api_key: {type: string}
|
|
470
469
|
datadog_app_key: {type: string}
|
|
470
|
+
datadog_api_url: {type: string}
|
|
471
471
|
|
|
472
472
|
### Dingtalk
|
|
473
473
|
dingtalk_access_token: {type: string}
|
|
@@ -710,22 +710,6 @@ properties:
|
|
|
710
710
|
mattermost_opensearch_discover_color: {type: string}
|
|
711
711
|
mattermost_opensearch_discover_title: {type: string}
|
|
712
712
|
|
|
713
|
-
|
|
714
|
-
### Microsoft Teams
|
|
715
|
-
ms_teams_webhook_url: *arrayOfString
|
|
716
|
-
ms_teams_alert_summary: {type: string}
|
|
717
|
-
ms_teams_theme_color: {type: string}
|
|
718
|
-
ms_teams_proxy: {type: string}
|
|
719
|
-
ms_teams_alert_fixed_width: {type: boolean}
|
|
720
|
-
ms_teams_alert_facts: *arrayOfMsTeamsFacts
|
|
721
|
-
ms_teams_attach_kibana_discover_url: {type: boolean}
|
|
722
|
-
ms_teams_kibana_discover_title: {type: string}
|
|
723
|
-
ms_teams_ca_certs: {type: [boolean, string]}
|
|
724
|
-
ms_teams_ignore_ssl_errors: {type: boolean}
|
|
725
|
-
ms_teams_attach_opensearch_discover_url: {type: boolean}
|
|
726
|
-
ms_teams_opensearch_discover_title: {type: string}
|
|
727
|
-
|
|
728
|
-
|
|
729
713
|
### Microsoft Power Automate
|
|
730
714
|
ms_power_automate_webhook_url: *arrayOfString
|
|
731
715
|
ms_power_automate_webhook_url_from_field: { type: string }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: elastalert2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.30.0
|
|
4
4
|
Summary: Automated rule-based alerting for Elasticsearch
|
|
5
5
|
Home-page: https://github.com/jertel/elastalert2
|
|
6
6
|
License: Apache 2.0
|
|
@@ -15,32 +15,32 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.12
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: apscheduler<4.0,>=3.11.
|
|
18
|
+
Requires-Dist: apscheduler<4.0,>=3.11.2
|
|
19
19
|
Requires-Dist: aws-requests-auth>=0.4.3
|
|
20
|
-
Requires-Dist: boto3>=1.
|
|
20
|
+
Requires-Dist: boto3>=1.42.89
|
|
21
21
|
Requires-Dist: cffi>=2.0.0
|
|
22
|
-
Requires-Dist: croniter>=6.
|
|
22
|
+
Requires-Dist: croniter>=6.2.2
|
|
23
23
|
Requires-Dist: elasticsearch==7.10.1
|
|
24
24
|
Requires-Dist: envparse>=0.2.0
|
|
25
25
|
Requires-Dist: exotel==0.1.5
|
|
26
26
|
Requires-Dist: Jinja2>=3.1.6
|
|
27
27
|
Requires-Dist: jira>=3.10.5
|
|
28
|
-
Requires-Dist: jsonpointer>=3.
|
|
29
|
-
Requires-Dist: jsonschema>=4.
|
|
30
|
-
Requires-Dist: prettytable>=3.
|
|
28
|
+
Requires-Dist: jsonpointer>=3.1.1
|
|
29
|
+
Requires-Dist: jsonschema>=4.26.0
|
|
30
|
+
Requires-Dist: prettytable>=3.17.0
|
|
31
31
|
Requires-Dist: prison>=0.2.1
|
|
32
|
-
Requires-Dist:
|
|
32
|
+
Requires-Dist: prometheus-client>=0.25.0
|
|
33
33
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
34
|
-
Requires-Dist: pytz>=2026.1
|
|
34
|
+
Requires-Dist: pytz>=2026.1.post1
|
|
35
35
|
Requires-Dist: PyYAML>=6.0.3
|
|
36
|
-
Requires-Dist: zabbix-utils>=2.0.
|
|
37
|
-
Requires-Dist: requests>=2.
|
|
36
|
+
Requires-Dist: zabbix-utils>=2.0.4
|
|
37
|
+
Requires-Dist: requests>=2.33.1
|
|
38
38
|
Requires-Dist: sortedcontainers>=2.4.0
|
|
39
39
|
Requires-Dist: statsd-tags==3.2.1.post1
|
|
40
40
|
Requires-Dist: stomp.py>=8.2.0
|
|
41
|
-
Requires-Dist: tencentcloud-sdk-python>=3.
|
|
41
|
+
Requires-Dist: tencentcloud-sdk-python>=3.1.79
|
|
42
42
|
Requires-Dist: texttable>=1.7.0
|
|
43
|
-
Requires-Dist: twilio>=9.
|
|
43
|
+
Requires-Dist: twilio>=9.10.5
|
|
44
44
|
Dynamic: classifier
|
|
45
45
|
Dynamic: description
|
|
46
46
|
Dynamic: description-content-type
|
|
@@ -57,7 +57,6 @@ elastalert/alerters/smseagle.py
|
|
|
57
57
|
elastalert/alerters/sns.py
|
|
58
58
|
elastalert/alerters/sqs.py
|
|
59
59
|
elastalert/alerters/stomp.py
|
|
60
|
-
elastalert/alerters/teams.py
|
|
61
60
|
elastalert/alerters/telegram.py
|
|
62
61
|
elastalert/alerters/tencentsms.py
|
|
63
62
|
elastalert/alerters/thehive.py
|
|
@@ -119,7 +118,6 @@ tests/alerters/smseagle_test.py
|
|
|
119
118
|
tests/alerters/sns_test.py
|
|
120
119
|
tests/alerters/sqs_test.py
|
|
121
120
|
tests/alerters/stomp_test.py
|
|
122
|
-
tests/alerters/teams_test.py
|
|
123
121
|
tests/alerters/telegram_test.py
|
|
124
122
|
tests/alerters/tencentsms_test.py
|
|
125
123
|
tests/alerters/thehive_test.py
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
apscheduler<4.0,>=3.11.
|
|
1
|
+
apscheduler<4.0,>=3.11.2
|
|
2
2
|
aws-requests-auth>=0.4.3
|
|
3
|
-
boto3>=1.
|
|
3
|
+
boto3>=1.42.89
|
|
4
4
|
cffi>=2.0.0
|
|
5
|
-
croniter>=6.
|
|
5
|
+
croniter>=6.2.2
|
|
6
6
|
elasticsearch==7.10.1
|
|
7
7
|
envparse>=0.2.0
|
|
8
8
|
exotel==0.1.5
|
|
9
9
|
Jinja2>=3.1.6
|
|
10
10
|
jira>=3.10.5
|
|
11
|
-
jsonpointer>=3.
|
|
12
|
-
jsonschema>=4.
|
|
13
|
-
prettytable>=3.
|
|
11
|
+
jsonpointer>=3.1.1
|
|
12
|
+
jsonschema>=4.26.0
|
|
13
|
+
prettytable>=3.17.0
|
|
14
14
|
prison>=0.2.1
|
|
15
|
-
|
|
15
|
+
prometheus-client>=0.25.0
|
|
16
16
|
python-dateutil>=2.9.0.post0
|
|
17
|
-
pytz>=2026.1
|
|
17
|
+
pytz>=2026.1.post1
|
|
18
18
|
PyYAML>=6.0.3
|
|
19
|
-
zabbix-utils>=2.0.
|
|
20
|
-
requests>=2.
|
|
19
|
+
zabbix-utils>=2.0.4
|
|
20
|
+
requests>=2.33.1
|
|
21
21
|
sortedcontainers>=2.4.0
|
|
22
22
|
statsd-tags==3.2.1.post1
|
|
23
23
|
stomp.py>=8.2.0
|
|
24
|
-
tencentcloud-sdk-python>=3.
|
|
24
|
+
tencentcloud-sdk-python>=3.1.79
|
|
25
25
|
texttable>=1.7.0
|
|
26
|
-
twilio>=9.
|
|
26
|
+
twilio>=9.10.5
|
|
@@ -8,7 +8,7 @@ from setuptools import setup
|
|
|
8
8
|
base_dir = os.path.dirname(__file__)
|
|
9
9
|
setup(
|
|
10
10
|
name='elastalert2',
|
|
11
|
-
version='2.
|
|
11
|
+
version='2.30.0',
|
|
12
12
|
description='Automated rule-based alerting for Elasticsearch',
|
|
13
13
|
long_description=open('README.md').read(),
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
@@ -35,31 +35,31 @@ setup(
|
|
|
35
35
|
package_data={'elastalert': ['schema.yaml', 'es_mappings/**/*.json']},
|
|
36
36
|
python_requires='>=3.12',
|
|
37
37
|
install_requires=[
|
|
38
|
-
'apscheduler>=3.11.
|
|
38
|
+
'apscheduler>=3.11.2,<4.0',
|
|
39
39
|
'aws-requests-auth>=0.4.3',
|
|
40
|
-
'boto3>=1.
|
|
40
|
+
'boto3>=1.42.89',
|
|
41
41
|
'cffi>=2.0.0',
|
|
42
|
-
'croniter>=6.
|
|
42
|
+
'croniter>=6.2.2',
|
|
43
43
|
'elasticsearch==7.10.1',
|
|
44
44
|
'envparse>=0.2.0',
|
|
45
45
|
'exotel==0.1.5',
|
|
46
46
|
'Jinja2>=3.1.6',
|
|
47
47
|
'jira>=3.10.5',
|
|
48
|
-
'jsonpointer>=3.
|
|
49
|
-
'jsonschema>=4.
|
|
50
|
-
'prettytable>=3.
|
|
48
|
+
'jsonpointer>=3.1.1',
|
|
49
|
+
'jsonschema>=4.26.0',
|
|
50
|
+
'prettytable>=3.17.0',
|
|
51
51
|
'prison>=0.2.1',
|
|
52
|
-
'
|
|
52
|
+
'prometheus-client>=0.25.0',
|
|
53
53
|
'python-dateutil>=2.9.0.post0',
|
|
54
|
-
'pytz>=2026.1',
|
|
54
|
+
'pytz>=2026.1.post1',
|
|
55
55
|
'PyYAML>=6.0.3',
|
|
56
|
-
'zabbix-utils>=2.0.
|
|
57
|
-
'requests>=2.
|
|
56
|
+
'zabbix-utils>=2.0.4',
|
|
57
|
+
'requests>=2.33.1',
|
|
58
58
|
'sortedcontainers>=2.4.0',
|
|
59
59
|
'statsd-tags==3.2.1.post1',
|
|
60
60
|
'stomp.py>=8.2.0',
|
|
61
|
-
'tencentcloud-sdk-python>=3.
|
|
61
|
+
'tencentcloud-sdk-python>=3.1.79',
|
|
62
62
|
'texttable>=1.7.0',
|
|
63
|
-
'twilio>=9.
|
|
63
|
+
'twilio>=9.10.5',
|
|
64
64
|
]
|
|
65
65
|
)
|
|
@@ -49,6 +49,73 @@ def test_datadog_alerter(caplog):
|
|
|
49
49
|
assert ('elastalert', logging.INFO, 'Alert sent to Datadog') == caplog.record_tuples[0]
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
def test_datadog_alerter_custom_api_url(caplog):
|
|
53
|
+
"""datadog_api_url overrides the default endpoint when provided."""
|
|
54
|
+
caplog.set_level(logging.INFO)
|
|
55
|
+
rule = {
|
|
56
|
+
'name': 'Test Datadog Event Alerter',
|
|
57
|
+
'type': 'any',
|
|
58
|
+
'datadog_api_key': 'test-api-key',
|
|
59
|
+
'datadog_app_key': 'test-app-key',
|
|
60
|
+
'datadog_api_url': 'https://api.us5.datadoghq.com/api/v1/events',
|
|
61
|
+
'alert': [],
|
|
62
|
+
'alert_subject': 'Test Datadog Event Alert'
|
|
63
|
+
}
|
|
64
|
+
rules_loader = FileRulesLoader({})
|
|
65
|
+
rules_loader.load_modules(rule)
|
|
66
|
+
alert = DatadogAlerter(rule)
|
|
67
|
+
match = {
|
|
68
|
+
'@timestamp': '2021-01-01T00:00:00',
|
|
69
|
+
'name': 'datadog-test-name'
|
|
70
|
+
}
|
|
71
|
+
with mock.patch('requests.post') as mock_post_request:
|
|
72
|
+
alert.alert([match])
|
|
73
|
+
|
|
74
|
+
mock_post_request.assert_called_once_with(
|
|
75
|
+
'https://api.us5.datadoghq.com/api/v1/events',
|
|
76
|
+
data=mock.ANY,
|
|
77
|
+
headers={
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
'DD-API-KEY': rule['datadog_api_key'],
|
|
80
|
+
'DD-APPLICATION-KEY': rule['datadog_app_key']
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
assert ('elastalert', logging.INFO, 'Alert sent to Datadog') == caplog.record_tuples[0]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_datadog_alerter_default_api_url(caplog):
|
|
87
|
+
"""datadog_api_url defaults to the US1 endpoint when not provided."""
|
|
88
|
+
caplog.set_level(logging.INFO)
|
|
89
|
+
rule = {
|
|
90
|
+
'name': 'Test Datadog Event Alerter',
|
|
91
|
+
'type': 'any',
|
|
92
|
+
'datadog_api_key': 'test-api-key',
|
|
93
|
+
'datadog_app_key': 'test-app-key',
|
|
94
|
+
'alert': [],
|
|
95
|
+
'alert_subject': 'Test Datadog Event Alert'
|
|
96
|
+
}
|
|
97
|
+
rules_loader = FileRulesLoader({})
|
|
98
|
+
rules_loader.load_modules(rule)
|
|
99
|
+
alert = DatadogAlerter(rule)
|
|
100
|
+
match = {
|
|
101
|
+
'@timestamp': '2021-01-01T00:00:00',
|
|
102
|
+
'name': 'datadog-test-name'
|
|
103
|
+
}
|
|
104
|
+
with mock.patch('requests.post') as mock_post_request:
|
|
105
|
+
alert.alert([match])
|
|
106
|
+
|
|
107
|
+
mock_post_request.assert_called_once_with(
|
|
108
|
+
'https://api.datadoghq.com/api/v1/events',
|
|
109
|
+
data=mock.ANY,
|
|
110
|
+
headers={
|
|
111
|
+
'Content-Type': 'application/json',
|
|
112
|
+
'DD-API-KEY': rule['datadog_api_key'],
|
|
113
|
+
'DD-APPLICATION-KEY': rule['datadog_app_key']
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
assert ('elastalert', logging.INFO, 'Alert sent to Datadog') == caplog.record_tuples[0]
|
|
117
|
+
|
|
118
|
+
|
|
52
119
|
def test_datadog_ea_exception():
|
|
53
120
|
with pytest.raises(EAException) as ea:
|
|
54
121
|
rule = {
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import copy
|
|
2
|
-
import json
|
|
3
|
-
import requests
|
|
4
|
-
|
|
5
|
-
from elastalert.alerts import Alerter, DateTimeEncoder
|
|
6
|
-
from elastalert.util import EAException, elastalert_logger, lookup_es_key
|
|
7
|
-
from requests.exceptions import RequestException
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class MsTeamsAlerter(Alerter):
|
|
11
|
-
""" Creates a Microsoft Teams Conversation Message for each alert """
|
|
12
|
-
required_options = frozenset(['ms_teams_webhook_url'])
|
|
13
|
-
|
|
14
|
-
def __init__(self, rule):
|
|
15
|
-
super(MsTeamsAlerter, self).__init__(rule)
|
|
16
|
-
self.ms_teams_webhook_url = self.rule.get('ms_teams_webhook_url', None)
|
|
17
|
-
if isinstance(self.ms_teams_webhook_url, str):
|
|
18
|
-
self.ms_teams_webhook_url = [self.ms_teams_webhook_url]
|
|
19
|
-
self.ms_teams_proxy = self.rule.get('ms_teams_proxy', None)
|
|
20
|
-
self.ms_teams_alert_summary = self.rule.get('ms_teams_alert_summary', None)
|
|
21
|
-
self.ms_teams_alert_fixed_width = self.rule.get('ms_teams_alert_fixed_width', False)
|
|
22
|
-
self.ms_teams_theme_color = self.rule.get('ms_teams_theme_color', '')
|
|
23
|
-
self.ms_teams_ca_certs = self.rule.get('ms_teams_ca_certs')
|
|
24
|
-
self.ms_teams_ignore_ssl_errors = self.rule.get('ms_teams_ignore_ssl_errors', False)
|
|
25
|
-
self.ms_teams_alert_facts = self.rule.get('ms_teams_alert_facts', '')
|
|
26
|
-
self.ms_teams_attach_kibana_discover_url = self.rule.get('ms_teams_attach_kibana_discover_url', False)
|
|
27
|
-
self.ms_teams_kibana_discover_title = self.rule.get('ms_teams_kibana_discover_title', 'Discover in Kibana')
|
|
28
|
-
self.ms_teams_attach_opensearch_discover_url = self.rule.get('ms_teams_attach_opensearch_discover_url', False)
|
|
29
|
-
self.ms_teams_opensearch_discover_title = self.rule.get('ms_teams_opensearch_discover_title', 'Discover in opensearch')
|
|
30
|
-
|
|
31
|
-
def format_body(self, body):
|
|
32
|
-
if self.ms_teams_alert_fixed_width:
|
|
33
|
-
body = body.replace('`', "'")
|
|
34
|
-
body = "```{0}```".format('```\n\n```'.join(x for x in body.split('\n'))).replace('\n``````', '')
|
|
35
|
-
return body
|
|
36
|
-
|
|
37
|
-
def populate_facts(self, matches):
|
|
38
|
-
alert_facts = []
|
|
39
|
-
for arg in self.ms_teams_alert_facts:
|
|
40
|
-
arg = copy.copy(arg)
|
|
41
|
-
matched_value = lookup_es_key(matches[0], arg['value'])
|
|
42
|
-
arg['value'] = matched_value if matched_value is not None else arg['value']
|
|
43
|
-
alert_facts.append(arg)
|
|
44
|
-
return alert_facts
|
|
45
|
-
|
|
46
|
-
def alert(self, matches):
|
|
47
|
-
body = self.create_alert_body(matches)
|
|
48
|
-
body = self.format_body(body)
|
|
49
|
-
|
|
50
|
-
title = self.create_title(matches)
|
|
51
|
-
summary = title if self.ms_teams_alert_summary is None else self.ms_teams_alert_summary
|
|
52
|
-
# post to Teams
|
|
53
|
-
headers = {'content-type': 'application/json'}
|
|
54
|
-
|
|
55
|
-
if self.ms_teams_ca_certs:
|
|
56
|
-
verify = self.ms_teams_ca_certs
|
|
57
|
-
else:
|
|
58
|
-
verify = not self.ms_teams_ignore_ssl_errors
|
|
59
|
-
if self.ms_teams_ignore_ssl_errors:
|
|
60
|
-
requests.packages.urllib3.disable_warnings()
|
|
61
|
-
|
|
62
|
-
# set https proxy, if it was provided
|
|
63
|
-
proxies = {'https': self.ms_teams_proxy} if self.ms_teams_proxy else None
|
|
64
|
-
payload = {
|
|
65
|
-
'@type': 'MessageCard',
|
|
66
|
-
'@context': 'http://schema.org/extensions',
|
|
67
|
-
'summary': summary ,
|
|
68
|
-
'title': title,
|
|
69
|
-
'sections': [{'text': body}],
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if self.ms_teams_alert_facts != '':
|
|
73
|
-
payload['sections'][0]['facts'] = self.populate_facts(matches)
|
|
74
|
-
|
|
75
|
-
if self.ms_teams_theme_color != '':
|
|
76
|
-
payload['themeColor'] = self.ms_teams_theme_color
|
|
77
|
-
|
|
78
|
-
if self.ms_teams_attach_kibana_discover_url:
|
|
79
|
-
kibana_discover_url = lookup_es_key(matches[0], 'kibana_discover_url')
|
|
80
|
-
if kibana_discover_url:
|
|
81
|
-
payload['potentialAction'] = [
|
|
82
|
-
{
|
|
83
|
-
'@type': 'OpenUri',
|
|
84
|
-
'name': self.ms_teams_kibana_discover_title,
|
|
85
|
-
'targets': [
|
|
86
|
-
{
|
|
87
|
-
'os': 'default',
|
|
88
|
-
'uri': kibana_discover_url,
|
|
89
|
-
}
|
|
90
|
-
],
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
if self.ms_teams_attach_opensearch_discover_url:
|
|
94
|
-
opensearch_discover_url = lookup_es_key(matches[0], 'opensearch_discover_url')
|
|
95
|
-
if opensearch_discover_url:
|
|
96
|
-
payload['potentialAction'] = [
|
|
97
|
-
{
|
|
98
|
-
'@type': 'OpenUri',
|
|
99
|
-
'name': self.ms_teams_opensearch_discover_title,
|
|
100
|
-
'targets': [
|
|
101
|
-
{
|
|
102
|
-
'os': 'default',
|
|
103
|
-
'uri': opensearch_discover_url,
|
|
104
|
-
}
|
|
105
|
-
],
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
for url in self.ms_teams_webhook_url:
|
|
110
|
-
try:
|
|
111
|
-
response = requests.post(url, data=json.dumps(payload, cls=DateTimeEncoder),
|
|
112
|
-
headers=headers, proxies=proxies, verify=verify)
|
|
113
|
-
response.raise_for_status()
|
|
114
|
-
except RequestException as e:
|
|
115
|
-
raise EAException("Error posting to MS Teams: %s" % e)
|
|
116
|
-
elastalert_logger.info("Alert sent to MS Teams")
|
|
117
|
-
|
|
118
|
-
def get_info(self):
|
|
119
|
-
return {'type': 'ms_teams',
|
|
120
|
-
'ms_teams_webhook_url': self.ms_teams_webhook_url}
|