From 462dbbf1641ce7dbef93918d695edd048b334ad1 Mon Sep 17 00:00:00 2001 From: smile Date: Sun, 10 Mar 2019 12:53:35 +0300 Subject: [PATCH 1/2] Add a webhook service to integrate with http/https api to send messages. --- config.py | 11 +++++++++++ core/config/proto/legacy.py | 13 +++++++++++++ main/models/notificationgroup.py | 3 ++- ui/web/main/notificationgroup/Application.js | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/config.py b/config.py index 2187c5c18a..a1deb53af4 100644 --- a/config.py +++ b/config.py @@ -512,6 +512,17 @@ class Config(BaseConfig): retry_timeout = IntParameter(default=2) use_proxy = BooleanParameter(default=False) + class webhook(ConfigSection): + url = SecretParameter() + message = StringParameter() + sender = StringParameter() + received = StringParameter() + options = StringParameter() + # Method default False, use GET method + method = BooleanParameter(default=False) + retry_timeout = IntParameter(default=2) + use_proxy = BooleanParameter(default=False) + class threadpool(ConfigSection): idle_timeout = SecondsParameter(default="30s") shutdown_timeout = SecondsParameter(default="1M") diff --git a/core/config/proto/legacy.py b/core/config/proto/legacy.py index 97ae26d3b0..d2308f57b7 100644 --- a/core/config/proto/legacy.py +++ b/core/config/proto/legacy.py @@ -138,6 +138,19 @@ class LegacyProtocol(BaseProtocol): # TgSender ("tgsender.token", "tgsender.token"), ("tgsender-global-%(node)s.token", "tgsender.token"), + # WebHook + ("webhook.url", "webhook.url"), + ("webhook-global-%(node)s.url", "webhook.url"), + ("webhook.message", "webhook.message"), + ("webhook-global-%(node)s.message", "webhook.message"), + ("webhook.sender", "webhook.sender"), + ("webhook-global-%(node)s.sender", "webhook.sender"), + ("webhook.received", "webhook.received"), + ("webhook-global-%(node)s.received", "webhook.received"), + ("webhook.options", "webhook.options"), + ("webhook-global-%(node)s.options", "webhook.options"), + ("webhook.method", "webhook.method"), + ("webhook-global-%(node)s.method", "webhook.method"), # TrapCollector ("trapcollector.listen_traps", "trapcollector.listen"), ("trapcollector-%(pool)s-%(node)s.listen_traps", "trapcollector.listen"), diff --git a/main/models/notificationgroup.py b/main/models/notificationgroup.py index 257cc99677..2c6040f77f 100644 --- a/main/models/notificationgroup.py +++ b/main/models/notificationgroup.py @@ -29,7 +29,8 @@ logger = logging.getLogger(__name__) NOTIFICATION_TOPICS = { "mail": "mailsender", - "tg": "tgsender" + "tg": "tgsender", + "wh": "webhook" } NOTIFICATION_METHOD_CHOICES = [ diff --git a/ui/web/main/notificationgroup/Application.js b/ui/web/main/notificationgroup/Application.js index ca09b4a0b0..054287ea12 100644 --- a/ui/web/main/notificationgroup/Application.js +++ b/ui/web/main/notificationgroup/Application.js @@ -85,6 +85,7 @@ Ext.define("NOC.main.notificationgroup.Application", { store: [ ["mail", "Mail"], ["tg", "Telegram"], + ["wh", "Webhook"], ["file", "File"], ["xmpp", "Jabber"] ] -- GitLab From ad510878fa29c5118d2c9df4c5a0dece6a213998 Mon Sep 17 00:00:00 2001 From: smile Date: Sun, 10 Mar 2019 12:54:21 +0300 Subject: [PATCH 2/2] Add service --- services/webhook/__init__.py | 0 services/webhook/service.py | 106 +++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 services/webhook/__init__.py create mode 100644 services/webhook/service.py diff --git a/services/webhook/__init__.py b/services/webhook/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/services/webhook/service.py b/services/webhook/service.py new file mode 100644 index 0000000000..f07863a472 --- /dev/null +++ b/services/webhook/service.py @@ -0,0 +1,106 @@ +#!./bin/python +# -*- coding: utf-8 -*- +# ---------------------------------------------------------------------- +# webhook service +# ---------------------------------------------------------------------- +# Copyright (C) 2007-2019 The NOC Project +# See LICENSE for details +# ---------------------------------------------------------------------- + +# Third-party modules +import re +import datetime +import time +import urllib +# NOC modules +from noc.core.service.base import Service +from noc.core.http.client import fetch_sync +from noc.core.perf import metrics +from noc.config import config + + +class WebhookService(Service): + name = "webhook" + + def __init__(self, *args, **kwargs): + super(WebhookService, self).__init__(*args, **kwargs) + self.url = config.webhook.url + self.message = config.webhook.message + self.sender = config.webhook.sender + self.method = "POST" if config.webhook.method is True else "GET" + self.received = config.webhook.received + self.options = config.webhook.options + + def on_activate(self): + if self.url and self.message and self.received: + self.subscribe( + topic=self.name, + channel="sender", + handler=self.on_message + ) + + def on_message(self, message, address, subject, body, **kwargs): + self.logger.info( + "[%s] Receiving message: %s (%s) [%s, attempt %d]", + message.id, subject, address, + datetime.datetime.fromtimestamp( + message.timestamp / 1000000000.0 + ), + message.attempts + ) + return self.send_tb(message.id, address, subject, body) + + def _str(self, uni): + ''' Make inbound a string Encoding to utf-8 if needed ''' + try: + return str(uni) + except Exception as e: + self.logger.error("Not str() -> %s failed: %s", uni, e) + return uni.encode('utf-8') + + @staticmethod + def escape_markdown(text): + """Helper function to escape telegram markup symbols""" + escape_chars = '\*_`' + return re.sub(r'([%s])' % escape_chars, r'\\\1', text) + + def send_tb(self, messages, address, subject, body): + # proxy_addres = config.proxy.https_proxy # not used. + sendMessage = self._str(subject + '\n' + body) + time.sleep(config.webhook.retry_timeout) + if self.url and self.message and self.received: + url = self.url + if self.received: + url = url + "%s%s" % (self.received, address) + if self.sender: + url = url + self.sender + url = url + "%s%s" % (self.message, urllib.quote(sendMessage)) + if self.options: + url = url + self.options + self.logger.info("HTTP %s %s", self.method, url) + try: + code, header, body = fetch_sync( + url, + method=self.method, + allow_proxy=True, + request_timeout=config.activator.http_request_timeout, + follow_redirects=True, + validate_cert=config.activator.http_validate_cert, + ) + if 200 <= code <= 299: + self.logger.info("Result: %s" % body) + metrics["webhook_proxy_sended_ok"] += 1 + return True + else: + self.logger.error("HTTP %s %s failed: %s %s", self.method, url, code, body) + metrics["webhook_proxy_failed_httperror"] += 1 + return False + except Exception as e: + self.logger.error("HTTP %s failed: %s", self.method, e) + else: + self.logger.info("No Url, no message, no sender, no received") + return False + + +if __name__ == "__main__": + WebhookService().start() -- GitLab