diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b9c96c4..8e3ecd84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - [Helm] Add optional liveness and readiness probe - [#1604](https://github.com/jertel/elastalert2/pull/1604) - @aizerin - Add `include_rule_params_in_matches` rule parameter to enable copying of specific rule params into match data - [#1605](https://github.com/jertel/elastalert2/pull/1605) - @jertel - [Helm] Add `--prometheus_addr` command line parameter to allow binding the Prometheus metrics server on a different host address - [#1608](https://github.com/jertel/elastalert2/pull/1608) - @tgxworld +- Support YZJ general robots - - @innerpeacez ## Other changes - [Docs] Add missing documentation of the `aggregation_alert_time_compared_with_timestamp_field` option. - [#1588](https://github.com/jertel/elastalert2/pull/1588) - @nicolasnovelli diff --git a/docs/source/alerts.rst b/docs/source/alerts.rst index 2d18eaa6..5457ccb6 100644 --- a/docs/source/alerts.rst +++ b/docs/source/alerts.rst @@ -57,6 +57,7 @@ or - webex_webhook - workwechat - zabbix + - yzj Options for each alerter can either defined at the top level of the YAML file, or nested within the alert name, allowing for different settings for multiple of the same alerter. For example, consider sending multiple emails, but with different 'To' and 'From' fields: @@ -2605,3 +2606,30 @@ Example usage:: zbx_key: "sender_load1" where ``hostname`` is the available elasticsearch field. + +YZJ +~~~~~~~ + +YZJ will send notification to a YZJ application. The body of the notification is formatted the same as with other alerters. + +Required: + +``yzj_token``: The request token. + +Optional: + +``yzj_webhook_url``: The webhook URL. + +``yzj_type``: Default 0, send text message. https://www.yunzhijia.com/opendocs/docs.html#/server-api/im/index?id=%e7%be%a4%e7%bb%84%e6%9c%ba%e5%99%a8%e4%ba%ba + +``yzj_proxy``: By default ElastAlert 2 will not use a network proxy to send notifications to YZJ. Set this option using ``hostname:port`` if you need to use a proxy. only supports https. + +``yzj_custom_loc``: The YZJ custom loc for privately deployed. + + +Example usage:: + + alert: + - "yzj" + yzj_token: "token" + diff --git a/elastalert/alerters/yzj.py b/elastalert/alerters/yzj.py new file mode 100644 index 00000000..eee21909 --- /dev/null +++ b/elastalert/alerters/yzj.py @@ -0,0 +1,57 @@ +import json +import warnings +from urllib.parse import urlunsplit, urlsplit + +import requests +from requests import RequestException + +from elastalert.alerts import Alerter, DateTimeEncoder +from elastalert.util import EAException, elastalert_logger + + +class YzjAlerter(Alerter): + """ Creates a YZJ room message for each alert """ + required_options = frozenset(['yzj_token']) + + def __init__(self, rule): + super(YzjAlerter, self).__init__(rule) + self.yzj_token = self.rule.get('yzj_token', None) + self.yzj_type = self.rule.get('yzj_type', 0) + self.yzj_webhook_url = 'https://www.yunzhijia.com/gateway/robot/webhook/send?yzjtype=%s&yzjtoken=%s' % (self.yzj_type, self.yzj_token) + self.yzj_proxy = self.rule.get('yzj_proxy', None) + self.yzj_custom_loc = self.rule.get('yzj_custom_loc', None) + + def alert(self, matches): + body = self.create_alert_body(matches) + + proxies = {'https': self.yzj_proxy} if self.yzj_proxy else None + headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json;charset=utf-8' + } + + if self.yzj_custom_loc is not None: + part = urlsplit(self.yzj_webhook_url) + custom_part = part._replace(netloc=self.yzj_custom_loc) + self.yzj_webhook_url = urlunsplit(custom_part) + + payload = { + 'content': body + } + + try: + response = requests.post(self.yzj_webhook_url, data=json.dumps(payload, cls=DateTimeEncoder), + headers=headers, + proxies=proxies) + warnings.resetwarnings() + response.raise_for_status() + except RequestException as e: + raise EAException("Error posting to yzj: %s" % e) + + elastalert_logger.info("Trigger sent to YZJ") + + def get_info(self): + return { + "type": "yzj", + "yzj_webhook_url": self.yzj_webhook_url + } diff --git a/elastalert/loaders.py b/elastalert/loaders.py index ca6247f7..8ddc34c0 100644 --- a/elastalert/loaders.py +++ b/elastalert/loaders.py @@ -53,6 +53,7 @@ from elastalert.alerters.sns import SnsAlerter from elastalert.alerters.teams import MsTeamsAlerter from elastalert.alerters.powerautomate import MsPowerAutomateAlerter +from elastalert.alerters.yzj import YzjAlerter from elastalert.alerters.zabbix import ZabbixAlerter from elastalert.alerters.tencentsms import TencentSMSAlerter from elastalert.alerters.indexer import IndexerAlerter @@ -145,6 +146,7 @@ class RulesLoader(object): 'iris': elastalert.alerters.iris.IrisAlerter, 'indexer': IndexerAlerter, 'matrixhookshot': MatrixHookshotAlerter, + 'yzj': YzjAlerter, } # A partial ordering of alert types. Relative order will be preserved in the resulting alerts list diff --git a/elastalert/schema.yaml b/elastalert/schema.yaml index 6d1d13d6..561a7c12 100644 --- a/elastalert/schema.yaml +++ b/elastalert/schema.yaml @@ -63,7 +63,7 @@ definitions: additionalProperties: false properties: name: {type: string} - value: {type: string} + value: {type: string} arrayOfMsTeamsFacts: &arrayOfMsTeamsFacts type: array @@ -913,3 +913,9 @@ properties: zbx_host_from_field: {type: boolean} zbx_host: {type: string} zbx_key: {type: string} + + ### YZJ + yzj_token: {type: string} + yzj_type: {type: integer, enum: [0]} + yzj_custom_loc: {type: string} + diff --git a/tests/alerters/yzj_test.py b/tests/alerters/yzj_test.py new file mode 100644 index 00000000..8d04eb16 --- /dev/null +++ b/tests/alerters/yzj_test.py @@ -0,0 +1,47 @@ +import json +import logging + +from unittest import mock + +from elastalert.alerters.yzj import YzjAlerter +from elastalert.loaders import FileRulesLoader + + +def test_yzj_text(caplog): + caplog.set_level(logging.INFO) + rule = { + 'name': 'Test YZJ Rule', + 'type': 'any', + 'yzj_token': 'xxxxxxx', + 'yzj_custom_loc': 'www.myloc.cn', + 'alert': [], + 'alert_subject': 'Test YZJ' + } + rules_loader = FileRulesLoader({}) + rules_loader.load_modules(rule) + alert = YzjAlerter(rule) + match = { + '@timestamp': '2021-01-01T00:00:00', + 'somefield': 'foobarbaz' + } + + with mock.patch('requests.post') as mock_post_request: + alert.alert([match]) + + expected_data = { + 'content': 'Test YZJ Rule\n\n@timestamp: 2021-01-01T00:00:00\nsomefield: foobarbaz\n' + } + + mock_post_request.assert_called_once_with( + 'https://www.myloc.cn/gateway/robot/webhook/send?yzjtype=0&yzjtoken=xxxxxxx', + data=mock.ANY, + headers={ + 'Content-Type': 'application/json', + 'Accept': 'application/json;charset=utf-8' + }, + proxies=None + ) + + actual_data = json.loads(mock_post_request.call_args_list[0][1]['data']) + assert expected_data == actual_data + assert ('elastalert', logging.INFO, 'Trigger sent to YZJ') == caplog.record_tuples[0]