From 2a6632b78f09a3fb70c18151a5e79618569c9205 Mon Sep 17 00:00:00 2001 From: Nick Satterly <nfsatterly@gmail.com> Date: Mon, 20 Mar 2023 23:39:34 +0100 Subject: [PATCH] Fix multiple linting errors --- .isort.cfg | 2 + .pre-commit-config.yaml | 46 +++++ AUTHORS | 1 - README.md | 1 - integrations/consul/consulalerta.py | 75 +++---- integrations/consul/consulheartbeat.py | 17 +- integrations/consul/setup.py | 7 +- integrations/fail2ban/alerta.conf | 4 +- integrations/mailer/email.html.tmpl | 2 +- integrations/mailer/email.tmpl | 1 - integrations/mailer/mailer.py | 127 ++++++------ integrations/mailer/requirements.txt | 2 +- integrations/mailer/setup.py | 4 +- integrations/mailer/test_rules.py | 55 +++--- integrations/opsgenie/README.md | 26 +-- integrations/opsgenie/oecAlertaExecutor.py | 131 ++++++++----- integrations/opsweekly/alerta.php | 1 - integrations/pinger/pinger.py | 60 +++--- integrations/pinger/setup.py | 17 +- integrations/snmptrap/README.md | 4 +- integrations/snmptrap/handler.py | 33 ++-- integrations/snmptrap/setup.py | 4 +- integrations/sqs/alerta_sqs.py | 21 +- integrations/sqs/setup.py | 4 +- integrations/supervisor/evlistener.py | 14 +- integrations/syslog/setup.py | 4 +- integrations/syslog/syslogfwder.py | 178 +++++++++-------- integrations/urlmon/settings.py | 28 ++- integrations/urlmon/setup.py | 4 +- integrations/urlmon/urlmon.py | 76 +++++--- plugins/alertops/README.md | 9 +- plugins/alertops/alerta_alertops.py | 61 +++--- plugins/alertops/setup.py | 4 +- plugins/amqp/alerta_amqp.py | 20 +- plugins/amqp/listener.py | 4 +- plugins/amqp/setup.py | 5 +- plugins/cachet/alerta_cachet.py | 29 +-- plugins/cachet/setup.py | 5 +- plugins/debug/alerta_debug.py | 17 +- plugins/debug/setup.py | 5 +- plugins/dingtalk/README.md | 3 - plugins/dingtalk/alerta_ding.py | 29 +-- plugins/dingtalk/dingtalkchatbot/__about__.py | 2 +- plugins/dingtalk/dingtalkchatbot/chatbot.py | 184 +++++++++--------- .../dingtalk/dingtalkchatbot/chatbot_test.py | 45 +++-- plugins/dingtalk/dingtalkchatbot/samples.py | 35 ++-- plugins/dingtalk/setup.py | 5 +- plugins/enhance/alerta_enhance.py | 6 +- plugins/enhance/setup.py | 5 +- plugins/forward/alerta_forward.py | 5 +- plugins/forward/setup.py | 5 +- plugins/geoip/alerta_geoip.py | 27 +-- plugins/geoip/setup.py | 5 +- plugins/goalert/alerta_goalert.py | 52 ++--- plugins/goalert/setup.py | 5 +- plugins/influxdb/alerta_influxdb.py | 22 ++- plugins/influxdb/setup.py | 5 +- plugins/jira/README.md | 4 +- plugins/jira/alerta_jira.py | 76 ++++---- plugins/jira/setup.py | 4 +- plugins/logstash/alerta_logstash.py | 20 +- plugins/logstash/setup.py | 5 +- plugins/matrix/alerta_matrix.py | 69 ++++--- plugins/matrix/setup.py | 22 +-- plugins/mattermost/README.md | 2 +- plugins/mattermost/alerta_mattermost.py | 5 +- plugins/mattermost/setup.py | 4 +- plugins/msteams/alerta_msteams.py | 74 ++++--- plugins/msteams/setup.py | 5 +- plugins/normalise/alerta_normalise.py | 5 +- plugins/normalise/setup.py | 5 +- plugins/op5/README.md | 2 +- plugins/op5/alerta_op5.py | 29 +-- plugins/op5/setup.py | 7 +- plugins/opsgenie/alerta_opsgenie.py | 89 +++++---- plugins/opsgenie/setup.py | 5 +- plugins/pagerduty/alerta_pagerduty.py | 56 +++--- plugins/pagerduty/setup.py | 5 +- plugins/prometheus/README.md | 10 +- plugins/prometheus/alerta_prometheus.py | 119 ++++++----- plugins/prometheus/setup.py | 5 +- plugins/pubsub/README.md | 2 +- plugins/pubsub/alerta_pubsub.py | 43 ++-- plugins/pubsub/setup.py | 5 +- plugins/pushover/alerta_pushover.py | 38 ++-- plugins/pushover/setup.py | 5 +- plugins/rocketchat/alerta_rocketchat.py | 59 +++--- plugins/rocketchat/setup.py | 5 +- plugins/slack/README.md | 4 +- plugins/slack/alerta_slack.py | 141 ++++++++------ plugins/slack/setup.py | 5 +- plugins/slack/test_slack.py | 6 +- plugins/sns/alerta_sns.py | 29 +-- plugins/sns/setup.py | 5 +- plugins/syslog/alerta_logger.py | 20 +- plugins/syslog/setup.py | 5 +- plugins/telegram/alerta_telegram.py | 58 +++--- plugins/telegram/setup.py | 5 +- plugins/timeout/README.md | 3 +- plugins/timeout/alerta_timeout.py | 12 +- plugins/timeout/setup.py | 6 +- plugins/twilio/alerta_twilio_sms.py | 28 +-- plugins/twilio/setup.py | 5 +- plugins/zabbix/README.md | 4 - plugins/zabbix/alerta_zabbix.py | 57 ++++-- plugins/zabbix/setup.py | 5 +- pylintrc | 1 - requirements-dev.txt | 8 +- webhooks/azuremonitor/alerta_azuremonitor.py | 8 +- webhooks/azuremonitor/setup.py | 4 +- webhooks/azuremonitor/test_azuremonitor.py | 45 +++-- webhooks/fail2ban/alerta_fail2ban.py | 7 +- webhooks/fail2ban/setup.py | 4 +- webhooks/mailgun/alerta_mailgun.py | 11 +- webhooks/mailgun/setup.py | 4 +- webhooks/msteams/alerta_msteamswebhook.py | 43 ++-- webhooks/msteams/setup.py | 4 +- webhooks/msteams/test_msteamswebhook.py | 68 +++---- webhooks/query/README.md | 2 +- webhooks/query/alerta_query.py | 47 +++-- webhooks/query/setup.py | 5 +- webhooks/sentry/alerta_sentry.py | 4 +- webhooks/sentry/setup.py | 4 +- webhooks/sentry/test_sentry.py | 41 ++-- webhooks/statuscake/README.md | 1 - webhooks/statuscake/alerta_statuscake.py | 16 +- webhooks/statuscake/setup.py | 6 +- 127 files changed, 1692 insertions(+), 1371 deletions(-) create mode 100644 .isort.cfg create mode 100644 .pre-commit-config.yaml diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..327b5b0 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +known_third_party = Queue,alerta,alerta_azuremonitor,alerta_msteamswebhook,alerta_sentry,alerta_slack,alertaclient,boto,cachetclient,consul,dateutil,dingtalkchatbot,flask,google,influxdb,jinja2,kombu,mailer,matterhook,mock,op5,pymsteams,pytest,pyzabbix,requests,settings,setuptools,telepot,twilio,yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..78dfe91 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,46 @@ +repos: +- repo: https://github.com/pre-commit/mirrors-autopep8 + rev: v1.5.1 + hooks: + - id: autopep8 +- repo: https://github.com/pre-commit/pre-commit-hooks.git + rev: v2.5.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-docstring-first + - id: check-json + - id: check-merge-conflict + - id: check-yaml + - id: debug-statements + - id: double-quote-string-fixer + - id: end-of-file-fixer + - id: fix-encoding-pragma + args: ['--remove'] + - id: pretty-format-json + args: ['--autofix'] + - id: name-tests-test + args: ['--django'] + exclude: ^tests/helpers/ + - id: requirements-txt-fixer + - id: trailing-whitespace +- repo: https://github.com/pycqa/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + args: ['--config=setup.cfg', '--ignore=E501'] +- repo: https://github.com/asottile/pyupgrade + rev: v1.27.0 + hooks: + - id: pyupgrade + args: ['--py3-plus'] +- repo: https://github.com/asottile/seed-isort-config + rev: v1.9.4 + hooks: + - id: seed-isort-config +- repo: https://github.com/pre-commit/mirrors-isort + rev: v4.3.21 + hooks: + - id: isort diff --git a/AUTHORS b/AUTHORS index c346d37..b0666c8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -63,4 +63,3 @@ Yoshiharu Mori <y-mori@sraoss.co.jp> Карамышев Степан <karamyshev@corp.sputnik.ru> $ git log --format='%aN <%aE>' | sort -f | uniq - diff --git a/README.md b/README.md index 972b979..aec5d88 100644 --- a/README.md +++ b/README.md @@ -96,4 +96,3 @@ License ------- Copyright (c) 2014-2020 Nick Satterly and [AUTHORS](AUTHORS). Available under the MIT License. - diff --git a/integrations/consul/consulalerta.py b/integrations/consul/consulalerta.py index af9f57e..a6bdf1c 100755 --- a/integrations/consul/consulalerta.py +++ b/integrations/consul/consulalerta.py @@ -2,112 +2,117 @@ import json import os - -import consul import sys import time + +import consul from alertaclient.api import Client CONSUL_HOST = os.environ.get('CONSUL_HOST', '127.0.0.1') CONSUL_PORT = int(os.environ.get('CONSUL_PORT', 8500)) -client = consul.Consul(host=CONSUL_HOST, port=CONSUL_PORT, token=None, scheme='http', consistency='default', dc=None, verify=True) +client = consul.Consul(host=CONSUL_HOST, port=CONSUL_PORT, token=None, + scheme='http', consistency='default', dc=None, verify=True) j = json.load(sys.stdin) -print("Request:") +print('Request:') print(j) try: url = client.kv.get('alerta/apiurl')[1]['Value'] except Exception: - print("No URL defined, exiting") + print('No URL defined, exiting') sys.exit(1) try: key = client.kv.get('alerta/apikey')[1]['Value'] except Exception: - print("No key defined, exiting") + print('No key defined, exiting') sys.exit(1) try: max_retries = int(client.kv.get('alerta/max_retries')[1]['Value']) except TypeError: - print("No value defined, using default") + print('No value defined, using default') max_retries = 3 try: sleep = int(client.kv.get('alerta/sleep')[1]['Value']) except TypeError: - print("No value defined, using default") + print('No value defined, using default') sleep = 2 try: timeout = int(client.kv.get('alerta/timeout')[1]['Value']) except TypeError: - print("No value defined, using default") + print('No value defined, using default') timeout = 900 try: origin = client.kv.get('alerta/origin')[1]['Value'] except TypeError: - print("No value defined, using default") - origin = "consul" + print('No value defined, using default') + origin = 'consul' try: alerttype = client.kv.get('alerta/alerttype')[1]['Value'] except TypeError: - print("No value defined, using default") - alerttype = "ConsulAlert" + print('No value defined, using default') + alerttype = 'ConsulAlert' api = Client(endpoint=url, key=key) SEVERITY_MAP = { - 'critical': 'critical', - 'warning': 'warning', - 'passing': 'ok', + 'critical': 'critical', + 'warning': 'warning', + 'passing': 'ok', } -def createalert( data ): + +def createalert(data): try: - environment = client.kv.get('alerta/env/{0}'.format(data['Node']))[1]['Value'] + environment = client.kv.get( + 'alerta/env/{}'.format(data['Node']))[1]['Value'] except Exception: try: environment = client.kv.get('alerta/defaultenv')[1]['Value'] except Exception: - environment = "Production" + environment = 'Production' for _ in range(max_retries): try: - print("Response:") + print('Response:') response = api.send_alert( - resource=data['Node'], - event=data['CheckId'], - value=data['Status'], - correlate=SEVERITY_MAP.keys(), - environment=environment, - service=[data['CheckId']], - severity=SEVERITY_MAP[data['Status']], - text=data['Output'], - timeout=timeout, - origin=origin, - type=alerttype + resource=data['Node'], + event=data['CheckId'], + value=data['Status'], + correlate=SEVERITY_MAP.keys(), + environment=environment, + service=[data['CheckId']], + severity=SEVERITY_MAP[data['Status']], + text=data['Output'], + timeout=timeout, + origin=origin, + type=alerttype ) print(response) except Exception as e: - print("HTTP Error: {}".format(e)) + print('HTTP Error: {}'.format(e)) time.sleep(sleep) continue else: break else: - print("api is down") + print('api is down') + def main(): for item in enumerate(j): - i=item[0] + i = item[0] createalert(j[i]) -if __name__ == "__main__": + +if __name__ == '__main__': main() diff --git a/integrations/consul/consulheartbeat.py b/integrations/consul/consulheartbeat.py index 9caa52c..97e226a 100755 --- a/integrations/consul/consulheartbeat.py +++ b/integrations/consul/consulheartbeat.py @@ -1,10 +1,12 @@ #!/usr/bin/env python -from alertaclient.api import Client -import consul import time -client = consul.Consul(host='127.0.0.1', port=8500, token=None, scheme='http', consistency='default', dc=None, verify=True) +import consul +from alertaclient.api import Client + +client = consul.Consul(host='127.0.0.1', port=8500, token=None, + scheme='http', consistency='default', dc=None, verify=True) url = client.kv.get('alerta/apiurl')[1]['Value'] key = client.kv.get('alerta/apikey')[1]['Value'] @@ -16,21 +18,24 @@ timeout = int(client.kv.get('alerta/timeout')[1]['Value']) origin = client.kv.get('alerta/origin')[1]['Value'] api = Client(endpoint=url, key=key) + def createheartbeat(): for _ in range(max_retries): try: print(api.heartbeat(origin=origin, timeout=timeout)) except Exception as e: - print("HTTP Error: {}".format(e)) + print('HTTP Error: {}'.format(e)) time.sleep(sleep) continue else: break else: - print("api is down") + print('api is down') + def main(): createheartbeat() -if __name__ == "__main__": + +if __name__ == '__main__': main() diff --git a/integrations/consul/setup.py b/integrations/consul/setup.py index cda1762..9dc9871 100644 --- a/integrations/consul/setup.py +++ b/integrations/consul/setup.py @@ -1,17 +1,16 @@ - from setuptools import setup version = '1.1.1' setup( - name="alerta-consul", + name='alerta-consul', version=version, description='Send emails from Alerta', url='https://github.com/alerta/alerta-contrib', license='MIT', author='Marco Supino', author_email='marco@supino.org', - py_modules=['consulalerta','consulheartbeat'], + py_modules=['consulalerta', 'consulheartbeat'], install_requires=[ 'alerta', 'python-consul' @@ -24,7 +23,7 @@ setup( 'consul-heartbeat = consulheartbeat:main' ] }, - keywords="alerta monitoring consul", + keywords='alerta monitoring consul', classifiers=[ 'Topic :: System :: Monitoring', ] diff --git a/integrations/fail2ban/alerta.conf b/integrations/fail2ban/alerta.conf index 9e79be6..caaf467 100644 --- a/integrations/fail2ban/alerta.conf +++ b/integrations/fail2ban/alerta.conf @@ -48,13 +48,13 @@ actionban = echo <matches> | grep -q "Failed password for [a-z][-a-z0-9_]* from # Notes.: Alerta API URL # Values: STRING # -alertaurl = +alertaurl = # Option: alertaapikey # Notes.: Alerta API key # Values: STRING # -alertaapikey = +alertaapikey = # Option: logpath # Notes.: Absolute path to the log file diff --git a/integrations/mailer/email.html.tmpl b/integrations/mailer/email.html.tmpl index f4a70aa..402a3b2 100644 --- a/integrations/mailer/email.html.tmpl +++ b/integrations/mailer/email.html.tmpl @@ -16,7 +16,7 @@ <strong>Duplicate Count</strong>: {{ alert.duplicate_count }} <br> <strong>Origin</strong>: {{ alert.origin }} <br> <strong>Tags</strong>: {{ alert.tags|join(', ') }} <br> -{% for key,value in alert.attributes.items() -%} +{% for key,value in alert.attributes.items() -%} {{ key|title }}: {{ value }} {% endfor -%} <br> diff --git a/integrations/mailer/email.tmpl b/integrations/mailer/email.tmpl index efc7860..b572fe8 100644 --- a/integrations/mailer/email.tmpl +++ b/integrations/mailer/email.tmpl @@ -30,4 +30,3 @@ To acknowledge this alert visit this URL: {{ dashboard_url }}/#/alert/{{ alert.id }} Generated by {{ program }} on {{ hostname }} at {{ now }} - diff --git a/integrations/mailer/mailer.py b/integrations/mailer/mailer.py index eaa3338..18b59f7 100755 --- a/integrations/mailer/mailer.py +++ b/integrations/mailer/mailer.py @@ -9,16 +9,14 @@ import re import signal import smtplib import socket -from configparser import RawConfigParser -from functools import reduce -import six - import sys import threading import time +from configparser import RawConfigParser from email.header import Header from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText +from functools import reduce import jinja2 from alertaclient.api import Client @@ -33,7 +31,7 @@ DNS_RESOLVER_AVAILABLE = False try: import dns.resolver DNS_RESOLVER_AVAILABLE = True -except: +except Exception: sys.stdout.write('Python dns.resolver unavailable. The skip_mta option will be forced to False\n') # nopep8 @@ -42,34 +40,39 @@ LOG = logging.getLogger(__name__) root = logging.getLogger() DEFAULT_OPTIONS = { - 'config_file': '~/.alerta.conf', - 'profile': None, - 'endpoint': 'http://localhost:8080', - 'key': '', - 'amqp_url': 'redis://localhost:6379/', - 'amqp_topic': 'notify', - 'amqp_queue_name': '', # Name of the AMQP queue. Default is no name (default queue destination). - 'amqp_queue_exclusive': True, # Exclusive queues may only be consumed by the current connection. - 'smtp_host': 'smtp.gmail.com', - 'smtp_port': 587, - 'smtp_username': '', # application-specific username if it differs from the specified 'mail_from' user + 'config_file': '~/.alerta.conf', + 'profile': None, + 'endpoint': 'http://localhost:8080', + 'key': '', + 'amqp_url': 'redis://localhost:6379/', + 'amqp_topic': 'notify', + # Name of the AMQP queue. Default is no name (default queue destination). + 'amqp_queue_name': '', + # Exclusive queues may only be consumed by the current connection. + 'amqp_queue_exclusive': True, + 'smtp_host': 'smtp.gmail.com', + 'smtp_port': 587, + # application-specific username if it differs from the specified 'mail_from' user + 'smtp_username': '', 'smtp_password': '', # application-specific password if gmail used 'smtp_starttls': True, # use the STARTTLS SMTP extension 'smtp_use_ssl': False, # whether or not SSL is being used for the SMTP connection - 'ssl_key_file': None, # a PEM formatted private key file for the SSL connection - 'ssl_cert_file': None, # a certificate chain file for the SSL connection - 'mail_from': '', # alerta@example.com - 'mail_to': [], # devops@example.com, support@example.com + 'ssl_key_file': None, # a PEM formatted private key file for the SSL connection + 'ssl_cert_file': None, # a certificate chain file for the SSL connection + 'mail_from': '', # alerta@example.com + 'mail_to': [], # devops@example.com, support@example.com 'mail_localhost': None, # fqdn to use in the HELO/EHLO command - 'mail_template': os.path.dirname(__file__) + os.sep + 'email.tmpl', + 'mail_template': os.path.dirname(__file__) + os.sep + 'email.tmpl', 'mail_template_html': os.path.dirname(__file__) + os.sep + 'email.html.tmpl', # nopep8 - 'mail_subject': ('[{{ alert.status|capitalize }}] {{ alert.environment }}: ' - '{{ alert.severity|capitalize }} {{ alert.event }} on ' - '{{ alert.service|join(\',\') }} {{ alert.resource }}'), + 'mail_subject': ( + '[{{ alert.status|capitalize }}] {{ alert.environment }}: ' + '{{ alert.severity|capitalize }} {{ alert.event }} on ' + '{{ alert.service|join(\',\') }} {{ alert.resource }}' + ), 'dashboard_url': 'http://try.alerta.io', - 'debug': False, - 'skip_mta': False, - 'email_type': 'text', # options are: text, html + 'debug': False, + 'skip_mta': False, + 'email_type': 'text', # options are: text, html 'severities': [] } @@ -135,10 +138,7 @@ class FanoutConsumer(ConsumerMixin): message.ack() return - if ( - alert.severity not in sevs and - alert.previous_severity not in sevs - ): + if alert.severity not in sevs and alert.previous_severity not in sevs: LOG.debug('Ignored alert %s: severity or previous_severity does not matche the severities configuration (%s)', alertid, sevs) message.ack() @@ -177,7 +177,7 @@ class MailSender(threading.Thread): self._template_name_html = os.path.basename( OPTIONS['mail_template_html']) - super(MailSender, self).__init__() + super().__init__() def run(self): @@ -199,9 +199,10 @@ class MailSender(threading.Thread): if keep_alive >= 10: try: - origin = '{}/{}'.format('alerta-mailer', OPTIONS['smtp_host']) + origin = '{}/{}'.format('alerta-mailer', + OPTIONS['smtp_host']) api.heartbeat(origin, tags=[__version__]) - except Exception as e: + except Exception: time.sleep(5) continue keep_alive = 0 @@ -221,7 +222,7 @@ class MailSender(threading.Thread): return True LOG.debug('Regex %s matches nothing', regex) return False - elif isinstance(value, six.string_types): # pylint: disable=undefined-variable + elif isinstance(value, str): # pylint: disable=undefined-variable LOG.debug('Trying to match %s to %s', value, regex) return re.search(regex, value) is not None @@ -266,7 +267,7 @@ class MailSender(threading.Thread): ' adding for this rule only') del contacts[:] contacts.extend(new_contacts) - + # Don't loose time (and try to send an email) if there is no contact... if not contacts: return @@ -284,10 +285,7 @@ class MailSender(threading.Thread): text = self._template_env.get_template( self._template_name).render(**template_vars) - if ( - OPTIONS['email_type'] == 'html' and - self._template_name_html - ): + if OPTIONS['email_type'] == 'html' and self._template_name_html: html = self._template_env.get_template( self._template_name_html).render(**template_vars) else: @@ -296,7 +294,7 @@ class MailSender(threading.Thread): msg = MIMEMultipart('alternative') msg['Subject'] = Header(subject, 'utf-8').encode() msg['From'] = OPTIONS['mail_from'] - msg['To'] = ", ".join(contacts) + msg['To'] = ', '.join(contacts) msg.preamble = msg['Subject'] # by default we are going to assume that the email is going to be text @@ -308,15 +306,15 @@ class MailSender(threading.Thread): try: self._send_email_message(msg, contacts) - LOG.debug('%s : Email sent to %s' % (alert.get_id(), - ','.join(contacts))) + LOG.debug('{} : Email sent to {}'.format(alert.get_id(), + ','.join(contacts))) return (msg, contacts) except smtplib.SMTPException as e: LOG.error('Failed to send mail to %s on %s:%s : %s', - ", ".join(contacts), + ', '.join(contacts), OPTIONS['smtp_host'], OPTIONS['smtp_port'], e) return None - except (socket.error, socket.herror, socket.gaierror) as e: + except (OSError, socket.herror, socket.gaierror) as e: LOG.error('Mail server connection error: %s', e) return None except Exception as e: @@ -337,10 +335,10 @@ class MailSender(threading.Thread): msg['To'] = dest if OPTIONS['smtp_use_ssl']: mx = smtplib.SMTP_SSL(mxhost, - OPTIONS['smtp_port'], - local_hostname=OPTIONS['mail_localhost'], - keyfile=OPTIONS['ssl_key_file'], - certfile=OPTIONS['ssl_cert_file']) + OPTIONS['smtp_port'], + local_hostname=OPTIONS['mail_localhost'], + keyfile=OPTIONS['ssl_key_file'], + certfile=OPTIONS['ssl_cert_file']) else: mx = smtplib.SMTP(mxhost, OPTIONS['smtp_port'], @@ -356,10 +354,10 @@ class MailSender(threading.Thread): else: if OPTIONS['smtp_use_ssl']: mx = smtplib.SMTP_SSL(OPTIONS['smtp_host'], - OPTIONS['smtp_port'], - local_hostname=OPTIONS['mail_localhost'], - keyfile=OPTIONS['ssl_key_file'], - certfile=OPTIONS['ssl_cert_file']) + OPTIONS['smtp_port'], + local_hostname=OPTIONS['mail_localhost'], + keyfile=OPTIONS['ssl_key_file'], + certfile=OPTIONS['ssl_cert_file']) else: mx = smtplib.SMTP(OPTIONS['smtp_host'], OPTIONS['smtp_port'], @@ -428,7 +426,7 @@ def validate_rules(rules): def parse_group_rules(config_file): - rules_dir = "{}/alerta.rules.d".format(os.path.dirname(config_file)) + rules_dir = '{}/alerta.rules.d'.format(os.path.dirname(config_file)) LOG.debug('Looking for rules files in %s', rules_dir) if os.path.exists(rules_dir): rules_d = [] @@ -436,11 +434,11 @@ def parse_group_rules(config_file): for filename in files[2]: LOG.debug('Parsing %s', filename) try: - with open(os.path.join(files[0], filename), 'r') as f: + with open(os.path.join(files[0], filename)) as f: rules = validate_rules(json.load(f)) if rules is not None: rules_d.extend(rules) - except: + except Exception: LOG.exception('Could not parse file') return rules_d return () @@ -460,12 +458,12 @@ def main(): defopts = {k: str(v) if type(v) is bool else v for k, v in DEFAULT_OPTIONS.items()} # nopep8 config = RawConfigParser(defaults=defopts) - if os.path.exists("{}.d".format(config_file)): - config_path = "{}.d".format(config_file) + if os.path.exists('{}.d'.format(config_file)): + config_path = '{}.d'.format(config_file) config_list = [] for files in os.walk(config_path): for filename in files[2]: - config_list.append("{}/{}".format(config_path, filename)) + config_list.append('{}/{}'.format(config_path, filename)) config_list.append(os.path.expanduser(config_file)) config_file = config_list @@ -477,8 +475,8 @@ def main(): config.read(config_file) else: config.read(os.path.expanduser(config_file)) - except Exception as e: - LOG.warning("Problem reading configuration file %s - is this an ini file?", config_file) # nopep8 + except Exception: + LOG.warning('Problem reading configuration file %s - is this an ini file?', config_file) # nopep8 sys.exit(1) if config.has_section(CONFIG_SECTION): @@ -489,7 +487,8 @@ def main(): int: config.getint, float: config.getfloat, bool: config.getboolean, - list: lambda s, o: [e.strip() for e in config.get(s, o).split(',')] if len(config.get(s, o)) else [] + list: lambda s, o: [e.strip() for e in config.get( + s, o).split(',')] if len(config.get(s, o)) else [] } for opt in DEFAULT_OPTIONS: # Convert the options to the expected type @@ -500,7 +499,8 @@ def main(): OPTIONS['endpoint'] = os.environ.get('ALERTA_ENDPOINT') or OPTIONS['endpoint'] # nopep8 OPTIONS['key'] = os.environ.get('ALERTA_API_KEY') or OPTIONS['key'] - OPTIONS['smtp_username'] = os.environ.get('SMTP_USERNAME') or OPTIONS['smtp_username'] or OPTIONS['mail_from'] + OPTIONS['smtp_username'] = os.environ.get( + 'SMTP_USERNAME') or OPTIONS['smtp_username'] or OPTIONS['mail_from'] OPTIONS['smtp_password'] = os.environ.get('SMTP_PASSWORD') or OPTIONS['smtp_password'] # nopep8 if os.environ.get('DEBUG'): @@ -543,5 +543,6 @@ def main(): print(str(e)) sys.exit(1) + if __name__ == '__main__': main() diff --git a/integrations/mailer/requirements.txt b/integrations/mailer/requirements.txt index 76405bb..e07f58d 100644 --- a/integrations/mailer/requirements.txt +++ b/integrations/mailer/requirements.txt @@ -1,4 +1,4 @@ alerta>=5.0.2 +jinja2 kombu redis -jinja2 diff --git a/integrations/mailer/setup.py b/integrations/mailer/setup.py index f0bf41e..6415bce 100644 --- a/integrations/mailer/setup.py +++ b/integrations/mailer/setup.py @@ -5,7 +5,7 @@ import setuptools version = '5.2.1' setuptools.setup( - name="alerta-mailer", + name='alerta-mailer', version=version, description='Send emails from Alerta', url='https://github.com/alerta/alerta-contrib', @@ -29,7 +29,7 @@ setuptools.setup( 'alerta-mailer = mailer:main' ] }, - keywords="alerta monitoring mailer sendmail smtp", + keywords='alerta monitoring mailer sendmail smtp', classifiers=[ 'Topic :: System :: Monitoring', ] diff --git a/integrations/mailer/test_rules.py b/integrations/mailer/test_rules.py index 9adebbf..5358959 100644 --- a/integrations/mailer/test_rules.py +++ b/integrations/mailer/test_rules.py @@ -1,10 +1,10 @@ ''' Unit test definitions for all rules ''' -import pytest import mailer +import pytest from alertaclient.models.alert import Alert -from mock import MagicMock, patch, DEFAULT +from mock import DEFAULT, MagicMock, patch def test_rules_dont_exist(): @@ -13,7 +13,7 @@ def test_rules_dont_exist(): ''' with patch('mailer.os') as system_os: system_os.path.exists.return_value = False - res = mailer.parse_group_rules('config_file') + # res = mailer.parse_group_rules('config_file') system_os.path.exists.called_once_with('confile_file') # assert res is None @@ -57,38 +57,38 @@ TESTDOCS = [ ({}, False), ([], True), ([ - {"name": "invalid_no_fields", - "contacts": []} + {'name': 'invalid_no_fields', + 'contacts': []} ], False), ([ - {"name": "invalid_empty_fields", - "fields": [], - "contacts": []} + {'name': 'invalid_empty_fields', + 'fields': [], + 'contacts': []} ], False), ([ - {"name": "invalid_no_contacts", - "fields": [{"field": "resource", "regex": r"\d{4}"}]} + {'name': 'invalid_no_contacts', + 'fields': [{'field': 'resource', 'regex': r'\d{4}'}]} ], False), ([ - {"name": "invalid_no_field_on_fields", - "fields": [{"regex": r"\d{4}"}], - "contacts": []} + {'name': 'invalid_no_field_on_fields', + 'fields': [{'regex': r'\d{4}'}], + 'contacts': []} ], False), ([ - {"name": "invalid_fields_not_list", - "fields": {"regex": r"\d{4}"}, - "contacts": []} + {'name': 'invalid_fields_not_list', + 'fields': {'regex': r'\d{4}'}, + 'contacts': []} ], False), ([ - {"name": "invalid_no_fields_regex", - "fields": [{"field": "test"}], - "contacts": []} + {'name': 'invalid_no_fields_regex', + 'fields': [{'field': 'test'}], + 'contacts': []} ], False), ([ - {"name": "invalid_no_fields_regex", - "fields": [{"field": "tags", "regex": "atag"}], - "exclude": True, - "contacts": []} + {'name': 'invalid_no_fields_regex', + 'fields': [{'field': 'tags', 'regex': 'atag'}], + 'exclude': True, + 'contacts': []} ], True), ] @@ -108,9 +108,9 @@ def test_rules_validation(doc, is_valid): RULES_DATA = [ # ({'resource': 'server-1234', 'event': '5678'}, [], []), ({'resource': '1234', 'event': '5678'}, - [{"name": "Test1", - "fields": [{"field": "resource", "regex": r"(\w.*)?\d{4}"}], - "contacts": ["test@example.com"]}], + [{'name': 'Test1', + 'fields': [{'field': 'resource', 'regex': r'(\w.*)?\d{4}'}], + 'contacts': ['test@example.com']}], ['test@example.com']) ] @@ -144,7 +144,7 @@ def test_rule_matches_list(): regex.match.side_effect = [MagicMock(), None] assert mail_sender._rule_matches('regex', ['item1']) is True regex.match.assert_called_with('regex', 'item1') - assert mail_sender._rule_matches('regex', ['item2']) is False + assert mail_sender._rule_matches('regex', ['item2']) is False regex.match.assert_called_with('regex', 'item2') @@ -162,4 +162,3 @@ def test_rule_matches_string(): regex.search.assert_called_with('regex', 'value1') assert mail_sender._rule_matches('regex', 'value2') is False regex.search.assert_called_with('regex', 'value2') - diff --git a/integrations/opsgenie/README.md b/integrations/opsgenie/README.md index 088c4aa..15161c8 100644 --- a/integrations/opsgenie/README.md +++ b/integrations/opsgenie/README.md @@ -2,11 +2,11 @@ Set up OpsGenie with an OpsGenie Edge Connector integration. ================== -While the OpsGenie plugin provided by Alerta can send alerts to OpsGenie it does not allow OpsGenie to update Alerta. -Fortunately, OpsGenie has an edge connector we can install and configure to use some code to do this for us. +While the OpsGenie plugin provided by Alerta can send alerts to OpsGenie it does not allow OpsGenie to update Alerta. +Fortunately, OpsGenie has an edge connector we can install and configure to use some code to do this for us. -Set up OpsGenie Edge Connector (oec) +Set up OpsGenie Edge Connector (oec) ------------------ Log in to OpsGenie @@ -17,10 +17,10 @@ Set up OpsGenie Edge Connector (oec) - from OEC "alert is acknowledged" -> Alerta will ack the alert - from OEC "alert is closed" -> Alerta will close the alert - - from OEC "alert is unacknowledged" -> Alerta will unack the alert - - from OEC "a note is added" -> Alerta will add a note to the alert - - from OEC "A user executes assign ownership" -> Alerta will assign the alert - - from OEC "A user takes ownership" -> Alerta will assign the alert + - from OEC "alert is unacknowledged" -> Alerta will unack the alert + - from OEC "a note is added" -> Alerta will add a note to the alert + - from OEC "A user executes assign ownership" -> Alerta will assign the alert + - from OEC "A user takes ownership" -> Alerta will assign the alert Click "Add new action" and add whichever actions you desire from OEC. @@ -47,7 +47,7 @@ As mentioned all actions will be shown to be executed by the user you chose to a Install and configure OpsGenie Edge Connector on a host in your network. Alerta has been tested with OEC version 1.1.3 ------------------ - Some links to OpsGenie OEC documentation: + Some links to OpsGenie OEC documentation: [Installation docs for OEC provided by Atlassian](https://support.atlassian.com/opsgenie/docs/opsgenie-edge-connector-installation-packs/) @@ -110,10 +110,10 @@ Remove some things that OEC installs by default If Alerta is configured to send alerts to OpsGenie then OEC should get updates and be able to update alerts in Alerta from any of the OpsGenie interfaces (web/phone etc..) -Troubleshooting +Troubleshooting ------------------ - If alerts are not firing it could be due to the alert source not being set. This requires an update to the OpsGenie plugin that hasn't been accepted yet. - Including a line in the plugin to set the source from the config or a reasonable default should address this. + If alerts are not firing it could be due to the alert source not being set. This requires an update to the OpsGenie plugin that hasn't been accepted yet. + Including a line in the plugin to set the source from the config or a reasonable default should address this. ```OPSGENIE_ALERT_SOURCE = os.environ.get('OPSGENIE_ALERT_SOURCE') or app.config.get('OPSGENIE_ALERT_SOURCE', 'Alerta'``` @@ -131,8 +131,8 @@ Troubleshooting } ``` - This is useful for OpsGenie Edge Connector to not update ALL Edge connector integrations if you have more than one running in your env. This will send updates to Alerta when the - source was Alerta. So if JIRA is also integrated through OEC it won't be trying to send any updates to Alerta etc. + This is useful for OpsGenie Edge Connector to not update ALL Edge connector integrations if you have more than one running in your env. This will send updates to Alerta when the + source was Alerta. So if JIRA is also integrated through OEC it won't be trying to send any updates to Alerta etc.  diff --git a/integrations/opsgenie/oecAlertaExecutor.py b/integrations/opsgenie/oecAlertaExecutor.py index 32c6fd2..00321bf 100644 --- a/integrations/opsgenie/oecAlertaExecutor.py +++ b/integrations/opsgenie/oecAlertaExecutor.py @@ -3,15 +3,21 @@ import argparse import json import logging import sys + import requests parser = argparse.ArgumentParser() -parser.add_argument('-payload', '--queuePayload', help='Payload from queue', required=True) -parser.add_argument('-apiKey', '--apiKey', help='The apiKey of the integration', required=True) -parser.add_argument('-opsgenieUrl', '--opsgenieUrl', help='The url', required=True) +parser.add_argument('-payload', '--queuePayload', + help='Payload from queue', required=True) +parser.add_argument('-apiKey', '--apiKey', + help='The apiKey of the integration', required=True) +parser.add_argument('-opsgenieUrl', '--opsgenieUrl', + help='The url', required=True) parser.add_argument('-logLevel', '--logLevel', help='Log level', required=True) -parser.add_argument('-alertaApiUrl', '--alertaApiUrl', help='The url to do alerta api operations', required=True) -parser.add_argument('-alertaApiKey', '--alertaApiKey', help='The api key to do alerta api operations', required=True) +parser.add_argument('-alertaApiUrl', '--alertaApiUrl', + help='The url to do alerta api operations', required=True) +parser.add_argument('-alertaApiKey', '--alertaApiKey', + help='The api key to do alerta api operations', required=True) args = vars(parser.parse_args()) logging.basicConfig(stream=sys.stdout, level=args['logLevel']) @@ -20,11 +26,14 @@ LOG_PREFIX = 'oec_action' def do_alerta_things(alerta_api_target, alerta_headers, payload): try: - r = requests.put(alerta_api_target, json=payload, headers=alerta_headers, timeout=2) + r = requests.put(alerta_api_target, json=payload, + headers=alerta_headers, timeout=2) except Exception as e: - logging.error("{} - Error updating {}. Error: {}".format(LOG_PREFIX, alerta_api_target, e)) + logging.error( + '{} - Error updating {}. Error: {}'.format(LOG_PREFIX, alerta_api_target, e)) - logging.info('{} - Call to {} return status code: {}'.format(LOG_PREFIX, alerta_api_target, r.status_code)) + logging.info('{} - Call to {} return status code: {}'.format(LOG_PREFIX, + alerta_api_target, r.status_code)) return r.status_code @@ -32,7 +41,8 @@ def get_alert_status(alerta_api_target, alerta_headers): try: r = requests.get(alerta_api_target, headers=alerta_headers, timeout=2) except Exception as e: - logging.error("{} - Error getting {} : {}".format(LOG_PREFIX, alerta_api_target, e)) + logging.error( + '{} - Error getting {} : {}'.format(LOG_PREFIX, alerta_api_target, e)) contents = json.loads(r.content) cur_status = contents['alert'].get('status', None) @@ -45,111 +55,128 @@ def main(): queue_message_string = args['queuePayload'] queue_message = json.loads(queue_message_string) - action = queue_message["action"] - LOG_PREFIX = "[ {} ]".format(action) + action = queue_message['action'] + LOG_PREFIX = '[ {} ]'.format(action) - alert_id = queue_message["alert"]["alertId"] - origin = queue_message["alert"]["source"] - username = queue_message["alert"]["username"] - logging.debug("{} - Username is: {}, Origin is: {}".format(LOG_PREFIX, username, origin)) + alert_id = queue_message['alert']['alertId'] + origin = queue_message['alert']['source'] + username = queue_message['alert']['username'] + logging.debug( + '{} - Username is: {}, Origin is: {}'.format(LOG_PREFIX, username, origin)) alerta_url = args['alertaApiUrl'] - alerta_headers = {'Content-type': 'application/json', 'Authorization': 'Key {}'.format(args['alertaApiKey'])} + alerta_headers = {'Content-type': 'application/json', + 'Authorization': 'Key {}'.format(args['alertaApiKey'])} - logging.info("{} - Using Alerta URL : {}".format(LOG_PREFIX, alerta_url)) - logging.debug("{} - Message: {}".format(LOG_PREFIX, queue_message)) + logging.info('{} - Using Alerta URL : {}'.format(LOG_PREFIX, alerta_url)) + logging.debug('{} - Message: {}'.format(LOG_PREFIX, queue_message)) timeout = 300 # default timeout for connections to opsgenie api action_timeout = 7200 # default alerta action timeout - logging.info("{} - Will execute {} for alertId {}".format(LOG_PREFIX, action, alert_id)) + logging.info( + '{} - Will execute {} for alertId {}'.format(LOG_PREFIX, action, alert_id)) - action_map = {"Acknowledge": "ack", - "AddNote": "note", - "AssignOwnership": "assign", - "TakeOwnership": "assign", - "UnAcknowledge": "unack", - "Close": "close", - "Snooze": "shelve"} + action_map = {'Acknowledge': 'ack', + 'AddNote': 'note', + 'AssignOwnership': 'assign', + 'TakeOwnership': 'assign', + 'UnAcknowledge': 'unack', + 'Close': 'close', + 'Snooze': 'shelve'} if alert_id: - alert_api_url = "{}/v2/alerts/{}".format(args['opsgenieUrl'], alert_id) + alert_api_url = '{}/v2/alerts/{}'.format(args['opsgenieUrl'], alert_id) headers = { - "Content-Type": "application/json", - "Accept-Language": "application/json", - "Authorization": "GenieKey {}".format(args['apiKey']) + 'Content-Type': 'application/json', + 'Accept-Language': 'application/json', + 'Authorization': 'GenieKey {}'.format(args['apiKey']) } - alert_response = requests.get(alert_api_url, headers=headers, timeout=timeout) + alert_response = requests.get( + alert_api_url, headers=headers, timeout=timeout) if alert_response.status_code < 299 and alert_response.json()['data']: if action in action_map.keys() and origin == 'Alerta': - alias = queue_message["alert"]["alias"] - logging.info("{} - {} {} from {}".format(LOG_PREFIX, action, alias, username)) + alias = queue_message['alert']['alias'] + logging.info( + '{} - {} {} from {}'.format(LOG_PREFIX, action, alias, username)) alerta_action = action_map[action] # set default target and payload - alerta_api_target = "{}/{}/action".format(alerta_url, alias) - payload = {"action": alerta_action, "text": "{}d by {}.".format(action, username), "timeout": action_timeout} + alerta_api_target = '{}/{}/action'.format(alerta_url, alias) + payload = {'action': alerta_action, 'text': '{}d by {}.'.format( + action, username), 'timeout': action_timeout} # payload will change according to action and then fall through to the # default api call unless the alerta_api_target is set to None on its way down if action == 'Snooze': # snooze_end = queue_message["alert"]["snoozedUntil"] - snooze_end = queue_message["alert"]["snoozeEndDate"] + snooze_end = queue_message['alert']['snoozeEndDate'] # snooze_end = dt.fromtimestamp(int("{}".format(snooze_end)[:-3])) # < - datetime object # now = dt.fromtimestamp(dt.timestamp(dt.utcnow())) # snooze_seconds = int((snooze_end - now).total_seconds()) # if snooze_seconds > 0: # logging.info("{} - Snoozing for {} seconds".format(LOG_PREFIX, snooze_seconds)) - payload["text"] = "Shelved until: {} by {}".format(snooze_end, username) + payload['text'] = 'Shelved until: {} by {}'.format( + snooze_end, username) elif action == 'AddNote': # payload and target for notes is different than actions - alerta_api_target = "{}/{}/note".format(alerta_url, alias) + alerta_api_target = '{}/{}/note'.format(alerta_url, alias) # since we have one api key assigned to a default 'opsgenie' user # include the username with the note so we know who wrote it - payload = {"note": "{} Added by {}".format(queue_message["alert"]["note"], username)} + payload = {'note': '{} Added by {}'.format( + queue_message['alert']['note'], username)} elif action == 'AssignOwnership': # - owner = queue_message["alert"]["owner"] + owner = queue_message['alert']['owner'] # update the payload - payload["text"] = "Assigned to {} by {}".format(owner, username) + payload['text'] = 'Assigned to {} by {}'.format( + owner, username) elif action == 'TakeOwnership': # # open_payload = { "action": "open", "text": "transisition to open for assignment", "timeout": action_timeout } # do_alerta_things(alerta_api_target,open_payload) # update the payload - payload["text"] = "{} took ownership".format(username) - elif action == 'Acknowledge': # update the acked-by attribute too.. + payload['text'] = '{} took ownership'.format(username) + # update the acked-by attribute too.. + elif action == 'Acknowledge': # opsgenie does not send an action when an alert comes out of snooze # we will check the alert and if it has a 'shelved' status unshelve it # this is silly but the tags opgsgenie has are NOT the alert tags. # or I would just look at those # Get the alert so we can check the status - alert_url = "{}/{}".format(alerta_url, alias) + alert_url = '{}/{}'.format(alerta_url, alias) status = get_alert_status(alert_url, alerta_headers) if status == 'shelved': # unshelve the thing (default) # and then the normal action can run - unshelve_payload = {"action": "unshelve", "text": "Unshelved by {}.".format(username), "timeout": action_timeout} + unshelve_payload = {'action': 'unshelve', 'text': 'Unshelved by {}.'.format( + username), 'timeout': action_timeout} - do_alerta_things(alerta_api_target, alerta_headers, unshelve_payload) + do_alerta_things(alerta_api_target, + alerta_headers, unshelve_payload) # update the api target to None unshelving will put it back to Ack alerta_api_target = None # update the acked-by attribute - ack_by_payload = {"attributes": {"acked-by": username}} - ack_by_target = "{}/{}/attributes".format(alerta_url, alias) - do_alerta_things(ack_by_target, alerta_headers, ack_by_payload) + ack_by_payload = {'attributes': {'acked-by': username}} + ack_by_target = '{}/{}/attributes'.format( + alerta_url, alias) + do_alerta_things( + ack_by_target, alerta_headers, ack_by_payload) if alerta_api_target: # as long as none of the above set the # alerta_api_target to None we should do the original action - do_alerta_things(alerta_api_target, alerta_headers, payload) + do_alerta_things(alerta_api_target, + alerta_headers, payload) else: - logging.warning("{} - Alert with id [ {} ] does not exist in Opsgenie. It is probably deleted.".format(LOG_PREFIX, alert_id)) + logging.warning( + '{} - Alert with id [ {} ] does not exist in Opsgenie. It is probably deleted.'.format(LOG_PREFIX, alert_id)) else: - logging.warning("{} - Alert id was not sent in the payload. Ignoring.".format(LOG_PREFIX)) + logging.warning( + '{} - Alert id was not sent in the payload. Ignoring.'.format(LOG_PREFIX)) if __name__ == '__main__': diff --git a/integrations/opsweekly/alerta.php b/integrations/opsweekly/alerta.php index f32b7a1..da016c3 100644 --- a/integrations/opsweekly/alerta.php +++ b/integrations/opsweekly/alerta.php @@ -95,4 +95,3 @@ function doAlertaAPICall($path, $parameters, $alerta_baseurl, $alerta_apikey) { } } ?> - diff --git a/integrations/pinger/pinger.py b/integrations/pinger/pinger.py index 9a28155..2b27acd 100644 --- a/integrations/pinger/pinger.py +++ b/integrations/pinger/pinger.py @@ -1,14 +1,13 @@ - -import sys -import platform -import time -import subprocess -import threading -import Queue -import re import logging -import yaml +import platform +import re +import subprocess +import sys +import threading +import time +import Queue +import yaml from alertaclient.api import Client __version__ = '3.3.0' @@ -75,19 +74,23 @@ class WorkerThread(threading.Thread): environment, service, resource, retries, queue_time = item if time.time() - queue_time > LOOP_EVERY: - LOG.warning('Ping request to %s expired after %d seconds.', resource, int(time.time() - queue_time)) + LOG.warning('Ping request to %s expired after %d seconds.', + resource, int(time.time() - queue_time)) self.queue.task_done() continue LOG.info('%s pinging %s...', self.getName(), resource) if retries > 1: - rc, rtt, loss, stdout = self.pinger(resource, count=2, timeout=5) + rc, rtt, loss, stdout = self.pinger( + resource, count=2, timeout=5) else: - rc, rtt, loss, stdout = self.pinger(resource, count=5, timeout=PING_MAX_TIMEOUT) + rc, rtt, loss, stdout = self.pinger( + resource, count=5, timeout=PING_MAX_TIMEOUT) if rc != PING_OK and retries: LOG.info('Retrying ping %s %s more times', resource, retries) - self.queue.put((environment, service, resource, retries - 1, time.time())) + self.queue.put((environment, service, resource, + retries - 1, time.time())) self.queue.task_done() continue @@ -96,15 +99,18 @@ class WorkerThread(threading.Thread): if avg > PING_SLOW_CRITICAL: event = 'PingSlow' severity = 'critical' - text = 'Node responded to ping in %s ms avg (> %s ms)' % (avg, PING_SLOW_CRITICAL) + text = 'Node responded to ping in {} ms avg (> {} ms)'.format( + avg, PING_SLOW_CRITICAL) elif avg > PING_SLOW_WARNING: event = 'PingSlow' severity = 'warning' - text = 'Node responded to ping in %s ms avg (> %s ms)' % (avg, PING_SLOW_WARNING) + text = 'Node responded to ping in {} ms avg (> {} ms)'.format( + avg, PING_SLOW_WARNING) else: event = 'PingOK' severity = 'normal' - text = 'Node responding to ping avg/max %s/%s ms.' % tuple(rtt) + text = 'Node responding to ping avg/max %s/%s ms.' % tuple( + rtt) value = '%s/%s ms' % tuple(rtt) elif rc == PING_FAILED: event = 'PingFailed' @@ -156,22 +162,26 @@ class WorkerThread(threading.Thread): if timeout > PING_MAX_TIMEOUT: timeout = PING_MAX_TIMEOUT - if sys.platform == "darwin": - cmd = "ping -q -c %s -i %s -t %s %s" % (count, interval, timeout, node) + if sys.platform == 'darwin': + cmd = 'ping -q -c {} -i {} -t {} {}'.format( + count, interval, timeout, node) else: - cmd = "ping -q -c %s -i %s -w %s %s" % (count, interval, timeout, node) - ping = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + cmd = 'ping -q -c {} -i {} -w {} {}'.format( + count, interval, timeout, node) + ping = subprocess.Popen( + cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = ping.communicate()[0].rstrip('\n') rc = ping.returncode LOG.debug('Ping %s => %s (rc=%d)', cmd, stdout, rc) - m = re.search('(?P<loss>\d+(\.\d+)?)% packet loss', stdout) + m = re.search(r'(?P<loss>\d+(\.\d+)?)% packet loss', stdout) if m: loss = m.group('loss') else: loss = 'n/a' - m = re.search('(?P<min>\d+\.\d+)/(?P<avg>\d+\.\d+)/(?P<max>\d+\.\d+)/(?P<mdev>\d+\.\d+)\s+ms', stdout) + m = re.search( + r'(?P<min>\d+\.\d+)/(?P<avg>\d+\.\d+)/(?P<max>\d+\.\d+)/(?P<mdev>\d+\.\d+)\s+ms', stdout) if m: rtt = (float(m.group('avg')), float(m.group('max'))) else: @@ -185,7 +195,7 @@ class WorkerThread(threading.Thread): return rc, rtt, loss, stdout -class PingerDaemon(object): +class PingerDaemon: def __init__(self): @@ -222,7 +232,8 @@ class PingerDaemon(object): environment = p['environment'] service = p['service'] retries = p.get('retries', PING_MAX_RETRIES) - self.queue.put((environment, service, target, retries, time.time())) + self.queue.put( + (environment, service, target, retries, time.time())) LOG.debug('Send heartbeat...') try: @@ -250,5 +261,6 @@ def main(): pinger = PingerDaemon() pinger.run() + if __name__ == '__main__': main() diff --git a/integrations/pinger/setup.py b/integrations/pinger/setup.py index 6198155..ac55918 100644 --- a/integrations/pinger/setup.py +++ b/integrations/pinger/setup.py @@ -1,14 +1,13 @@ - -from setuptools import setup, find_packages +from setuptools import setup setup( - name="alerta-pinger", + name='alerta-pinger', version='3.3.0', - description="Alerta Pinger daemon", - license="MIT", - author="Nick Satterly", - author_email="nick.satterly@theguardian.com", - url="http://github.com/alerta/alerta-contrib", + description='Alerta Pinger daemon', + license='MIT', + author='Nick Satterly', + author_email='nick.satterly@theguardian.com', + url='http://github.com/alerta/alerta-contrib', py_modules=['pinger'], install_requires=[ 'alerta', @@ -19,7 +18,7 @@ setup( 'alerta-pinger = pinger:main' ] }, - keywords="alerta ping daemon", + keywords='alerta ping daemon', classifiers=[ 'Development Status :: 5 - Production/Stable', 'License :: OSI Approved :: MIT License', diff --git a/integrations/snmptrap/README.md b/integrations/snmptrap/README.md index a52d54b..60c684f 100644 --- a/integrations/snmptrap/README.md +++ b/integrations/snmptrap/README.md @@ -165,8 +165,8 @@ Snmptrap Format | $A | the hostname corresponding to the contents of the agent-addr field of the PDU, if available, | | | otherwise the contents of the agent-addr field of the PDU (v1 TRAPs only). | | $b | PDU source address (Note: this is not necessarily an IPv4 address) | -| $B | PDU source hostname if available, otherwise PDU source address (see note above) | -| $N | enterprise string | +| $B | PDU source hostname if available, otherwise PDU source address (see note above) | +| $N | enterprise string | | $O | oid as name or numbers | | $P | security information from the PDU (community name for v1/v2c, user and context for v3) | | $q | trap sub-type (numeric, in decimal) | diff --git a/integrations/snmptrap/handler.py b/integrations/snmptrap/handler.py index 10c01e3..beca30e 100644 --- a/integrations/snmptrap/handler.py +++ b/integrations/snmptrap/handler.py @@ -1,4 +1,3 @@ - import datetime import logging import os @@ -11,11 +10,12 @@ from alertaclient.api import Client __version__ = '5.0.0' -LOG = logging.getLogger("alerta.snmptrap") -logging.basicConfig(format="%(asctime)s - %(name)s: %(levelname)s - %(message)s", level=logging.DEBUG) +LOG = logging.getLogger('alerta.snmptrap') +logging.basicConfig( + format='%(asctime)s - %(name)s: %(levelname)s - %(message)s', level=logging.DEBUG) -class SnmpTrapHandler(object): +class SnmpTrapHandler: def __init__(self): @@ -37,7 +37,8 @@ class SnmpTrapHandler(object): LOG.debug('unicoded -> %s', data) try: - resource, event, correlate, trap_version, trapvars = self.parse_snmptrap(data) + resource, event, correlate, trap_version, trapvars = self.parse_snmptrap( + data) if resource and event: self.api.send_alert( resource=resource, @@ -50,9 +51,11 @@ class SnmpTrapHandler(object): service=['Network'], text=trapvars['$W'], event_type='snmptrapAlert', - attributes={'trapvars': {k.replace('$', '_'): v for k, v in trapvars.items()}}, + attributes={'trapvars': { + k.replace('$', '_'): v for k, v in trapvars.items()}}, tags=[trap_version], - create_time=datetime.datetime.strptime('%sT%s.000Z' % (trapvars['$x'], trapvars['$X']), '%Y-%m-%dT%H:%M:%S.%fZ'), + create_time=datetime.datetime.strptime('{}T{}.000Z'.format( + trapvars['$x'], trapvars['$X']), '%Y-%m-%dT%H:%M:%S.%fZ'), raw_data=data ) except Exception as e: @@ -107,7 +110,8 @@ class SnmpTrapHandler(object): trapvars['$' + str(idx)] = value # $n LOG.debug('$%s %s', str(idx), value) - trapvars['$q'] = trapvars['$q'].lstrip('.') # if numeric, remove leading '.' + trapvars['$q'] = trapvars['$q'].lstrip( + '.') # if numeric, remove leading '.' trapvars['$#'] = str(idx) LOG.debug('varbinds = %s', varbinds) @@ -132,7 +136,8 @@ class SnmpTrapHandler(object): trapvars['$O'] = 'egpNeighborLoss' elif trapvars['$w'] == '6': # enterpriseSpecific(6) if trapvars['$q'].isdigit(): # XXX - specific trap number was not decoded - trapvars['$O'] = '%s.0.%s' % (trapvars['$N'], trapvars['$q']) + trapvars['$O'] = '{}.0.{}'.format( + trapvars['$N'], trapvars['$q']) else: trapvars['$O'] = trapvars['$q'] @@ -161,7 +166,8 @@ class SnmpTrapHandler(object): trapvars['$O'] = trapvars['$2'] # SNMPv2-MIB::snmpTrapOID.0 LOG.debug('trapvars = %s', trapvars) - LOG.info('%s-Trap-PDU %s from %s at %s %s', trap_version, trapvars['$O'], trapvars['$B'], trapvars['$x'], trapvars['$X']) + LOG.info('%s-Trap-PDU %s from %s at %s %s', trap_version, + trapvars['$O'], trapvars['$B'], trapvars['$x'], trapvars['$X']) if trapvars['$B'] != '<UNKNOWN>': resource = trapvars['$B'] @@ -179,16 +185,17 @@ class SnmpTrapHandler(object): def main(): - LOG = logging.getLogger("alerta.snmptrap") + LOG = logging.getLogger('alerta.snmptrap') try: SnmpTrapHandler().run() except (SystemExit, KeyboardInterrupt): - LOG.info("Exiting alerta SNMP trapper.") + LOG.info('Exiting alerta SNMP trapper.') sys.exit(0) except Exception as e: LOG.error(e, exc_info=1) sys.exit(1) + if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/integrations/snmptrap/setup.py b/integrations/snmptrap/setup.py index b09b9c3..90b3313 100644 --- a/integrations/snmptrap/setup.py +++ b/integrations/snmptrap/setup.py @@ -3,7 +3,7 @@ import setuptools setuptools.setup( - name="alerta-snmptrap", + name='alerta-snmptrap', version='5.0.0', description='Alerta script for SNMP traps', url='https://github.com/alerta/alerta-contrib', @@ -21,7 +21,7 @@ setuptools.setup( 'alerta-snmptrap = handler:main' ] }, - keywords="alerta snmp trap monitoring", + keywords='alerta snmp trap monitoring', classifiers=[ 'Topic :: System :: Monitoring', ] diff --git a/integrations/sqs/alerta_sqs.py b/integrations/sqs/alerta_sqs.py index ab43b5b..34680d7 100755 --- a/integrations/sqs/alerta_sqs.py +++ b/integrations/sqs/alerta_sqs.py @@ -1,15 +1,13 @@ #!/usr/bin/env python +import logging import os import sys import time -import logging - -from flask.config import Config import boto.sqs from boto.sqs.message import RawMessage -from boto import exception +from flask.config import Config LOG = logging.getLogger('alerta.sqs') @@ -20,13 +18,17 @@ config.from_envvar('ALERTA_SVR_CONF_FILE', silent=True) DEFAULT_AWS_REGION = 'eu-west-1' DEFAULT_AWS_SQS_QUEUE = 'alerts' -AWS_REGION = os.environ.get('AWS_REGION') or config.get('AWS_REGION', DEFAULT_AWS_REGION) -AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') or config.get('AWS_ACCESS_KEY_ID') -AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') or config.get('AWS_SECRET_ACCESS_KEY') -AWS_SQS_QUEUE = os.environ.get('AWS_SQS_QUEUE') or config.get('AWS_SQS_QUEUE', DEFAULT_AWS_SQS_QUEUE) +AWS_REGION = os.environ.get('AWS_REGION') or config.get( + 'AWS_REGION', DEFAULT_AWS_REGION) +AWS_ACCESS_KEY_ID = os.environ.get( + 'AWS_ACCESS_KEY_ID') or config.get('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = os.environ.get( + 'AWS_SECRET_ACCESS_KEY') or config.get('AWS_SECRET_ACCESS_KEY') +AWS_SQS_QUEUE = os.environ.get('AWS_SQS_QUEUE') or config.get( + 'AWS_SQS_QUEUE', DEFAULT_AWS_SQS_QUEUE) -class Worker(object): +class Worker: def __init__(self): @@ -72,5 +74,6 @@ def main(): except (SystemExit, KeyboardInterrupt): sys.exit(0) + if __name__ == '__main__': main() diff --git a/integrations/sqs/setup.py b/integrations/sqs/setup.py index 7255201..5d90622 100644 --- a/integrations/sqs/setup.py +++ b/integrations/sqs/setup.py @@ -5,7 +5,7 @@ import setuptools version = '3.3.0' setuptools.setup( - name="alerta-sqs", + name='alerta-sqs', version=version, description='Alerta integration for AWS SQS', url='https://github.com/alerta/alerta-contrib', @@ -24,7 +24,7 @@ setuptools.setup( 'alerta-sqs = alerta_sqs:main' ] }, - keywords="alerta monitoring amazon sqs", + keywords='alerta monitoring amazon sqs', classifiers=[ 'Topic :: System :: Monitoring', ] diff --git a/integrations/supervisor/evlistener.py b/integrations/supervisor/evlistener.py index 096f32e..d9d4f55 100755 --- a/integrations/supervisor/evlistener.py +++ b/integrations/supervisor/evlistener.py @@ -1,13 +1,13 @@ #!/usr/bin/env python -import sys import json import platform +import sys from alertaclient.api import Client -class Listener(object): +class Listener: def wait(self): data = sys.stdin.readline() @@ -55,7 +55,8 @@ def main(): severity = 'normal' try: api.send_alert( - resource='%s:%s' % (platform.uname()[1], body['processname']), + resource='{}:{}'.format( + platform.uname()[1], body['processname']), environment='Production', service=['supervisord'], event=event, @@ -72,8 +73,10 @@ def main(): value='serial=%s' % headers['serial'], severity=severity, origin=headers['server'], - text='State changed from %s to %s.' % (body['from_state'], event), - raw_data='%s\n\n%s' % (json.dumps(headers), json.dumps(body)) + text='State changed from {} to {}.'.format( + body['from_state'], event), + raw_data='{}\n\n{}'.format( + json.dumps(headers), json.dumps(body)) ) except Exception as e: listener.log_stderr(e) @@ -81,5 +84,6 @@ def main(): else: listener.send_cmd('RESULT 2\nOK') + if __name__ == '__main__': main() diff --git a/integrations/syslog/setup.py b/integrations/syslog/setup.py index 010cc2d..6f3ba13 100644 --- a/integrations/syslog/setup.py +++ b/integrations/syslog/setup.py @@ -5,7 +5,7 @@ import setuptools version = '3.5.0' setuptools.setup( - name="alerta-syslog", + name='alerta-syslog', version=version, description='Alerta script for Syslog messages', url='https://github.com/alerta/alerta-contrib', @@ -23,7 +23,7 @@ setuptools.setup( 'alerta-syslog = syslogfwder:main' ] }, - keywords="alerta syslog monitoring", + keywords='alerta syslog monitoring', classifiers=[ 'Topic :: System :: Monitoring', ] diff --git a/integrations/syslog/syslogfwder.py b/integrations/syslog/syslogfwder.py index a9018d9..ef6d707 100644 --- a/integrations/syslog/syslogfwder.py +++ b/integrations/syslog/syslogfwder.py @@ -1,15 +1,13 @@ - -import os -import sys -import platform -import socket -import select -import re import logging +import os +import platform +import re +import select +import socket +import sys from alertaclient.api import Client - __version__ = '3.5.0' SYSLOG_TCP_PORT = int(os.environ.get('SYSLOG_TCP_PORT', 514)) @@ -17,59 +15,60 @@ SYSLOG_UDP_PORT = int(os.environ.get('SYSLOG_UDP_PORT', 514)) SYSLOG_FACILITY_NAMES = [ - "kern", - "user", - "mail", - "daemon", - "auth", - "syslog", - "lpr", - "news", - "uucp", - "cron", - "authpriv", - "ftp", - "ntp", - "audit", - "alert", - "clock", - "local0", - "local1", - "local2", - "local3", - "local4", - "local5", - "local6", - "local7" + 'kern', + 'user', + 'mail', + 'daemon', + 'auth', + 'syslog', + 'lpr', + 'news', + 'uucp', + 'cron', + 'authpriv', + 'ftp', + 'ntp', + 'audit', + 'alert', + 'clock', + 'local0', + 'local1', + 'local2', + 'local3', + 'local4', + 'local5', + 'local6', + 'local7' ] SYSLOG_SEVERITY_NAMES = [ - "emerg", - "alert", - "crit", - "err", - "warning", - "notice", - "info", - "debug" + 'emerg', + 'alert', + 'crit', + 'err', + 'warning', + 'notice', + 'info', + 'debug' ] SYSLOG_SEVERITY_MAP = { - "emerg": "critical", - "alert": "critical", - "crit": "major", - "err": "minor", - "warning": "warning", - "notice": "normal", - "info": "informational", - "debug": "debug", + 'emerg': 'critical', + 'alert': 'critical', + 'crit': 'major', + 'err': 'minor', + 'warning': 'warning', + 'notice': 'normal', + 'info': 'informational', + 'debug': 'debug', } if sys.version_info[0] >= 3: unicode = str - + + def priority_to_code(name): - return SYSLOG_SEVERITY_MAP.get(name, "unknown") + return SYSLOG_SEVERITY_MAP.get(name, 'unknown') def decode_priority(priority): @@ -77,13 +76,15 @@ def decode_priority(priority): level = priority & 7 return SYSLOG_FACILITY_NAMES[facility], SYSLOG_SEVERITY_NAMES[level] + LOOP_EVERY = 20 # seconds -LOG = logging.getLogger("alerta.syslog") -logging.basicConfig(format="%(asctime)s - %(name)s: %(levelname)s - %(message)s", level=logging.DEBUG) +LOG = logging.getLogger('alerta.syslog') +logging.basicConfig( + format='%(asctime)s - %(name)s: %(levelname)s - %(message)s', level=logging.DEBUG) -class SyslogDaemon(object): +class SyslogDaemon: def __init__(self): @@ -94,7 +95,7 @@ class SyslogDaemon(object): try: self.udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.udp.bind(('', SYSLOG_UDP_PORT)) - except socket.error as e: + except OSError as e: LOG.error('Syslog UDP error: %s', e) sys.exit(2) LOG.info('Listening on syslog port %s/udp' % SYSLOG_UDP_PORT) @@ -106,7 +107,7 @@ class SyslogDaemon(object): self.tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.tcp.bind(('', SYSLOG_TCP_PORT)) self.tcp.listen(5) - except socket.error as e: + except OSError as e: LOG.error('Syslog TCP error: %s', e) sys.exit(2) LOG.info('Listening on syslog port %s/tcp' % SYSLOG_TCP_PORT) @@ -119,19 +120,22 @@ class SyslogDaemon(object): while not self.shuttingdown: try: LOG.debug('Waiting for syslog messages...') - ip, op, rdy = select.select([self.udp, self.tcp], [], [], LOOP_EVERY) + ip, op, rdy = select.select( + [self.udp, self.tcp], [], [], LOOP_EVERY) if ip: for i in ip: if i == self.udp: data, addr = self.udp.recvfrom(4096) data = unicode(data, 'utf-8', errors='ignore') - LOG.debug('Syslog UDP data received from %s: %s', addr, data) + LOG.debug( + 'Syslog UDP data received from %s: %s', addr, data) if i == self.tcp: client, addr = self.tcp.accept() data = client.recv(4096) data = unicode(data, 'utf-8', errors='ignore') client.close() - LOG.debug('Syslog TCP data received from %s: %s', addr, data) + LOG.debug( + 'Syslog TCP data received from %s: %s', addr, data) alerts = self.parse_syslog(ip=addr[0], data=data) for alert in alerts: @@ -166,48 +170,54 @@ class SyslogDaemon(object): if not msg or 'last message repeated' in msg: continue - if re.match('<\d+>1', msg): + if re.match(r'<\d+>1', msg): # Parse RFC 5424 compliant message - m = re.match(r'<(\d+)>1 (\S+) (\S+) (\S+) (\S+) (\S+) (.*)', msg) + m = re.match( + r'<(\d+)>1 (\S+) (\S+) (\S+) (\S+) (\S+) (.*)', msg) if m: PRI = int(m.group(1)) - ISOTIMESTAMP = m.group(2) + # ISOTIMESTAMP = m.group(2) HOSTNAME = m.group(3) APPNAME = m.group(4) PROCID = m.group(5) MSGID = m.group(6) - TAG = '%s[%s] %s' % (APPNAME, PROCID, MSGID) + TAG = '{}[{}] {}'.format(APPNAME, PROCID, MSGID) MSG = m.group(7) - LOG.info("Parsed RFC 5424 message OK") + LOG.info('Parsed RFC 5424 message OK') else: - LOG.error("Could not parse RFC 5424 syslog message: %s", msg) + LOG.error( + 'Could not parse RFC 5424 syslog message: %s', msg) continue elif re.match(r'<(\d{1,3})>\S{3}\s', msg): # Parse RFC 3164 compliant message - m = re.match(r'<(\d{1,3})>\S{3}\s{1,2}\d?\d \d{2}:\d{2}:\d{2} (\S+)( (\S+):)? (.*)', msg) + m = re.match( + r'<(\d{1,3})>\S{3}\s{1,2}\d?\d \d{2}:\d{2}:\d{2} (\S+)( (\S+):)? (.*)', msg) if m: PRI = int(m.group(1)) HOSTNAME = m.group(2) TAG = m.group(4) MSG = m.group(5) - LOG.info("Parsed RFC 3164 message OK") + LOG.info('Parsed RFC 3164 message OK') else: - LOG.error("Could not parse RFC 3164 syslog message: %s", msg) + LOG.error( + 'Could not parse RFC 3164 syslog message: %s', msg) continue - elif re.match('<\d+>.*%[A-Z0-9_-]+', msg): + elif re.match(r'<\d+>.*%[A-Z0-9_-]+', msg): # Parse Cisco Syslog message - m = re.match('<(\d+)>.*(%([A-Z0-9_-]+)):? (.*)', msg) + m = re.match(r'<(\d+)>.*(%([A-Z0-9_-]+)):? (.*)', msg) if m: LOG.debug(m.groups()) PRI = int(m.group(1)) CISCO_SYSLOG = m.group(2) try: - CISCO_FACILITY, CISCO_SEVERITY, CISCO_MNEMONIC = m.group(3).split('-') + CISCO_FACILITY, CISCO_MNEMONIC = m.group( + 3).split('-') except ValueError as e: - LOG.error('Could not parse Cisco syslog - %s: %s', e, m.group(3)) - CISCO_FACILITY = CISCO_SEVERITY = CISCO_MNEMONIC = 'na' + LOG.error( + 'Could not parse Cisco syslog - %s: %s', e, m.group(3)) + CISCO_FACILITY = CISCO_MNEMONIC = 'na' TAG = CISCO_MNEMONIC MSG = m.group(4) @@ -218,27 +228,30 @@ class SyslogDaemon(object): try: socket.inet_aton(ip) (resource, _, _) = socket.gethostbyaddr(ip) - except (socket.error, socket.herror): + except (OSError, socket.herror): resource = ip - resource = '%s:%s' % (resource, CISCO_FACILITY) + resource = '{}:{}'.format(resource, CISCO_FACILITY) else: - LOG.error("Could not parse Cisco syslog message: %s", msg) + LOG.error('Could not parse Cisco syslog message: %s', msg) continue facility, level = decode_priority(PRI) # Defaults - event = event or '%s%s' % (facility.capitalize(), level.capitalize()) - resource = resource or '%s%s' % (HOSTNAME, ':' + TAG if TAG else '') + event = event or '{}{}'.format( + facility.capitalize(), level.capitalize()) + resource = resource or '{}{}'.format( + HOSTNAME, ':' + TAG if TAG else '') severity = priority_to_code(level) group = 'Syslog' value = level text = MSG environment = 'Production' service = ['Platform'] - tags = ['%s.%s' % (facility, level)] - correlate = ['%s%s' % (facility.capitalize(), s.capitalize()) for s in SYSLOG_SEVERITY_NAMES] + tags = ['{}.{}'.format(facility, level)] + correlate = ['{}{}'.format(facility.capitalize(), s.capitalize()) + for s in SYSLOG_SEVERITY_NAMES] raw_data = msg syslogAlert = { @@ -246,7 +259,7 @@ class SyslogDaemon(object): 'event': event, 'environment': environment, 'severity': severity, - 'correlate':correlate, + 'correlate': correlate, 'service': service, 'group': group, 'value': value, @@ -262,14 +275,13 @@ class SyslogDaemon(object): def main(): - LOG = logging.getLogger("alerta.syslog") + LOG = logging.getLogger('alerta.syslog') try: SyslogDaemon().run() except (SystemExit, KeyboardInterrupt): - LOG.info("Exiting alerta syslog.") + LOG.info('Exiting alerta syslog.') sys.exit(0) except Exception as e: LOG.error(e, exc_info=1) sys.exit(1) - diff --git a/integrations/urlmon/settings.py b/integrations/urlmon/settings.py index 432b54f..242a046 100644 --- a/integrations/urlmon/settings.py +++ b/integrations/urlmon/settings.py @@ -1,24 +1,22 @@ - # ENDPOINT = "http://10.0.2.2:8080" -ENDPOINT = "http://localhost:8080" +ENDPOINT = 'http://localhost:8080' API_KEY = None checks = [ { - "resource": "www.google.com", - "url": "http://www.google.com?q=foo#q=foo", - "environment": "Production", - "service": ["Google", "Search"], - "api_endpoint": "http://localhost:8080", - "api_key": None, + 'resource': 'www.google.com', + 'url': 'http://www.google.com?q=foo#q=foo', + 'environment': 'Production', + 'service': ['Google', 'Search'], + 'api_endpoint': 'http://localhost:8080', + 'api_key': None, }, { - "resource": "guardian-football", - "url": "https://www.guardian.co.uk/football", - "environment": "Production", - "service": ["theguardian.com", "Sport"], - "tags": ["football"], - "check_ssl": True + 'resource': 'guardian-football', + 'url': 'https://www.guardian.co.uk/football', + 'environment': 'Production', + 'service': ['theguardian.com', 'Sport'], + 'tags': ['football'], + 'check_ssl': True }, ] - diff --git a/integrations/urlmon/setup.py b/integrations/urlmon/setup.py index 8e2c07b..9b6c102 100644 --- a/integrations/urlmon/setup.py +++ b/integrations/urlmon/setup.py @@ -5,7 +5,7 @@ import setuptools version = '3.3.0' setuptools.setup( - name="alerta-urlmon", + name='alerta-urlmon', version=version, description='Alerta script for URL monitoring', url='https://github.com/alerta/alerta-contrib', @@ -23,7 +23,7 @@ setuptools.setup( 'alerta-urlmon = urlmon:main' ] }, - keywords="alerta url monitoring", + keywords='alerta url monitoring', classifiers=[ 'Topic :: System :: Monitoring', ] diff --git a/integrations/urlmon/urlmon.py b/integrations/urlmon/urlmon.py index cf5b30d..b1735d5 100644 --- a/integrations/urlmon/urlmon.py +++ b/integrations/urlmon/urlmon.py @@ -6,17 +6,20 @@ import queue import re import socket import ssl +import sys import threading +import time from http.server import BaseHTTPRequestHandler as BHRH from urllib.error import URLError # pylint: disable=no-name-in-module from urllib.parse import urlparse # pylint: disable=no-name-in-module -from urllib.request import build_opener, ProxyHandler, HTTPBasicAuthHandler, install_opener, Request, urlopen # pylint: disable=no-name-in-module +from urllib.request import ( # pylint: disable=no-name-in-module + HTTPBasicAuthHandler, ProxyHandler, Request, build_opener, install_opener, + urlopen) -import sys -import time +import settings from alertaclient.api import Client -HTTP_RESPONSES = dict([(k, v[0]) for k, v in list(BHRH.responses.items())]) +HTTP_RESPONSES = {k: v[0] for k, v in list(BHRH.responses.items())} # Add missing responses HTTP_RESPONSES[102] = 'Processing' @@ -43,7 +46,7 @@ _HTTP_ALERTS = [ __version__ = '3.3.0' LOOP_EVERY = 60 # seconds -#TARGET_FILE = 'urlmon.targets' # FIXME -- or settings.py ??? +# TARGET_FILE = 'urlmon.targets' # FIXME -- or settings.py ??? SERVER_THREADS = 20 SLOW_WARNING_THRESHOLD = 5000 # ms SLOW_CRITICAL_THRESHOLD = 10000 # ms @@ -51,10 +54,10 @@ MAX_TIMEOUT = 15000 # ms SSL_DAYS = 30 SSL_DAYS_PANIC = 7 -import settings -LOG = logging.getLogger("alerta.urlmon") -logging.basicConfig(format="%(asctime)s - %(name)s: %(levelname)s - %(message)s", level=logging.DEBUG) +LOG = logging.getLogger('alerta.urlmon') +logging.basicConfig( + format='%(asctime)s - %(name)s: %(levelname)s - %(message)s', level=logging.DEBUG) class WorkerThread(threading.Thread): @@ -116,42 +119,49 @@ class WorkerThread(threading.Thread): event = 'HttpResponseRegexOK' severity = 'normal' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d that matched "%s" in %dms' % (status, status_regex, rtt) + text = 'HTTP server responded with status code %d that matched "%s" in %dms' % ( + status, status_regex, rtt) else: event = 'HttpResponseRegexError' severity = 'major' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d that failed to match "%s"' % (status, status_regex) + text = 'HTTP server responded with status code %d that failed to match "%s"' % ( + status, status_regex) elif 100 <= status <= 199: event = 'HttpInformational' severity = 'normal' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d in %dms' % (status, rtt) + text = 'HTTP server responded with status code %d in %dms' % ( + status, rtt) elif 200 <= status <= 299: event = 'HttpResponseOK' severity = 'normal' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d in %dms' % (status, rtt) + text = 'HTTP server responded with status code %d in %dms' % ( + status, rtt) elif 300 <= status <= 399: event = 'HttpRedirection' severity = 'minor' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d in %dms' % (status, rtt) + text = 'HTTP server responded with status code %d in %dms' % ( + status, rtt) elif 400 <= status <= 499: event = 'HttpClientError' severity = 'minor' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d in %dms' % (status, rtt) + text = 'HTTP server responded with status code %d in %dms' % ( + status, rtt) elif 500 <= status <= 599: event = 'HttpServerError' severity = 'major' value = '%s (%d)' % (description, status) - text = 'HTTP server responded with status code %d in %dms' % (status, rtt) + text = 'HTTP server responded with status code %d in %dms' % ( + status, rtt) else: event = 'HttpUnknownError' @@ -177,7 +187,8 @@ class WorkerThread(threading.Thread): m = re.search(search_string, line) if m: found = True - LOG.debug("Regex: Found %s in %s", search_string, line) + LOG.debug('Regex: Found %s in %s', + search_string, line) break if not found: event = 'HttpContentError' @@ -191,9 +202,11 @@ class WorkerThread(threading.Thread): try: body = json.loads(body) except ValueError as e: - LOG.error('Could not evaluate rule %s: %s', rule, e) + LOG.error( + 'Could not evaluate rule %s: %s', rule, e) try: - eval(rule) # NOTE: assumes request body in variable called 'body' + # NOTE: assumes request body in variable called 'body' + eval(rule) except (SyntaxError, NameError, ZeroDivisionError) as e: LOG.error('Could not evaluate rule %s: %s', rule, e) except Exception as e: @@ -205,7 +218,7 @@ class WorkerThread(threading.Thread): value = 'Rule failed' text = 'Website available but rule evaluation failed (%s)' % rule - LOG.debug("URL: %s, Status: %s (%s), Round-Trip Time: %dms -> %s", + LOG.debug('URL: %s, Status: %s (%s), Round-Trip Time: %dms -> %s', check['url'], description, status, rtt, event) resource = check['resource'] @@ -215,7 +228,8 @@ class WorkerThread(threading.Thread): service = check['service'] text = text tags = check.get('tags', list()) - threshold_info = "%s : RT > %d RT > %d x %s" % (check['url'], warn_thold, crit_thold, check.get('count', 1)) + threshold_info = '%s : RT > %d RT > %d x %s' % ( + check['url'], warn_thold, crit_thold, check.get('count', 1)) try: local_api.send_alert( @@ -249,15 +263,18 @@ class WorkerThread(threading.Thread): conn.settimeout(3.0) conn.connect((domain, port)) ssl_info = conn.getpeercert() - days_left = datetime.datetime.strptime(ssl_info['notAfter'], ssl_date_fmt) - datetime.datetime.utcnow() + days_left = datetime.datetime.strptime( + ssl_info['notAfter'], ssl_date_fmt) - datetime.datetime.utcnow() if days_left < datetime.timedelta(days=0): text = 'HTTPS cert for %s expired' % check['resource'] severity = 'critical' elif days_left < datetime.timedelta(days=SSL_DAYS) and days_left > datetime.timedelta(days=SSL_DAYS_PANIC): - text = 'HTTPS cert for %s will expire at %s' % (check['resource'], days_left) + text = 'HTTPS cert for {} will expire at {}'.format( + check['resource'], days_left) severity = 'major' elif days_left <= datetime.timedelta(days=SSL_DAYS_PANIC): - text = 'HTTPS cert for %s will expire at %s' % (check['resource'], days_left) + text = 'HTTPS cert for {} will expire at {}'.format( + check['resource'], days_left) severity = 'critical' else: severity = 'normal' @@ -363,7 +380,7 @@ class WorkerThread(threading.Thread): return status, reason, body, rtt -class UrlmonDaemon(object): +class UrlmonDaemon: def __init__(self): @@ -395,7 +412,8 @@ class UrlmonDaemon(object): LOG.debug('Send heartbeat...') try: origin = '{}/{}'.format('urlmon', platform.uname()[1]) - self.api.heartbeat(origin, tags=[__version__], timeout=3600) + self.api.heartbeat( + origin, tags=[__version__], timeout=3600) except Exception as e: LOG.warning('Failed to send heartbeat: %s', e) @@ -431,17 +449,17 @@ class UrlmonDaemon(object): def main(): - LOG = logging.getLogger("alerta.urlmon") + LOG = logging.getLogger('alerta.urlmon') try: UrlmonDaemon().run() except Exception as e: LOG.error(e, exc_info=1) sys.exit(1) - except KeyboardInterrupt as e: - LOG.warning("Exiting alerta urlmon.") + except KeyboardInterrupt: + LOG.warning('Exiting alerta urlmon.') sys.exit(1) + if __name__ == '__main__': main() - diff --git a/plugins/alertops/README.md b/plugins/alertops/README.md index 5b80fd5..df04020 100644 --- a/plugins/alertops/README.md +++ b/plugins/alertops/README.md @@ -33,10 +33,10 @@ server configuration file or the environment variables: The `ALERTOPS_URL` variable is generated during integration configuration within the AlertOps console. This should be added to the server configuration file. ```python -PLUGINS = ['alertops'] +PLUGINS = ['alertops'] ALERTOPS_URL = '' # default="Not configured" ``` -The `DASHBOARD_URL` setting should be configured in the server configuration file to link pushover messages to the Alerta console through the AlertOps webhook: +The `DASHBOARD_URL` setting should be configured in the server configuration file to link pushover messages to the Alerta console through the AlertOps webhook: ```python DASHBOARD_URL = '' # default="Not Set" @@ -53,11 +53,10 @@ DASHBOARD_URL = 'https://try.alerta.io' References ---------- - * AlertOps Integration Docs: + * AlertOps Integration Docs: https://help.alertops.com/integrations/pre-built-integration-guides/alerta License ------- -Copyright (c) 2019 AlertOps. - +Copyright (c) 2019 AlertOps. diff --git a/plugins/alertops/alerta_alertops.py b/plugins/alertops/alerta_alertops.py index 2c2d971..9d8fc1c 100644 --- a/plugins/alertops/alerta_alertops.py +++ b/plugins/alertops/alerta_alertops.py @@ -1,16 +1,14 @@ import logging import os -import re -import requests -from alerta.exceptions import RejectException +import requests +# from alerta.exceptions import RejectException from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins') @@ -20,58 +18,57 @@ DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config['DASHBOARD_URL'] class TriggerEvent(PluginBase): - def pre_receive(self, alert, **kwargs): return alert @staticmethod def _event_type(severity): if severity in ['cleared', 'normal', 'ok']: - return "close" + return 'close' else: - return "open" + return 'open' def post_receive(self, alert, **kwargs): if alert.repeat: - return + return + + message = '{}: {} alert for {} - {}'.format( + alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource) - message = "%s: %s alert for %s - %s" %( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource) - payload = { - "source_id": alert.id, - "source_status": TriggerEvent._event_type(alert.severity), - "description": message, - "resource": alert.resource, - "source": "alerta", - "source_url": '%s/#/alert/%s' % (DASHBOARD_URL, alert.id), - "details": alert.get_body(history=False)} + 'source_id': alert.id, + 'source_status': TriggerEvent._event_type(alert.severity), + 'description': message, + 'resource': alert.resource, + 'source': 'alerta', + 'source_url': '{}/#/alert/{}'.format(DASHBOARD_URL, alert.id), + 'details': alert.get_body(history=False)} LOG.debug('AlertOps Payload: %s', payload) try: r = requests.post(ALERTOPS_URL, json=payload, timeout=2) except Exception as e: - raise RuntimeError("AlertOps connection error: %s" % e) - LOG.debug('AlertOps response: %s - %s' % (r.status_code, r.text)) + raise RuntimeError('AlertOps connection error: %s' % e) + LOG.debug('AlertOps response: {} - {}'.format(r.status_code, r.text)) return def status_change(self, alert, status, text, **kwargs): if status not in ['ack', 'assign']: - return + return - message = "%s: %s alert for %s - %s" %( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource) + message = '{}: {} alert for {} - {}'.format( + alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource) payload = { - "source_id": alert.id, - "source_status": TriggerEvent._event_type(alert.severity), - "description": message, - "resource": alert.resource, - "source": "alerta", - "source_url": '%s/#/alert/%s' % (DASHBOARD_URL, alert.id), - "details": alert.get_body(history=False)} + 'source_id': alert.id, + 'source_status': TriggerEvent._event_type(alert.severity), + 'description': message, + 'resource': alert.resource, + 'source': 'alerta', + 'source_url': '{}/#/alert/{}'.format(DASHBOARD_URL, alert.id), + 'details': alert.get_body(history=False)} try: r = requests.post(ALERTOPS_URL, json=payload, timeout=2) except Exception as e: - raise RuntimeError("AlertOps connection error: %s" % e) - LOG.debug('AlertOps response: %s - %s' % (r.status_code, r.text)) - - + raise RuntimeError('AlertOps connection error: %s' % e) + LOG.debug('AlertOps response: {} - {}'.format(r.status_code, r.text)) diff --git a/plugins/alertops/setup.py b/plugins/alertops/setup.py index 9848413..9ec3ace 100644 --- a/plugins/alertops/setup.py +++ b/plugins/alertops/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '1.0.0.1' setup( - name="alerta-alertops", + name='alerta-alertops', version=version, description='Alerta plugin for AlertOps', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/amqp/alerta_amqp.py b/plugins/amqp/alerta_amqp.py index 284fcdb..a8bce65 100644 --- a/plugins/amqp/alerta_amqp.py +++ b/plugins/amqp/alerta_amqp.py @@ -1,7 +1,7 @@ - import logging import os +from alerta.plugins import PluginBase from kombu import BrokerConnection, Exchange, Producer from kombu.utils.debug import setup_logging @@ -9,7 +9,6 @@ try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.amqp') @@ -17,8 +16,10 @@ DEFAULT_AMQP_URL = 'mongodb://localhost:27017/kombu' DEFAULT_AMQP_TOPIC = 'notify' DEFAULT_AMQP_SEND_ALERT_HISTORY = True -AMQP_URL = os.environ.get('REDIS_URL') or os.environ.get('AMQP_URL') or app.config.get('AMQP_URL', DEFAULT_AMQP_URL) -AMQP_TOPIC = os.environ.get('AMQP_TOPIC') or app.config.get('AMQP_TOPIC', DEFAULT_AMQP_TOPIC) +AMQP_URL = os.environ.get('REDIS_URL') or os.environ.get( + 'AMQP_URL') or app.config.get('AMQP_URL', DEFAULT_AMQP_URL) +AMQP_TOPIC = os.environ.get('AMQP_TOPIC') or app.config.get( + 'AMQP_TOPIC', DEFAULT_AMQP_TOPIC) class FanoutPublisher(PluginBase): @@ -37,10 +38,11 @@ class FanoutPublisher(PluginBase): self.channel = self.connection.channel() self.exchange_name = AMQP_TOPIC - self.exchange = Exchange(name=self.exchange_name, type='fanout', channel=self.channel) + self.exchange = Exchange( + name=self.exchange_name, type='fanout', channel=self.channel) self.producer = Producer(exchange=self.exchange, channel=self.channel) - super(FanoutPublisher, self).__init__(name) + super().__init__(name) LOG.info('Configured fanout publisher on topic "%s"', AMQP_TOPIC) @@ -48,8 +50,10 @@ class FanoutPublisher(PluginBase): return alert def post_receive(self, alert, **kwargs): - LOG.info('Sending message %s to AMQP topic "%s"', alert.get_id(), AMQP_TOPIC) - body = alert.get_body(history=self.get_config('AMQP_SEND_ALERT_HISTORY', default=DEFAULT_AMQP_SEND_ALERT_HISTORY, type=bool, **kwargs)) + LOG.info('Sending message %s to AMQP topic "%s"', + alert.get_id(), AMQP_TOPIC) + body = alert.get_body(history=self.get_config( + 'AMQP_SEND_ALERT_HISTORY', default=DEFAULT_AMQP_SEND_ALERT_HISTORY, type=bool, **kwargs)) LOG.debug('Message: %s', body) self.producer.publish(body, declare=[self.exchange], retry=True) diff --git a/plugins/amqp/listener.py b/plugins/amqp/listener.py index bcd3fd1..c408191 100755 --- a/plugins/amqp/listener.py +++ b/plugins/amqp/listener.py @@ -29,7 +29,8 @@ class FanoutConsumer(ConsumerMixin): ) ] return [ - Consumer(queues=queues, accept=['json'], callbacks=[self.on_message]) + Consumer(queues=queues, accept=[ + 'json'], callbacks=[self.on_message]) ] def on_message(self, body, message): @@ -39,6 +40,7 @@ class FanoutConsumer(ConsumerMixin): print(str(e)) message.ack() + if __name__ == '__main__': from kombu.utils.debug import setup_logging setup_logging(loglevel='DEBUG', loggers=['']) diff --git a/plugins/amqp/setup.py b/plugins/amqp/setup.py index 9017e09..726391f 100644 --- a/plugins/amqp/setup.py +++ b/plugins/amqp/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.4.1' setup( - name="alerta-amqp", + name='alerta-amqp', version=version, description='Alerta plugin for AMQP messaging', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/cachet/alerta_cachet.py b/plugins/cachet/alerta_cachet.py index b5cabcc..af8d84a 100644 --- a/plugins/cachet/alerta_cachet.py +++ b/plugins/cachet/alerta_cachet.py @@ -1,21 +1,24 @@ - import json import logging import os +import cachetclient.cachet as cachet +from alerta.plugins import PluginBase + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase -import cachetclient.cachet as cachet LOG = logging.getLogger('alerta.plugins.cachet') -CACHET_API_URL = os.environ.get('CACHET_API_URL') or app.config['CACHET_API_URL'] -CACHET_API_TOKEN = os.environ.get('CACHET_API_TOKEN') or app.config['CACHET_API_TOKEN'] -CACHET_SSL_VERIFY = True if (os.environ.get('CACHET_SSL_VERIFY') == 'True' or app.config.get('CACHET_SSL_VERIFY', False)) else False +CACHET_API_URL = os.environ.get( + 'CACHET_API_URL') or app.config['CACHET_API_URL'] +CACHET_API_TOKEN = os.environ.get( + 'CACHET_API_TOKEN') or app.config['CACHET_API_TOKEN'] +CACHET_SSL_VERIFY = True if (os.environ.get( + 'CACHET_SSL_VERIFY') == 'True' or app.config.get('CACHET_SSL_VERIFY', False)) else False STATUS_MAP = { @@ -25,13 +28,15 @@ STATUS_MAP = { 'closed': 4 # Fixed } + class CachetIncident(PluginBase): def __init__(self, name=None): - self.incidents = cachet.Incidents(endpoint=CACHET_API_URL, api_token=CACHET_API_TOKEN, verify=CACHET_SSL_VERIFY) + self.incidents = cachet.Incidents( + endpoint=CACHET_API_URL, api_token=CACHET_API_TOKEN, verify=CACHET_SSL_VERIFY) - super(CachetIncident, self).__init__(name) + super().__init__(name) def pre_receive(self, alert): return alert @@ -42,14 +47,16 @@ class CachetIncident(PluginBase): status = STATUS_MAP[alert.status] message = alert.text - r = json.loads(self.incidents.get(name=name, message=message, status=status)) + r = json.loads(self.incidents.get( + name=name, message=message, status=status)) if r['meta']['pagination']['count']: return try: - r = json.loads(self.incidents.post(name=name, message=message, status=status, visible=True)) + r = json.loads(self.incidents.post( + name=name, message=message, status=status, visible=True)) except Exception as e: - raise RuntimeError("Cachet: ERROR - %s", e) + raise RuntimeError('Cachet: ERROR - %s', e) LOG.debug('Cachet: %s', r) diff --git a/plugins/cachet/setup.py b/plugins/cachet/setup.py index b0734db..05bc617 100644 --- a/plugins/cachet/setup.py +++ b/plugins/cachet/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.1' setup( - name="alerta-cachet", + name='alerta-cachet', version=version, description='Alerta plugin for Cachet status page', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/debug/alerta_debug.py b/plugins/debug/alerta_debug.py index 8e015e5..0d8b826 100644 --- a/plugins/debug/alerta_debug.py +++ b/plugins/debug/alerta_debug.py @@ -1,6 +1,6 @@ -import os import json import logging +import os from alerta.plugins import PluginBase @@ -19,12 +19,17 @@ class DebugTracing(PluginBase): DEBUG = self.get_config('DEBUG', default=False, type=bool, **kwargs) LOG.info('DEBUG=%s' % DEBUG) - BOOL_VAR = self.get_config('BOOL_VAR', default=False, type=bool, **kwargs) + BOOL_VAR = self.get_config( + 'BOOL_VAR', default=False, type=bool, **kwargs) INT_VAR = self.get_config('INT_VAR', default=0, type=int, **kwargs) - FLOAT_VAR = self.get_config('FLOAT_VAR', default=0.1, type=float, **kwargs) - LIST_VAR = self.get_config('LIST_VAR', default=['default', 'list'], type=list, **kwargs) - STR_VAR = self.get_config('STR_VAR', default='default-string', type=str, **kwargs) - DICT_VAR = self.get_config('DICT_VAR', default={'default': 'dict'}, type=json.loads, **kwargs) + FLOAT_VAR = self.get_config( + 'FLOAT_VAR', default=0.1, type=float, **kwargs) + LIST_VAR = self.get_config( + 'LIST_VAR', default=['default', 'list'], type=list, **kwargs) + STR_VAR = self.get_config( + 'STR_VAR', default='default-string', type=str, **kwargs) + DICT_VAR = self.get_config( + 'DICT_VAR', default={'default': 'dict'}, type=json.loads, **kwargs) LOG.debug('BOOL_VAR=%s' % BOOL_VAR) LOG.debug('INT_VAR=%s' % INT_VAR) diff --git a/plugins/debug/setup.py b/plugins/debug/setup.py index e90b98e..42c9669 100644 --- a/plugins/debug/setup.py +++ b/plugins/debug/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '7.0.0' setup( - name="alerta-debug", + name='alerta-debug', version=version, description='Alerta plugin for debug & tracing', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/dingtalk/README.md b/plugins/dingtalk/README.md index f64ba6f..f16ca04 100644 --- a/plugins/dingtalk/README.md +++ b/plugins/dingtalk/README.md @@ -52,6 +52,3 @@ DING_WEBHOOK_URL = 'https://oapi.dingtalk.com/robot/send?access_token=fc89e66e' WEBHOOK_MATCHERS = [ {"regex":"proxy[\\d+]", "webhook":"https://oapi.dingtalk.com/robot/send?access_token=f9216e240af"} ] DASHBOARD_URL = 'https://try.alerta.io' ``` - - - diff --git a/plugins/dingtalk/alerta_ding.py b/plugins/dingtalk/alerta_ding.py index b68c893..c1a7f9f 100644 --- a/plugins/dingtalk/alerta_ding.py +++ b/plugins/dingtalk/alerta_ding.py @@ -1,24 +1,22 @@ -from dingtalkchatbot.chatbot import DingtalkChatbot -import time -import json -import sys -import os import logging +import os + +from alerta.plugins import PluginBase +from dingtalkchatbot.chatbot import DingtalkChatbot try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase - LOG = logging.getLogger('alerta.plugins.ding') -DING_WEBHOOK_URL = os.environ.get('DING_WEBHOOK_URL') or app.config.get('DING_WEBHOOK_URL') -DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') - +DING_WEBHOOK_URL = os.environ.get( + 'DING_WEBHOOK_URL') or app.config.get('DING_WEBHOOK_URL') +DASHBOARD_URL = os.environ.get( + 'DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') class ServiceIntegration(PluginBase): @@ -30,9 +28,8 @@ class ServiceIntegration(PluginBase): def pre_receive(self, alert): return alert - def _prepare_payload(self, alert): - return "{}** **{}**\n`{}` ```{}```".format( + return '{}** **{}**\n`{}` ```{}```'.format( alert.severity, alert.environment, alert.event, @@ -40,8 +37,6 @@ class ServiceIntegration(PluginBase): ) LOG.debug('DingTalk: %s', alert) - - def post_receive(self, alert): if alert.repeat: return @@ -50,11 +45,7 @@ class ServiceIntegration(PluginBase): message = self._prepare_payload(alert) LOG.debug('DingTalk: %s', message) ding.send_text(msg='Received Alert {}'.format(message)) -#xiaoding.send_text(msg='next alert {}'.format(service_name_str)) + # xiaoding.send_text(msg='next alert {}'.format(service_name_str)) - - def status_change(self, alert, status, text): return - - diff --git a/plugins/dingtalk/dingtalkchatbot/__about__.py b/plugins/dingtalk/dingtalkchatbot/__about__.py index 13e0a02..b54e460 100644 --- a/plugins/dingtalk/dingtalkchatbot/__about__.py +++ b/plugins/dingtalk/dingtalkchatbot/__about__.py @@ -5,4 +5,4 @@ __version__ = '1.3.0' __author__ = 'devin' __author_email__ = '1324556701@qq.com' __license__ = 'MIT' -__cake__ = u'\u2728 \U0001f370 \u2728' \ No newline at end of file +__cake__ = '\u2728 \U0001f370 \u2728' diff --git a/plugins/dingtalk/dingtalkchatbot/chatbot.py b/plugins/dingtalk/dingtalkchatbot/chatbot.py index 3dce861..d1c7c74 100644 --- a/plugins/dingtalk/dingtalkchatbot/chatbot.py +++ b/plugins/dingtalk/dingtalkchatbot/chatbot.py @@ -1,12 +1,13 @@ #!/usr/bin/env python -# _*_ coding:utf-8 _*_ # create time: 07/01/2018 11:35 __author__ = 'Devin -- http://zhangchuzhao.site' import json -import time import logging +import time + import requests + try: JSONDecodeError = json.decoder.JSONDecodeError except AttributeError: @@ -34,16 +35,17 @@ def is_not_null_and_blank_str(content): return False -class DingtalkChatbot(object): +class DingtalkChatbot: """ 钉钉群自定义机器人(每个机器人每分钟最多发送20条),支持文本(text)、连接(link)、markdown三种消息类型! """ + def __init__(self, webhook): """ 机器人初始化 :param webhook: 钉钉群自定义机器人webhook地址 """ - super(DingtalkChatbot, self).__init__() + super().__init__() self.headers = {'Content-Type': 'application/json; charset=utf-8'} self.webhook = webhook self.times = 0 @@ -58,23 +60,23 @@ class DingtalkChatbot(object): :param at_dingtalk_ids: 被@人的dingtalkId(可选) :return: 返回消息发送结果 """ - data = {"msgtype": "text", "at": {}} + data = {'msgtype': 'text', 'at': {}} if is_not_null_and_blank_str(msg): - data["text"] = {"content": msg} + data['text'] = {'content': msg} else: - logging.error("text类型,消息内容不能为空!") - raise ValueError("text类型,消息内容不能为空!") + logging.error('text类型,消息内容不能为空!') + raise ValueError('text类型,消息内容不能为空!') if is_at_all: - data["at"]["isAtAll"] = is_at_all + data['at']['isAtAll'] = is_at_all if at_mobiles: at_mobiles = list(map(str, at_mobiles)) - data["at"]["atMobiles"] = at_mobiles + data['at']['atMobiles'] = at_mobiles if at_dingtalk_ids: at_dingtalk_ids = list(map(str, at_dingtalk_ids)) - data["at"]["atDingtalkIds"] = at_dingtalk_ids + data['at']['atDingtalkIds'] = at_dingtalk_ids logging.debug('text类型:%s' % data) return self.post(data) @@ -87,16 +89,16 @@ class DingtalkChatbot(object): """ if is_not_null_and_blank_str(pic_url): data = { - "msgtype": "image", - "image": { - "picURL": pic_url + 'msgtype': 'image', + 'image': { + 'picURL': pic_url } } logging.debug('image类型:%s' % data) return self.post(data) else: - logging.error("image类型中图片链接不能为空!") - raise ValueError("image类型中图片链接不能为空!") + logging.error('image类型中图片链接不能为空!') + raise ValueError('image类型中图片链接不能为空!') def send_link(self, title, text, message_url, pic_url=''): """ @@ -110,19 +112,19 @@ class DingtalkChatbot(object): """ if is_not_null_and_blank_str(title) and is_not_null_and_blank_str(text) and is_not_null_and_blank_str(message_url): data = { - "msgtype": "link", - "link": { - "text": text, - "title": title, - "picUrl": pic_url, - "messageUrl": message_url - } + 'msgtype': 'link', + 'link': { + 'text': text, + 'title': title, + 'picUrl': pic_url, + 'messageUrl': message_url + } } logging.debug('link类型:%s' % data) return self.post(data) else: - logging.error("link类型中消息标题或内容或链接不能为空!") - raise ValueError("link类型中消息标题或内容或链接不能为空!") + logging.error('link类型中消息标题或内容或链接不能为空!') + raise ValueError('link类型中消息标题或内容或链接不能为空!') def send_markdown(self, title, text, is_at_all=False, at_mobiles=[], at_dingtalk_ids=[]): """ @@ -136,29 +138,29 @@ class DingtalkChatbot(object): """ if is_not_null_and_blank_str(title) and is_not_null_and_blank_str(text): data = { - "msgtype": "markdown", - "markdown": { - "title": title, - "text": text + 'msgtype': 'markdown', + 'markdown': { + 'title': title, + 'text': text }, - "at": {} + 'at': {} } if is_at_all: - data["at"]["isAtAll"] = is_at_all + data['at']['isAtAll'] = is_at_all if at_mobiles: at_mobiles = list(map(str, at_mobiles)) - data["at"]["atMobiles"] = at_mobiles + data['at']['atMobiles'] = at_mobiles if at_dingtalk_ids: at_dingtalk_ids = list(map(str, at_dingtalk_ids)) - data["at"]["atDingtalkIds"] = at_dingtalk_ids + data['at']['atDingtalkIds'] = at_dingtalk_ids - logging.debug("markdown类型:%s" % data) + logging.debug('markdown类型:%s' % data) return self.post(data) else: - logging.error("markdown类型中消息标题或内容不能为空!") - raise ValueError("markdown类型中消息标题或内容不能为空!") + logging.error('markdown类型中消息标题或内容不能为空!') + raise ValueError('markdown类型中消息标题或内容不能为空!') def send_action_card(self, action_card): """ @@ -168,11 +170,11 @@ class DingtalkChatbot(object): """ if isinstance(action_card, ActionCard): data = action_card.get_data() - logging.debug("ActionCard类型:%s" % data) + logging.debug('ActionCard类型:%s' % data) return self.post(data) else: - logging.error("ActionCard类型:传入的实例类型不正确!") - raise TypeError("ActionCard类型:传入的实例类型不正确!") + logging.error('ActionCard类型:传入的实例类型不正确!') + raise TypeError('ActionCard类型:传入的实例类型不正确!') def send_feed_card(self, links): """ @@ -187,8 +189,8 @@ class DingtalkChatbot(object): if link_data_list: # 兼容:1、传入FeedLink或CardItem实例列表;2、传入数据字典列表; links = link_data_list - data = {"msgtype": "feedCard", "feedCard": {"links": links}} - logging.debug("FeedCard类型:%s" % data) + data = {'msgtype': 'feedCard', 'feedCard': {'links': links}} + logging.debug('FeedCard类型:%s' % data) return self.post(data) def post(self, data): @@ -206,38 +208,44 @@ class DingtalkChatbot(object): post_data = json.dumps(data) try: - response = requests.post(self.webhook, headers=self.headers, data=post_data) + response = requests.post( + self.webhook, headers=self.headers, data=post_data) except requests.exceptions.HTTPError as exc: - logging.error("消息发送失败, HTTP error: %d, reason: %s" % (exc.response.status_code, exc.response.reason)) + logging.error('消息发送失败, HTTP error: %d, reason: %s' % + (exc.response.status_code, exc.response.reason)) raise except requests.exceptions.ConnectionError: - logging.error("消息发送失败,HTTP connection error!") + logging.error('消息发送失败,HTTP connection error!') raise except requests.exceptions.Timeout: - logging.error("消息发送失败,Timeout error!") + logging.error('消息发送失败,Timeout error!') raise except requests.exceptions.RequestException: - logging.error("消息发送失败, Request Exception!") + logging.error('消息发送失败, Request Exception!') raise else: try: result = response.json() except JSONDecodeError: - logging.error("服务器响应异常,状态码:%s,响应内容:%s" % (response.status_code, response.text)) + logging.error('服务器响应异常,状态码:%s,响应内容:%s' % + (response.status_code, response.text)) return {'errcode': 500, 'errmsg': '服务器响应异常'} else: logging.debug('发送结果:%s' % result) if result['errcode']: - error_data = {"msgtype": "text", "text": {"content": "钉钉机器人消息发送失败,原因:%s" % result['errmsg']}, "at": {"isAtAll": True}} - logging.error("消息发送失败,自动通知:%s" % error_data) - requests.post(self.webhook, headers=self.headers, data=json.dumps(error_data)) + error_data = {'msgtype': 'text', 'text': { + 'content': '钉钉机器人消息发送失败,原因:%s' % result['errmsg']}, 'at': {'isAtAll': True}} + logging.error('消息发送失败,自动通知:%s' % error_data) + requests.post(self.webhook, headers=self.headers, + data=json.dumps(error_data)) return result -class ActionCard(object): +class ActionCard: """ ActionCard类型消息格式(整体跳转、独立跳转) """ + def __init__(self, title, text, btns, btn_orientation=0, hide_avatar=0): """ ActionCard初始化 @@ -247,7 +255,7 @@ class ActionCard(object): :param btn_orientation: 0:按钮竖直排列,1:按钮横向排列(可选) :param hide_avatar: 0:正常发消息者头像,1:隐藏发消息者头像(可选) """ - super(ActionCard, self).__init__() + super().__init__() self.title = title self.text = text self.btn_orientation = btn_orientation @@ -269,39 +277,40 @@ class ActionCard(object): if len(self.btns) == 1: # 整体跳转ActionCard类型 data = { - "msgtype": "actionCard", - "actionCard": { - "title": self.title, - "text": self.text, - "hideAvatar": self.hide_avatar, - "btnOrientation": self.btn_orientation, - "singleTitle": self.btns[0]["title"], - "singleURL": self.btns[0]["actionURL"] - } + 'msgtype': 'actionCard', + 'actionCard': { + 'title': self.title, + 'text': self.text, + 'hideAvatar': self.hide_avatar, + 'btnOrientation': self.btn_orientation, + 'singleTitle': self.btns[0]['title'], + 'singleURL': self.btns[0]['actionURL'] + } } return data else: # 独立跳转ActionCard类型 data = { - "msgtype": "actionCard", - "actionCard": { - "title": self.title, - "text": self.text, - "hideAvatar": self.hide_avatar, - "btnOrientation": self.btn_orientation, - "btns": self.btns + 'msgtype': 'actionCard', + 'actionCard': { + 'title': self.title, + 'text': self.text, + 'hideAvatar': self.hide_avatar, + 'btnOrientation': self.btn_orientation, + 'btns': self.btns } } return data else: - logging.error("ActionCard类型,消息标题或内容或按钮数量不能为空!") - raise ValueError("ActionCard类型,消息标题或内容或按钮数量不能为空!") + logging.error('ActionCard类型,消息标题或内容或按钮数量不能为空!') + raise ValueError('ActionCard类型,消息标题或内容或按钮数量不能为空!') -class FeedLink(object): +class FeedLink: """ FeedCard类型单条消息格式 """ + def __init__(self, title, message_url, pic_url): """ 初始化单条消息文本 @@ -309,7 +318,7 @@ class FeedLink(object): :param message_url: 点击单条信息后触发的URL :param pic_url: 点击单条消息后面图片触发的URL """ - super(FeedLink, self).__init__() + super().__init__() self.title = title self.message_url = message_url self.pic_url = pic_url @@ -321,17 +330,17 @@ class FeedLink(object): """ if is_not_null_and_blank_str(self.title) and is_not_null_and_blank_str(self.message_url) and is_not_null_and_blank_str(self.pic_url): data = { - "title": self.title, - "messageURL": self.message_url, - "picURL": self.pic_url + 'title': self.title, + 'messageURL': self.message_url, + 'picURL': self.pic_url } return data else: - logging.error("FeedCard类型单条消息文本、消息链接、图片链接不能为空!") - raise ValueError("FeedCard类型单条消息文本、消息链接、图片链接不能为空!") + logging.error('FeedCard类型单条消息文本、消息链接、图片链接不能为空!') + raise ValueError('FeedCard类型单条消息文本、消息链接、图片链接不能为空!') -class CardItem(object): +class CardItem: """ ActionCard和FeedCard消息类型中的子控件 """ @@ -343,7 +352,7 @@ class CardItem(object): @param url: 点击子控件时触发的URL @param pic_url: FeedCard的图片地址,ActionCard时不需要,故默认为None """ - super(CardItem, self).__init__() + super().__init__() self.title = title self.url = url self.pic_url = pic_url @@ -356,24 +365,25 @@ class CardItem(object): if is_not_null_and_blank_str(self.pic_url) and is_not_null_and_blank_str(self.title) and is_not_null_and_blank_str(self.url): # FeedCard类型 data = { - "title": self.title, - "messageURL": self.url, - "picURL": self.pic_url + 'title': self.title, + 'messageURL': self.url, + 'picURL': self.pic_url } return data elif is_not_null_and_blank_str(self.title) and is_not_null_and_blank_str(self.url): # ActionCard类型 data = { - "title": self.title, - "actionURL": self.url + 'title': self.title, + 'actionURL': self.url } return data else: - logging.error("CardItem是ActionCard的子控件时,title、url不能为空;是FeedCard的子控件时,title、url、pic_url不能为空!") - raise ValueError("CardItem是ActionCard的子控件时,title、url不能为空;是FeedCard的子控件时,title、url、pic_url不能为空!") + logging.error( + 'CardItem是ActionCard的子控件时,title、url不能为空;是FeedCard的子控件时,title、url、pic_url不能为空!') + raise ValueError( + 'CardItem是ActionCard的子控件时,title、url不能为空;是FeedCard的子控件时,title、url、pic_url不能为空!') if __name__ == '__main__': import doctest doctest.testmod() - diff --git a/plugins/dingtalk/dingtalkchatbot/chatbot_test.py b/plugins/dingtalk/dingtalkchatbot/chatbot_test.py index d7c78be..0530402 100644 --- a/plugins/dingtalk/dingtalkchatbot/chatbot_test.py +++ b/plugins/dingtalk/dingtalkchatbot/chatbot_test.py @@ -1,9 +1,9 @@ #!/usr/bin/env python -# _*_ coding:utf-8 _*_ # create time: 16/01/2018 14:37 import unittest -from dingtalkchatbot.chatbot import DingtalkChatbot, is_not_null_and_blank_str, ActionCard, FeedLink, CardItem +from dingtalkchatbot.chatbot import (ActionCard, CardItem, DingtalkChatbot, + FeedLink, is_not_null_and_blank_str) __author__ = 'Devin -- http://zhangchuzhao.site' @@ -31,26 +31,28 @@ class TestDingtalkChatbot(unittest.TestCase): def test_send_image(self): """测试发送表情图片消息函数""" - result = self.xiaoding.send_image(pic_url='http://uc-test-manage-00.umlife.net/jenkins/pic/flake8.png') + result = self.xiaoding.send_image( + pic_url='http://uc-test-manage-00.umlife.net/jenkins/pic/flake8.png') self.assertEqual(result['errcode'], 0) def test_send_link(self): """测试发送链接消息函数""" - result = self.xiaoding.send_link(title='万万没想到,某小璐竟然...', text='故事是这样子的...', message_url='http://www.kwongwah.com.my/?p=454748", pic_url="https://pbs.twimg.com/media/CEwj7EDWgAE5eIF.jpg') + result = self.xiaoding.send_link(title='万万没想到,某小璐竟然...', text='故事是这样子的...', + message_url='http://www.kwongwah.com.my/?p=454748", pic_url="https://pbs.twimg.com/media/CEwj7EDWgAE5eIF.jpg') self.assertEqual(result['errcode'], 0) def test_send_markdown(self): """测试发送Markdown格式消息函数""" result = self.xiaoding.send_markdown(title='氧气文字', text='#### 广州天气\n' - '> 9度,西北风1级,空气良89,相对温度73%\n\n' - '> \n' - '> ###### 10点20分发布 [天气](http://www.thinkpage.cn/) \n', + '> 9度,西北风1级,空气良89,相对温度73%\n\n' + '> \n' + '> ###### 10点20分发布 [天气](http://www.thinkpage.cn/) \n', is_at_all=True) self.assertEqual(result['errcode'], 0) def test_send_actioncard(self): """测试发送整体跳转ActionCard消息功能(CardItem新API)""" - btns1 = [CardItem(title="查看详情", url="https://www.dingtalk.com/")] + btns1 = [CardItem(title='查看详情', url='https://www.dingtalk.com/')] actioncard1 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns1, @@ -60,7 +62,8 @@ class TestDingtalkChatbot(unittest.TestCase): self.assertEqual(result['errcode'], 0) """测试发送单独跳转ActionCard消息功能""" - btns2 = [CardItem(title="支持", url="https://www.dingtalk.com/"), CardItem(title="反对", url="http://www.back china.com/news/2018/01/11/537468.html")] + btns2 = [CardItem(title='支持', url='https://www.dingtalk.com/'), CardItem( + title='反对', url='http://www.back china.com/news/2018/01/11/537468.html')] actioncard2 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns2, @@ -71,7 +74,7 @@ class TestDingtalkChatbot(unittest.TestCase): def test_send_actioncard_old_api(self): """测试发送整体跳转ActionCard消息功能(数据列表btns旧API)""" - btns1 = [{"title": "查看详情", "actionURL": "https://www.dingtalk.com/"}] + btns1 = [{'title': '查看详情', 'actionURL': 'https://www.dingtalk.com/'}] actioncard1 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns1, @@ -81,8 +84,8 @@ class TestDingtalkChatbot(unittest.TestCase): self.assertEqual(result['errcode'], 0) """测试发送单独跳转ActionCard消息功能""" - btns2 = [{"title": "支持", "actionURL": "https://www.dingtalk.com/"}, - {"title": "反对", "actionURL": "http://www.back china.com/news/2018/01/11/537468.html"}] + btns2 = [{'title': '支持', 'actionURL': 'https://www.dingtalk.com/'}, + {'title': '反对', 'actionURL': 'http://www.back china.com/news/2018/01/11/537468.html'}] actioncard2 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns2, @@ -93,18 +96,24 @@ class TestDingtalkChatbot(unittest.TestCase): def test_send_feedcard(self): """测试发送FeedCard类型消息功能(CardItem新API)""" - carditem1 = CardItem(title="氧气美女", url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") - carditem2 = CardItem(title="氧眼美女", url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") - carditem3 = CardItem(title="氧神美女", url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") + carditem1 = CardItem(title='氧气美女', url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') + carditem2 = CardItem(title='氧眼美女', url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') + carditem3 = CardItem(title='氧神美女', url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') cards = [carditem1, carditem2, carditem3] result = self.xiaoding.send_feed_card(cards) self.assertEqual(result['errcode'], 0) def test_send_feedcard_old_api(self): """测试发送FeedCard类型消息功能(FeedLink旧API)""" - feedlink1 = FeedLink(title="氧气美女", message_url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") - feedlink2 = FeedLink(title="氧眼美女", message_url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") - feedlink3 = FeedLink(title="氧神美女", message_url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") + feedlink1 = FeedLink(title='氧气美女', message_url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') + feedlink2 = FeedLink(title='氧眼美女', message_url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') + feedlink3 = FeedLink(title='氧神美女', message_url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') links = [feedlink1, feedlink2, feedlink3] result = self.xiaoding.send_feed_card(links) self.assertEqual(result['errcode'], 0) diff --git a/plugins/dingtalk/dingtalkchatbot/samples.py b/plugins/dingtalk/dingtalkchatbot/samples.py index 757e7bb..453af5a 100644 --- a/plugins/dingtalk/dingtalkchatbot/samples.py +++ b/plugins/dingtalk/dingtalkchatbot/samples.py @@ -1,11 +1,12 @@ #!/usr/bin/env python -# _*_ coding:utf-8 _*_ # create time: 15/01/2018 17:08 __author__ = 'Devin -- http://zhangchuzhao.site' -import json +import json # noqa: F401 import logging -import requests -from dingtalkchatbot.chatbot import DingtalkChatbot, ActionCard, FeedLink, CardItem + +import requests # noqa: F401 +from dingtalkchatbot.chatbot import FeedLink # noqa: F401 +from dingtalkchatbot.chatbot import ActionCard, CardItem, DingtalkChatbot logging.basicConfig(level=logging.DEBUG) @@ -13,7 +14,8 @@ if __name__ == '__main__': # *************************************这里填写自己钉钉群自定义机器人的token***************************************** webhook = 'https://oapi.dingtalk.com/robot/send?access_token=52d9034cc78680bc0d4ba6a65748e77fa7b96ee43d57b96116910606f7863d59' # 用户手机号列表 - at_mobiles = ['*************************这里填写需要提醒的用户的手机号码,字符串或数字都可以****************************'] + at_mobiles = [ + '*************************这里填写需要提醒的用户的手机号码,字符串或数字都可以****************************'] # 初始化机器人小丁 xiaoding = DingtalkChatbot(webhook) # text @@ -21,10 +23,12 @@ if __name__ == '__main__': xiaoding.send_text(msg='我就是小丁,小丁就是我!', at_mobiles=at_mobiles) # image表情 - xiaoding.send_image(pic_url='http://uc-test-manage-00.umlife.net/jenkins/pic/flake8.png') + xiaoding.send_image( + pic_url='http://uc-test-manage-00.umlife.net/jenkins/pic/flake8.png') # link - xiaoding.send_link(title='万万没想到,某小璐竟然...', text='故事是这样子的...', message_url='http://www.kwongwah.com.my/?p=454748", pic_url="https://pbs.twimg.com/media/CEwj7EDWgAE5eIF.jpg') + xiaoding.send_link(title='万万没想到,某小璐竟然...', text='故事是这样子的...', + message_url='http://www.kwongwah.com.my/?p=454748", pic_url="https://pbs.twimg.com/media/CEwj7EDWgAE5eIF.jpg') # markdown # 1、提醒所有人 @@ -41,7 +45,7 @@ if __name__ == '__main__': at_mobiles=at_mobiles) # 整体跳转ActionCard - btns1 = [CardItem(title="查看详情", url="https://www.dingtalk.com/")] + btns1 = [CardItem(title='查看详情', url='https://www.dingtalk.com/')] actioncard1 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns1, @@ -51,7 +55,8 @@ if __name__ == '__main__': # 单独跳转ActionCard # 1、两个按钮选择 - btns2 = [CardItem(title="支持", url="https://www.dingtalk.com/"), CardItem(title="反对", url="https://www.dingtalk.com/")] + btns2 = [CardItem(title='支持', url='https://www.dingtalk.com/'), + CardItem(title='反对', url='https://www.dingtalk.com/')] actioncard2 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns2, @@ -59,7 +64,8 @@ if __name__ == '__main__': hide_avatar=1) xiaoding.send_action_card(actioncard2) # 2、三个按钮选择 - btns3 = [CardItem(title="支持", url="https://www.dingtalk.com/"), CardItem(title="中立", url="https://www.dingtalk.com/"), CardItem(title="反对", url="https://www.dingtalk.com/")] + btns3 = [CardItem(title='支持', url='https://www.dingtalk.com/'), CardItem(title='中立', + url='https://www.dingtalk.com/'), CardItem(title='反对', url='https://www.dingtalk.com/')] actioncard3 = ActionCard(title='万万没想到,竟然...', text=' \n### 故事是这样子的...', btns=btns3, @@ -68,9 +74,12 @@ if __name__ == '__main__': xiaoding.send_action_card(actioncard3) # FeedCard类型 - card1 = CardItem(title="氧气美女", url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") - card2 = CardItem(title="氧眼美女", url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") - card3 = CardItem(title="氧神美女", url="https://www.dingtalk.com/", pic_url="https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg") + card1 = CardItem(title='氧气美女', url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') + card2 = CardItem(title='氧眼美女', url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') + card3 = CardItem(title='氧神美女', url='https://www.dingtalk.com/', + pic_url='https://unzippedtv.com/wp-content/uploads/sites/28/2016/02/asian.jpg') cards = [card1, card2, card3] xiaoding.send_feed_card(cards) diff --git a/plugins/dingtalk/setup.py b/plugins/dingtalk/setup.py index a4d4d9e..9dfdd26 100644 --- a/plugins/dingtalk/setup.py +++ b/plugins/dingtalk/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '0.0.1' setup( - name="alerta-ding", + name='alerta-ding', version=version, description='Example Alerta plugin for test alerts', url='https://github.com/alerta/alerta-contrib', @@ -21,4 +21,3 @@ setup( ] } ) - diff --git a/plugins/enhance/alerta_enhance.py b/plugins/enhance/alerta_enhance.py index efda878..df8bb1a 100644 --- a/plugins/enhance/alerta_enhance.py +++ b/plugins/enhance/alerta_enhance.py @@ -1,4 +1,3 @@ - import logging from alerta.plugins import PluginBase @@ -12,7 +11,7 @@ class EnhanceAlert(PluginBase): def pre_receive(self, alert): - LOG.info("Enhancing alert...") + LOG.info('Enhancing alert...') # Set "isOutOfHours" flag for later use by notification plugins dayOfWeek = alert.create_time.strftime('%a') @@ -23,7 +22,8 @@ class EnhanceAlert(PluginBase): alert.attributes['isOutOfHours'] = False # Add link to Run Book based on event name - alert.attributes['runBookUrl'] = '%s/%s' % (RUNBOOK_URL, alert.event.replace(' ', '-')) + alert.attributes['runBookUrl'] = '{}/{}'.format( + RUNBOOK_URL, alert.event.replace(' ', '-')) return alert diff --git a/plugins/enhance/setup.py b/plugins/enhance/setup.py index 9e98ca7..aa34e91 100644 --- a/plugins/enhance/setup.py +++ b/plugins/enhance/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.3' setup( - name="alerta-enhance", + name='alerta-enhance', version=version, description='Alerta plugin for alert enhancement', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/forward/alerta_forward.py b/plugins/forward/alerta_forward.py index e0bf523..a0f9ae2 100644 --- a/plugins/forward/alerta_forward.py +++ b/plugins/forward/alerta_forward.py @@ -1,5 +1,5 @@ -import os import logging +import os from alerta.plugins import PluginBase from alertaclient.api import Client @@ -18,6 +18,7 @@ FORWARD_API_KEY = os.environ.get( FORWARD_MAX_LENGTH = os.environ.get( 'FORWARD_MAX_LENGTH') or app.config.get('FORWARD_MAX_LENGTH') or 3 + class ForwardAlert(PluginBase): def pre_receive(self, alert): @@ -28,7 +29,7 @@ class ForwardAlert(PluginBase): return client = Client(endpoint=FORWARD_URL, key=FORWARD_API_KEY) fw_count = alert.attributes.get('fw_count') or 0 - fw_count = fw_count+1 + fw_count = fw_count + 1 if fw_count >= FORWARD_MAX_LENGTH: LOG.debug('alert discarded by cycle overflow') return diff --git a/plugins/forward/setup.py b/plugins/forward/setup.py index 5f0b07c..9ef9c31 100644 --- a/plugins/forward/setup.py +++ b/plugins/forward/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.0' setup( - name="alerta-forward", + name='alerta-forward', version=version, description='Alerta plugin for forwarding alerts', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/geoip/alerta_geoip.py b/plugins/geoip/alerta_geoip.py index e0abcb9..965966d 100644 --- a/plugins/geoip/alerta_geoip.py +++ b/plugins/geoip/alerta_geoip.py @@ -1,18 +1,20 @@ - import logging import os + import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.geoip') -GEOIP_URL = os.environ.get('GEOIP_URL') or app.config.get('GEOIP_URL', 'http://api.ipstack.com') -GEOIP_ACCESS_KEY = os.environ.get('GEOIP_ACCESS_KEY') or app.config.get('GEOIP_ACCESS_KEY', None) +GEOIP_URL = os.environ.get('GEOIP_URL') or app.config.get( + 'GEOIP_URL', 'http://api.ipstack.com') +GEOIP_ACCESS_KEY = os.environ.get( + 'GEOIP_ACCESS_KEY') or app.config.get('GEOIP_ACCESS_KEY', None) class GeoLocation(PluginBase): @@ -20,15 +22,18 @@ class GeoLocation(PluginBase): def pre_receive(self, alert): ip_addr = alert.attributes['ip'].split(', ')[0] - LOG.debug("GeoIP lookup for IP: %s", ip_addr) + LOG.debug('GeoIP lookup for IP: %s', ip_addr) if 'ip' in alert.attributes: - url = '%s/%s?access_key=%s' % (GEOIP_URL, ip_addr, GEOIP_ACCESS_KEY) + url = '{}/{}?access_key={}'.format(GEOIP_URL, + ip_addr, GEOIP_ACCESS_KEY) else: - LOG.warning("IP address must be included as an alert attribute.") - raise RuntimeWarning("IP address must be included as an alert attribute.") + LOG.warning('IP address must be included as an alert attribute.') + raise RuntimeWarning( + 'IP address must be included as an alert attribute.') - r = requests.get(url, headers={'Content-type': 'application/json'}, timeout=2) + r = requests.get( + url, headers={'Content-type': 'application/json'}, timeout=2) try: geoip_lookup = r.json() alert.attributes = { @@ -36,8 +41,8 @@ class GeoLocation(PluginBase): 'country': geoip_lookup['location'].get('country_flag_emoji') } except Exception as e: - LOG.error("GeoIP lookup failed: %s" % str(e)) - raise RuntimeError("GeoIP lookup failed: %s" % str(e)) + LOG.error('GeoIP lookup failed: %s' % str(e)) + raise RuntimeError('GeoIP lookup failed: %s' % str(e)) return alert diff --git a/plugins/geoip/setup.py b/plugins/geoip/setup.py index fb154d3..f803a10 100644 --- a/plugins/geoip/setup.py +++ b/plugins/geoip/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.4.0' setup( - name="alerta-geoip", + name='alerta-geoip', version=version, description='Alerta plugin for GeoIP Lookup', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/goalert/alerta_goalert.py b/plugins/goalert/alerta_goalert.py index c967744..d5ad2f2 100644 --- a/plugins/goalert/alerta_goalert.py +++ b/plugins/goalert/alerta_goalert.py @@ -1,42 +1,44 @@ import logging import os -import re + import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.goalert') LOG.info('Initializing') -GOALERT_URL = os.environ.get('GOALERT_URL') or app.config['GOALERT_URL'] +GOALERT_URL = os.environ.get('GOALERT_URL') or app.config['GOALERT_URL'] GOALERT_TOKEN = os.environ.get('GOALERT_TOKEN') or app.config['GOALERT_TOKEN'] -GOALERT_VERIFY = os.environ.get('GOALERT_VERIFY') or app.config['GOALERT_VERIFY'] +GOALERT_VERIFY = os.environ.get( + 'GOALERT_VERIFY') or app.config['GOALERT_VERIFY'] DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config['DASHBOARD_URL'] + class TriggerEvent(PluginBase): def goalerts_endpoint(self): - return '%s%s' % (GOALERT_URL, '/api/v2/generic/incoming') + return '{}{}'.format(GOALERT_URL, '/api/v2/generic/incoming') def goalert_close_alert(self, alert, why): closeUrl = self.goalerts_endpoint() json = { - "token": GOALERT_TOKEN, - "dedup": alert.id, - "action": "close", - "details": why + 'token': GOALERT_TOKEN, + 'dedup': alert.id, + 'action': 'close', + 'details': why } - LOG.debug('goalert close %s: %s %s' % (why, alert.id, closeUrl)) + LOG.debug('goalert close {}: {} {}'.format(why, alert.id, closeUrl)) try: r = requests.post(closeUrl, json, timeout=2, verify=GOALERT_VERIFY) except Exception as e: - raise RuntimeError("goalert connection error: %s" % e) + raise RuntimeError('goalert connection error: %s' % e) return r # def goalert_ack_alert(self, alert, why): @@ -54,7 +56,8 @@ class TriggerEvent(PluginBase): return alert def post_receive(self, alert): - LOG.debug('Alert receive %s: %s' % (alert.id, alert.get_body(history=False))) + LOG.debug('Alert receive %s: %s' % + (alert.id, alert.get_body(history=False))) if alert.repeat: LOG.debug('Alert repeating; ignored') return @@ -62,28 +65,31 @@ class TriggerEvent(PluginBase): if (alert.severity in ['cleared', 'normal', 'ok']) or (alert.status == 'closed'): r = self.goalert_close_alert(alert, 'CREATE-CLOSE') else: - body = alert.get_body(history=False) + # body = alert.get_body(history=False) json = { - "token": GOALERT_TOKEN, - "dedup": alert.id, - "summary": alert.resource, - "details": "[%s] %s: %s" % (alert.environment, alert.resource, alert.value) + 'token': GOALERT_TOKEN, + 'dedup': alert.id, + 'summary': alert.resource, + 'details': '[{}] {}: {}'.format(alert.environment, alert.resource, alert.value) } LOG.debug('goalert CREATE payload: %s' % json) endpoint = self.goalerts_endpoint() try: - r = requests.post(endpoint, json, timeout=2, verify=GOALERT_VERIFY) + r = requests.post(endpoint, json, timeout=2, + verify=GOALERT_VERIFY) except Exception as e: - raise RuntimeError("goalert connection error: %s" % e) + raise RuntimeError('goalert connection error: %s' % e) - LOG.debug('goalert response: %s - %s' % (r.status_code, r.text)) + LOG.debug('goalert response: {} - {}'.format(r.status_code, r.text)) def status_change(self, alert, status, text): - LOG.debug('Alert change %s to %s: %s' % (alert.id, status, alert.get_body(history=False))) + LOG.debug('Alert change %s to %s: %s' % + (alert.id, status, alert.get_body(history=False))) if status not in ['ack', 'assign', 'closed', 'expired']: - LOG.debug('Not sending status change to goalert: %s to %s' % (alert.id, status)) + LOG.debug('Not sending status change to goalert: %s to %s' % + (alert.id, status)) return if status == 'closed': @@ -93,4 +99,4 @@ class TriggerEvent(PluginBase): # elif status == 'ack': # r = self.goalert_ack_alert(alert, 'STATUS-ACK') - LOG.debug('goalert response: %s - %s' % (r.status_code, r.text)) + LOG.debug('goalert response: {} - {}'.format(r.status_code, r.text)) diff --git a/plugins/goalert/setup.py b/plugins/goalert/setup.py index 68d8e0f..4bfe477 100644 --- a/plugins/goalert/setup.py +++ b/plugins/goalert/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.3' setup( - name="alerta-goalert", + name='alerta-goalert', version=version, description='Alerta plugin for GoAlert', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/influxdb/alerta_influxdb.py b/plugins/influxdb/alerta_influxdb.py index d819cc9..5fe1bdf 100644 --- a/plugins/influxdb/alerta_influxdb.py +++ b/plugins/influxdb/alerta_influxdb.py @@ -1,27 +1,29 @@ - import logging import os - from datetime import datetime +from alerta.plugins import PluginBase +from influxdb import InfluxDBClient + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase -from influxdb import InfluxDBClient LOG = logging.getLogger('alerta.plugins.influxdb') # 'influxdb://username:password@localhost:8086/databasename' DEFAULT_INFLUXDB_DSN = 'influxdb://user:pass@localhost:8086/alerta' -INFLUXDB_DSN = os.environ.get('INFLUXDB_DSN') or app.config.get('INFLUXDB_DSN', DEFAULT_INFLUXDB_DSN) -INFLUXDB_DATABASE = os.environ.get('INFLUXDB_DATABASE') or app.config.get('INFLUXDB_DATABASE', None) +INFLUXDB_DSN = os.environ.get('INFLUXDB_DSN') or app.config.get( + 'INFLUXDB_DSN', DEFAULT_INFLUXDB_DSN) +INFLUXDB_DATABASE = os.environ.get( + 'INFLUXDB_DATABASE') or app.config.get('INFLUXDB_DATABASE', None) # Specify the name of a measurement to which all alerts will be logged -INFLUXDB_MEASUREMENT = os.environ.get('INFLUXDB_MEASUREMENT') or app.config.get('INFLUXDB_MEASUREMENT', 'event') +INFLUXDB_MEASUREMENT = os.environ.get( + 'INFLUXDB_MEASUREMENT') or app.config.get('INFLUXDB_MEASUREMENT', 'event') class InfluxDBWrite(PluginBase): @@ -38,7 +40,7 @@ class InfluxDBWrite(PluginBase): except Exception as e: LOG.error('InfluxDB: ERROR - %s' % e) - super(InfluxDBWrite, self).__init__(name) + super().__init__(name) def pre_receive(self, alert): return alert @@ -90,7 +92,7 @@ class InfluxDBWrite(PluginBase): try: self.client.write_points([point], time_precision='ms') except Exception as e: - raise RuntimeError("InfluxDB: ERROR - %s" % e) + raise RuntimeError('InfluxDB: ERROR - %s' % e) def status_change(self, alert, status, text): if status not in ['ack', 'assign']: @@ -102,4 +104,4 @@ class InfluxDBWrite(PluginBase): try: self.client.write_points([point], time_precision='ms') except Exception as e: - raise RuntimeError("InfluxDB: ERROR - %s" % e) + raise RuntimeError('InfluxDB: ERROR - %s' % e) diff --git a/plugins/influxdb/setup.py b/plugins/influxdb/setup.py index 31b27ed..df16414 100644 --- a/plugins/influxdb/setup.py +++ b/plugins/influxdb/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.4.3' setup( - name="alerta-influxdb", + name='alerta-influxdb', version=version, description='Alerta plugin for InfluxDB', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/jira/README.md b/plugins/jira/README.md index 7a51d94..726aa67 100644 --- a/plugins/jira/README.md +++ b/plugins/jira/README.md @@ -1,4 +1,4 @@ -Jira Plugin +Jira Plugin =========== Creates a task in Jira and adds the Jira Task attribute in alarm. The created attribute is a link to the Jira task and opens in a new tab. @@ -29,7 +29,7 @@ server configuration file or as environment variables. ```python PLUGINS = ['jira'] JIRA_PROJECT = '' #project name in jira -JIRA_URL = '' #url access to the jira +JIRA_URL = '' #url access to the jira JIRA_USER = '' #defined to the according to the jira access data JIRA_PASS = '' #defined to the according to the jira access data ``` diff --git a/plugins/jira/alerta_jira.py b/plugins/jira/alerta_jira.py index 65beec5..c13e131 100644 --- a/plugins/jira/alerta_jira.py +++ b/plugins/jira/alerta_jira.py @@ -1,14 +1,15 @@ -import logging -import os import http.client import json +import logging +import os from base64 import b64encode +from alerta.plugins import PluginBase + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase # set plugin logger LOG = logging.getLogger('alerta.plugins.jira') @@ -19,40 +20,41 @@ JIRA_PROJECT = app.config.get('JIRA_PROJECT') or os.environ.get('JIRA_PROJECT') JIRA_USER = app.config.get('JIRA_USER') or os.environ.get('JIRA_USER') JIRA_PASS = app.config.get('JIRA_PASS') or os.environ.get('JIRA_PASS') + class JiraCreate(PluginBase): def _sendjira(self, host, event, value, chart, text, severity): LOG.info('JIRA: Create task ...') - userpass = "%s:%s" %(JIRA_USER, JIRA_PASS) - userAndPass = b64encode(bytes(userpass, "utf-8")).decode("ascii") + userpass = '{}:{}'.format(JIRA_USER, JIRA_PASS) + userAndPass = b64encode(bytes(userpass, 'utf-8')).decode('ascii') LOG.debug('JIRA_URL: {}'.format(JIRA_URL)) - conn = http.client.HTTPSConnection("%s" %(JIRA_URL)) - + conn = http.client.HTTPSConnection('%s' % (JIRA_URL)) + payload_dict = { - "fields": { - "project": + 'fields': { + 'project': { - "key": "%s" %(JIRA_PROJECT) + 'key': '%s' % (JIRA_PROJECT) }, - "summary": "Server %s: alert %s in chart %s - Severity: %s" %(host.upper(), event.upper(), chart.upper(), severity.upper()), - "description": "The chart %s INFO: %s. \nVALUE: %s." %(chart, text, value), - "issuetype": { - "name": "Bug" + 'summary': 'Server {}: alert {} in chart {} - Severity: {}'.format(host.upper(), event.upper(), chart.upper(), severity.upper()), + 'description': 'The chart {} INFO: {}. \nVALUE: {}.'.format(chart, text, value), + 'issuetype': { + 'name': 'Bug' }, } } - payload = json.dumps(payload_dict, indent = 4) + payload = json.dumps(payload_dict, indent=4) headers = { - 'Content-Type': "application/json", - 'Authorization': "Basic %s" % userAndPass + 'Content-Type': 'application/json', + 'Authorization': 'Basic %s' % userAndPass } - conn.request("POST", '/rest/api/2/issue/', payload, headers) + conn.request('POST', '/rest/api/2/issue/', payload, headers) res = conn.getresponse() - data = res.read() + data = res.read() data = json.loads(data) - return data["key"] - + return data['key'] + # reject or modify an alert before it hits the database def pre_receive(self, alert): return alert @@ -62,26 +64,28 @@ class JiraCreate(PluginBase): try: # if the alert is critical and don't duplicate, create task in Jira if alert.status not in ['ack', 'closed', 'shelved'] and alert.duplicate_count == 0: - LOG.info("Jira: Received an alert") - LOG.debug("Jira: ALERT {}".format(alert)) - LOG.debug("Jira: ID {}".format(alert.id)) - LOG.debug("Jira: RESOURCE {}".format(alert.resource)) - LOG.debug("Jira: EVENT {}".format(alert.event)) - LOG.debug("Jira: SEVERITY {}".format(alert.severity)) - LOG.debug("Jira: TEXT {}".format(alert.text)) + LOG.info('Jira: Received an alert') + LOG.debug('Jira: ALERT {}'.format(alert)) + LOG.debug('Jira: ID {}'.format(alert.id)) + LOG.debug('Jira: RESOURCE {}'.format(alert.resource)) + LOG.debug('Jira: EVENT {}'.format(alert.event)) + LOG.debug('Jira: SEVERITY {}'.format(alert.severity)) + LOG.debug('Jira: TEXT {}'.format(alert.text)) # get basic info from alert host = alert.resource.split(':')[0] - LOG.debug("JIRA: HOST {}".format(host)) - chart = ".".join(alert.event.split('.')[:-1]) - LOG.debug("JIRA: CHART {}".format(chart)) + LOG.debug('JIRA: HOST {}'.format(host)) + chart = '.'.join(alert.event.split('.')[:-1]) + LOG.debug('JIRA: CHART {}'.format(chart)) event = alert.event.split('.')[-1] - LOG.debug("JIRA: EVENT {}".format(event)) + LOG.debug('JIRA: EVENT {}'.format(event)) # call the _sendjira and modify de text (discription) - task = self._sendjira(host, event, alert.value, chart, alert.text, alert.severity) - task_url = "https://" + JIRA_URL + "/browse/" + task - href = '<a href="%s" target="_blank">%s</a>' %(task_url, task) + task = self._sendjira( + host, event, alert.value, chart, alert.text, alert.severity) + task_url = 'https://' + JIRA_URL + '/browse/' + task + href = '<a href="{}" target="_blank">{}</a>'.format( + task_url, task) alert.attributes = {'Jira Task': href} return alert @@ -91,4 +95,4 @@ class JiraCreate(PluginBase): # triggered by external status changes, used by integrations def status_change(self, alert, status, text): - return \ No newline at end of file + return diff --git a/plugins/jira/setup.py b/plugins/jira/setup.py index 7fc8644..cf4d471 100644 --- a/plugins/jira/setup.py +++ b/plugins/jira/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '1.0.0' setup( - name="alerta-jira", + name='alerta-jira', version=version, description='Alerta plugin for create tasks in jira', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/logstash/alerta_logstash.py b/plugins/logstash/alerta_logstash.py index 2587a9c..8cdc179 100644 --- a/plugins/logstash/alerta_logstash.py +++ b/plugins/logstash/alerta_logstash.py @@ -1,14 +1,14 @@ - import json import logging import os import socket +from alerta.plugins import PluginBase + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.logstash') @@ -16,14 +16,17 @@ LOG = logging.getLogger('alerta.plugins.logstash') DEFAULT_LOGSTASH_HOST = 'localhost' DEFAULT_LOGSTASH_PORT = 6379 -LOGSTASH_HOST = os.environ.get('LOGSTASH_HOST') or app.config.get('LOGSTASH_HOST', DEFAULT_LOGSTASH_HOST) -LOGSTASH_PORT = os.environ.get('LOGSTASH_PORT') or app.config.get('LOGSTASH_PORT', DEFAULT_LOGSTASH_PORT) +LOGSTASH_HOST = os.environ.get('LOGSTASH_HOST') or app.config.get( + 'LOGSTASH_HOST', DEFAULT_LOGSTASH_HOST) +LOGSTASH_PORT = os.environ.get('LOGSTASH_PORT') or app.config.get( + 'LOGSTASH_PORT', DEFAULT_LOGSTASH_PORT) + class LogStashOutput(PluginBase): def __init__(self, name=None): self.sock = None - super(LogStashOutput, self).__init__(name) + super().__init__(name) def pre_receive(self, alert): return alert @@ -39,13 +42,14 @@ class LogStashOutput(PluginBase): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((LOGSTASH_HOST, logstash_port)) except Exception as e: - raise RuntimeError("Logstash TCP connection error: %s" % str(e)) + raise RuntimeError('Logstash TCP connection error: %s' % str(e)) try: - self.sock.send(b"%s\r\n" % json.dumps(alert.get_body(history=False)).encode('utf-8')) + self.sock.send(b'%s\r\n' % json.dumps( + alert.get_body(history=False)).encode('utf-8')) except Exception as e: LOG.exception(e) - raise RuntimeError("logstash exception") + raise RuntimeError('logstash exception') self.sock.close() diff --git a/plugins/logstash/setup.py b/plugins/logstash/setup.py index 841767c..2de702d 100644 --- a/plugins/logstash/setup.py +++ b/plugins/logstash/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.3' setup( - name="alerta-logstash", + name='alerta-logstash', version=version, description='Alerta plugin for ELK logstash', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/matrix/alerta_matrix.py b/plugins/matrix/alerta_matrix.py index 9d7043c..e75ebb8 100644 --- a/plugins/matrix/alerta_matrix.py +++ b/plugins/matrix/alerta_matrix.py @@ -1,37 +1,42 @@ +import json import logging import os -import requests import urllib -import json + +import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase -LOG = logging.getLogger("alerta.plugins.matrix") +LOG = logging.getLogger('alerta.plugins.matrix') MATRIX_HOMESERVER_URL = [ - os.environ.get("MATRIX_HOMESERVER") or app.config["MATRIX_HOMESERVER"], - "/_matrix/client/r0/rooms/", - urllib.parse.quote(os.environ.get("MATRIX_ROOM") or app.config["MATRIX_ROOM"], ":"), - "/send/m.room.message" + os.environ.get('MATRIX_HOMESERVER') or app.config['MATRIX_HOMESERVER'], + '/_matrix/client/r0/rooms/', + urllib.parse.quote(os.environ.get('MATRIX_ROOM') + or app.config['MATRIX_ROOM'], ':'), # noqa: W503 + '/send/m.room.message' ] -MATRIX_ACCESS_TOKEN = os.environ.get("MATRIX_ACCESS_TOKEN") or app.config["MATRIX_ACCESS_TOKEN"] -MATRIX_MESSAGE_TYPE = os.environ.get("MATRIX_MESSAGE_TYPE") or app.config.get("MATRIX_MESSAGE_TYPE", "notice") +MATRIX_ACCESS_TOKEN = os.environ.get( + 'MATRIX_ACCESS_TOKEN') or app.config['MATRIX_ACCESS_TOKEN'] +MATRIX_MESSAGE_TYPE = os.environ.get( + 'MATRIX_MESSAGE_TYPE') or app.config.get('MATRIX_MESSAGE_TYPE', 'notice') MATRIX_MESSAGE_TYPES = { - "notice": "m.notice", - "text": "m.text" + 'notice': 'm.notice', + 'text': 'm.text' } -DASHBOARD_URL = os.environ.get("DASHBOARD_URL") or app.config.get("DASHBOARD_URL", "") +DASHBOARD_URL = os.environ.get( + 'DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') SEVERITY_ICON = { - "critical": "🔴 ", - "warning": "⚠️ ", - "ok": "✅ ", - "cleared": "✅ ", - "normal": "✅ ", + 'critical': '🔴 ', + 'warning': '⚠️ ', + 'ok': '✅ ', + 'cleared': '✅ ', + 'normal': '✅ ', } @@ -44,13 +49,13 @@ class SendMessage(PluginBase): if alert.repeat: return - severity = SEVERITY_ICON.get(alert.severity, "") + severity = SEVERITY_ICON.get(alert.severity, '') - body = "{}{}: {} alert for {} \n{} - {} - {} \n{} \nDate: {}".format( + body = '{}{}: {} alert for {} \n{} - {} - {} \n{} \nDate: {}'.format( severity, alert.environment, alert.severity.capitalize(), - ",".join(alert.service), + ','.join(alert.service), alert.resource, alert.event, alert.value, @@ -62,7 +67,7 @@ class SendMessage(PluginBase): severity, alert.environment, alert.severity.capitalize(), - ",".join(alert.service), + ','.join(alert.service), alert.resource, alert.event, alert.value, @@ -73,25 +78,25 @@ class SendMessage(PluginBase): ) payload = { - "msgtype": MATRIX_MESSAGE_TYPES.get(MATRIX_MESSAGE_TYPE, "m.notice"), - "format": "org.matrix.custom.html", - "body": body, - "formatted_body": formatted_body, + 'msgtype': MATRIX_MESSAGE_TYPES.get(MATRIX_MESSAGE_TYPE, 'm.notice'), + 'format': 'org.matrix.custom.html', + 'body': body, + 'formatted_body': formatted_body, } - LOG.debug("Matrix: %s", payload) + LOG.debug('Matrix: %s', payload) try: r = requests.post( - "".join(MATRIX_HOMESERVER_URL), - headers={"Authorization": "Bearer " + MATRIX_ACCESS_TOKEN}, - data=json.dumps(payload).encode("utf-8"), + ''.join(MATRIX_HOMESERVER_URL), + headers={'Authorization': 'Bearer ' + MATRIX_ACCESS_TOKEN}, + data=json.dumps(payload).encode('utf-8'), timeout=2, ) except Exception as e: - raise RuntimeError("Matrix: ERROR - %s" % e) + raise RuntimeError('Matrix: ERROR - %s' % e) - LOG.debug("Matrix: %s - %s", r.status_code, r.text) + LOG.debug('Matrix: %s - %s', r.status_code, r.text) def status_change(self, alert, status, text): return diff --git a/plugins/matrix/setup.py b/plugins/matrix/setup.py index 0a9bf89..852fc09 100644 --- a/plugins/matrix/setup.py +++ b/plugins/matrix/setup.py @@ -1,19 +1,19 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup -version = "0.1.0" +version = '0.1.0' setup( - name="alerta-matrix", + name='alerta-matrix', version=version, - description="Alerta plugin for Matrix", - url="https://github.com/alerta/alerta-contrib", - license="MIT", - author="Magnus Walbeck", - author_email="mw@mwalbeck.org", + description='Alerta plugin for Matrix', + url='https://github.com/alerta/alerta-contrib', + license='MIT', + author='Magnus Walbeck', + author_email='mw@mwalbeck.org', packages=find_packages(), - py_modules=["alerta_matrix"], - install_requires=["requests"], + py_modules=['alerta_matrix'], + install_requires=['requests'], include_package_data=True, zip_safe=True, - entry_points={"alerta.plugins": ["matrix = alerta_matrix:SendMessage"]}, + entry_points={'alerta.plugins': ['matrix = alerta_matrix:SendMessage']}, ) diff --git a/plugins/mattermost/README.md b/plugins/mattermost/README.md index d531e76..f930efd 100644 --- a/plugins/mattermost/README.md +++ b/plugins/mattermost/README.md @@ -35,7 +35,7 @@ MATTERMOST_USERNAME = # '' default="alerta" Create a new incoming webhook in your Mattermost installation. See https://docs.mattermost.com/developer/webhooks-incoming.html -Templating +Templating ---------- No templating support at this time. Use hardcoded reasonable one. diff --git a/plugins/mattermost/alerta_mattermost.py b/plugins/mattermost/alerta_mattermost.py index 9c80a84..6ebb1ea 100644 --- a/plugins/mattermost/alerta_mattermost.py +++ b/plugins/mattermost/alerta_mattermost.py @@ -1,5 +1,7 @@ import logging import os + +from alerta.plugins import PluginBase from matterhook import Webhook try: @@ -7,7 +9,6 @@ try: except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.mattermost') @@ -42,7 +43,7 @@ class ServiceIntegration(PluginBase): def _prepare_payload(self, alert): LOG.debug('Mattermost: %s', alert) - return "{} **{}** **{}**\n`{}` ```{}```".format( + return '{} **{}** **{}**\n`{}` ```{}```'.format( self.get_icon(alert.severity), alert.severity, alert.environment, diff --git a/plugins/mattermost/setup.py b/plugins/mattermost/setup.py index 2d8595c..2008a08 100644 --- a/plugins/mattermost/setup.py +++ b/plugins/mattermost/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '1.1.3' setup( - name="alerta-mattermost", + name='alerta-mattermost', version=version, description='Alerta plugin for Mattermost', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/msteams/alerta_msteams.py b/plugins/msteams/alerta_msteams.py index 035b52a..b2e5f3b 100644 --- a/plugins/msteams/alerta_msteams.py +++ b/plugins/msteams/alerta_msteams.py @@ -1,31 +1,33 @@ - import json import logging import os -import requests + import pymsteams +import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.msteams') try: from jinja2 import Template except Exception as e: - LOG.error('MS Teams: ERROR - Jinja template error: %s, template functionality will be unavailable', e) + LOG.error( + 'MS Teams: ERROR - Jinja template error: %s, template functionality will be unavailable', e) MS_TEAMS_COLORS_MAP = app.config.get('MS_TEAMS_COLORS_MAP', {}) MS_TEAMS_DEFAULT_COLORS_MAP = {'security': '000000', - 'critical': 'D8122A', - 'major': 'EA680F', - 'minor': 'FFBE1E', - 'warning': '1E90FF'} + 'critical': 'D8122A', + 'major': 'EA680F', + 'minor': 'FFBE1E', + 'warning': '1E90FF'} MS_TEAMS_DEFAULT_COLOR = '00AA5A' -MS_TEAMS_DEFAULT_TIMEOUT = 7 # pymsteams http_timeout +MS_TEAMS_DEFAULT_TIMEOUT = 7 # pymsteams http_timeout + class SendConnectorCardMessage(PluginBase): @@ -34,12 +36,12 @@ class SendConnectorCardMessage(PluginBase): self._colors = MS_TEAMS_DEFAULT_COLORS_MAP self._colors.update(MS_TEAMS_COLORS_MAP) - super(SendConnectorCardMessage, self).__init__(name) + super().__init__(name) def _load_template(self, templateFmt): try: if os.path.exists(templateFmt): - with open(templateFmt, 'r') as f: + with open(templateFmt) as f: template = Template(f.read()) else: template = Template(templateFmt) @@ -52,19 +54,28 @@ class SendConnectorCardMessage(PluginBase): return alert def post_receive(self, alert, **kwargs): - MS_TEAMS_WEBHOOK_URL = self.get_config('MS_TEAMS_WEBHOOK_URL', default='', type=str, **kwargs) - MS_TEAMS_SUMMARY_FMT = self.get_config('MS_TEAMS_SUMMARY_FMT', default=None, type=str, **kwargs) # Message summary(title) format - MS_TEAMS_TEXT_FMT = self.get_config('MS_TEAMS_TEXT_FMT', default=None, type=str, **kwargs) # Message text format - MS_TEAMS_PAYLOAD = self.get_config('MS_TEAMS_PAYLOAD', default=None, type=str, **kwargs) # json/Jinja2 MS teams messagecard payload - MS_TEAMS_INBOUNDWEBHOOK_URL = self.get_config('MS_TEAMS_INBOUNDWEBHOOK_URL', default=None, type=str, **kwargs) # webhook url for connectorcard actions - MS_TEAMS_APIKEY = self.get_config('MS_TEAMS_APIKEY', default=None, type=str, **kwargs) # X-API-Key (needs webhook.write permission) - DASHBOARD_URL = self.get_config('DASHBOARD_URL', default='', type=str, **kwargs) + MS_TEAMS_WEBHOOK_URL = self.get_config( + 'MS_TEAMS_WEBHOOK_URL', default='', type=str, **kwargs) + MS_TEAMS_SUMMARY_FMT = self.get_config( + 'MS_TEAMS_SUMMARY_FMT', default=None, type=str, **kwargs) # Message summary(title) format + MS_TEAMS_TEXT_FMT = self.get_config( + 'MS_TEAMS_TEXT_FMT', default=None, type=str, **kwargs) # Message text format + # json/Jinja2 MS teams messagecard payload + MS_TEAMS_PAYLOAD = self.get_config( + 'MS_TEAMS_PAYLOAD', default=None, type=str, **kwargs) + MS_TEAMS_INBOUNDWEBHOOK_URL = self.get_config( + 'MS_TEAMS_INBOUNDWEBHOOK_URL', default=None, type=str, **kwargs) # webhook url for connectorcard actions + # X-API-Key (needs webhook.write permission) + MS_TEAMS_APIKEY = self.get_config( + 'MS_TEAMS_APIKEY', default=None, type=str, **kwargs) + DASHBOARD_URL = self.get_config( + 'DASHBOARD_URL', default='', type=str, **kwargs) if alert.repeat: return color = self._colors.get(alert.severity, MS_TEAMS_DEFAULT_COLOR) - url = "%s/#/alert/%s" % (DASHBOARD_URL, alert.id) + url = '{}/#/alert/{}'.format(DASHBOARD_URL, alert.id) template_vars = { 'alert': alert, @@ -75,7 +86,8 @@ class SendConnectorCardMessage(PluginBase): if MS_TEAMS_INBOUNDWEBHOOK_URL and MS_TEAMS_APIKEY: # Add X-API-Key header for teams(webhook) HttpPOST actions - template_vars['headers'] = '[ {{ "name": "X-API-Key", "value": "{}" }} ]'.format(MS_TEAMS_APIKEY) + template_vars['headers'] = '[ {{ "name": "X-API-Key", "value": "{}" }} ]'.format( + MS_TEAMS_APIKEY) template_vars['webhook_url'] = MS_TEAMS_INBOUNDWEBHOOK_URL if MS_TEAMS_PAYLOAD: @@ -87,7 +99,8 @@ class SendConnectorCardMessage(PluginBase): # Try to check that we've valid json json.loads(card_json) except Exception as e: - LOG.error('MS Teams: ERROR - Template(PAYLOAD) render failed: %s', e) + LOG.error( + 'MS Teams: ERROR - Template(PAYLOAD) render failed: %s', e) return else: # Use pymsteams to format/construct message card @@ -113,7 +126,8 @@ class SendConnectorCardMessage(PluginBase): try: text = txt_template.render(**template_vars) except Exception as e: - LOG.error('MS Teams: ERROR - Template(TEXT_FMT) render failed: %s', e) + LOG.error( + 'MS Teams: ERROR - Template(TEXT_FMT) render failed: %s', e) return else: text = alert.text @@ -123,19 +137,23 @@ class SendConnectorCardMessage(PluginBase): try: if MS_TEAMS_PAYLOAD: # Use requests.post to send raw json message card - LOG.debug("MS Teams sending(json payload) POST to %s", MS_TEAMS_WEBHOOK_URL) - r = requests.post(MS_TEAMS_WEBHOOK_URL, data=card_json, timeout=MS_TEAMS_DEFAULT_TIMEOUT) - LOG.debug('MS Teams response: %s / %s' % (r.status_code, r.text)) + LOG.debug('MS Teams sending(json payload) POST to %s', + MS_TEAMS_WEBHOOK_URL) + r = requests.post( + MS_TEAMS_WEBHOOK_URL, data=card_json, timeout=MS_TEAMS_DEFAULT_TIMEOUT) + LOG.debug('MS Teams response: %s / %s' % + (r.status_code, r.text)) else: # Use pymsteams to send card - msTeamsMessage = pymsteams.connectorcard(hookurl=MS_TEAMS_WEBHOOK_URL, http_timeout=MS_TEAMS_DEFAULT_TIMEOUT) + msTeamsMessage = pymsteams.connectorcard( + hookurl=MS_TEAMS_WEBHOOK_URL, http_timeout=MS_TEAMS_DEFAULT_TIMEOUT) msTeamsMessage.title(summary) msTeamsMessage.text(text) - msTeamsMessage.addLinkButton("Open in Alerta", url) + msTeamsMessage.addLinkButton('Open in Alerta', url) msTeamsMessage.color(color) msTeamsMessage.send() except Exception as e: - raise RuntimeError("MS Teams: ERROR - %s", e) + raise RuntimeError('MS Teams: ERROR - %s', e) def status_change(self, alert, status, text, **kwargs): return diff --git a/plugins/msteams/setup.py b/plugins/msteams/setup.py index 911e762..5910403 100644 --- a/plugins/msteams/setup.py +++ b/plugins/msteams/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.2.1' setup( - name="alerta-msteams", + name='alerta-msteams', version=version, description='Alerta plugin for Microsoft Teams', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/normalise/alerta_normalise.py b/plugins/normalise/alerta_normalise.py index 1af9d66..2e0e7bc 100644 --- a/plugins/normalise/alerta_normalise.py +++ b/plugins/normalise/alerta_normalise.py @@ -1,4 +1,3 @@ - import logging from alerta.plugins import PluginBase @@ -10,10 +9,10 @@ class NormaliseAlert(PluginBase): def pre_receive(self, alert): - LOG.info("Normalising alert...") + LOG.info('Normalising alert...') # prepend severity to alert text - alert.text = '%s: %s' % (alert.severity.upper(), alert.text) + alert.text = '{}: {}'.format(alert.severity.upper(), alert.text) # supply different default values if missing if not alert.group or alert.group == 'Misc': diff --git a/plugins/normalise/setup.py b/plugins/normalise/setup.py index 1e7eb3f..67379c9 100644 --- a/plugins/normalise/setup.py +++ b/plugins/normalise/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.1' setup( - name="alerta-normalise", + name='alerta-normalise', version=version, description='Alerta plugin for alert normalisation', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/op5/README.md b/plugins/op5/README.md index 160600f..dd3ea41 100644 --- a/plugins/op5/README.md +++ b/plugins/op5/README.md @@ -53,4 +53,4 @@ References License ------- -Copyright (c) 2017 Anton Delitsch. Available under the MIT License. \ No newline at end of file +Copyright (c) 2017 Anton Delitsch. Available under the MIT License. diff --git a/plugins/op5/alerta_op5.py b/plugins/op5/alerta_op5.py index 576791b..2fcc17b 100644 --- a/plugins/op5/alerta_op5.py +++ b/plugins/op5/alerta_op5.py @@ -1,23 +1,24 @@ - -import datetime import logging import os + +from alerta.plugins import PluginBase from op5 import OP5 try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.app import db -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.op5') DEFAULT_OP5_API_URL = 'https://demo.op5.com/api' -OP5_API_URL = os.environ.get('OP5_API_URL') or app.config.get('OP5_API_URL', None) -OP5_API_USERNAME = os.environ.get('OP5_API_USERNAME') or app.config.get('OP5_API_USERNAME', '') -OP5_API_PASSWORD = os.environ.get('OP5_API_PASSWORD') or app.config.get('OP5_API_PASSWORD', '') +OP5_API_URL = os.environ.get( + 'OP5_API_URL') or app.config.get('OP5_API_URL', None) +OP5_API_USERNAME = os.environ.get( + 'OP5_API_USERNAME') or app.config.get('OP5_API_USERNAME', '') +OP5_API_PASSWORD = os.environ.get( + 'OP5_API_PASSWORD') or app.config.get('OP5_API_PASSWORD', '') class OP5Acknowledge(PluginBase): @@ -37,14 +38,16 @@ class OP5Acknowledge(PluginBase): return if status == 'ack': - json_data = {"sticky":"1","notify":"0","persistent":"1","comment": text} + json_data = {'sticky': '1', 'notify': '0', + 'persistent': '1', 'comment': text} if alert.event_type == 'op5ServiceAlert': command_type = 'ACKNOWLEDGE_SVC_PROBLEM' - json_data["host_name"] = alert.resource - json_data["service_description"] = alert.event + json_data['host_name'] = alert.resource + json_data['service_description'] = alert.event if alert.event_type == 'op5HostAlert': command_type = 'ACKNOWLEDGE_HOST_PROBLEM' - json_data["host_name"] = alert.resource - - op5 = OP5(OP5_API_URL, OP5_API_USERNAME, OP5_API_PASSWORD, dryrun=False, debug=False, logtofile=False, interactive=False) + json_data['host_name'] = alert.resource + + op5 = OP5(OP5_API_URL, OP5_API_USERNAME, OP5_API_PASSWORD, + dryrun=False, debug=False, logtofile=False, interactive=False) op5.command(command_type, json_data) diff --git a/plugins/op5/setup.py b/plugins/op5/setup.py index a45d60e..1ac5331 100644 --- a/plugins/op5/setup.py +++ b/plugins/op5/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.2' setup( - name="alerta-op5", + name='alerta-op5', version=version, description='Alerta plugin for OP5 Monitor', url='https://github.com/alerta/alerta-contrib', @@ -14,7 +13,7 @@ setup( packages=find_packages(), py_modules=['alerta_op5'], install_requires=[ - "op5lib==1.0" + 'op5lib==1.0' ], include_package_data=True, zip_safe=True, diff --git a/plugins/opsgenie/alerta_opsgenie.py b/plugins/opsgenie/alerta_opsgenie.py index 0b18eaa..8b6be04 100644 --- a/plugins/opsgenie/alerta_opsgenie.py +++ b/plugins/opsgenie/alerta_opsgenie.py @@ -1,13 +1,14 @@ import logging import os import re + import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.opsgenie') LOG.info('Initializing') @@ -15,66 +16,78 @@ LOG.info('Initializing') OPSGENIE_EVENTS_CREATE_URL = 'https://api.opsgenie.com/v2/alerts' OPSGENIE_EVENTS_CLOSE_URL = 'https://api.opsgenie.com/v2/alerts/%s/close?identifierType=alias' OPSGENIE_EVENTS_ACK_URL = 'https://api.opsgenie.com/v2/alerts/%s/acknowledge?identifierType=alias' -OPSGENIE_SERVICE_KEY = os.environ.get('OPSGENIE_SERVICE_KEY') or app.config['OPSGENIE_SERVICE_KEY'] -OPSGENIE_TEAMS = os.environ.get('OPSGENIE_TEAMS', '') # comma separated list of teams -OPSGENIE_SEND_WARN = os.environ.get('OPSGENIE_SEND_WARN') or app.config.get('OPSGENIE_SEND_WARN', False) -SERVICE_KEY_MATCHERS = os.environ.get('SERVICE_KEY_MATCHERS') or app.config['SERVICE_KEY_MATCHERS'] -DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') -LOG.info('Initialized: %s key, %s matchers' % (OPSGENIE_SERVICE_KEY, SERVICE_KEY_MATCHERS)) +OPSGENIE_SERVICE_KEY = os.environ.get( + 'OPSGENIE_SERVICE_KEY') or app.config['OPSGENIE_SERVICE_KEY'] +# comma separated list of teams +OPSGENIE_TEAMS = os.environ.get('OPSGENIE_TEAMS', '') +OPSGENIE_SEND_WARN = os.environ.get( + 'OPSGENIE_SEND_WARN') or app.config.get('OPSGENIE_SEND_WARN', False) +SERVICE_KEY_MATCHERS = os.environ.get( + 'SERVICE_KEY_MATCHERS') or app.config['SERVICE_KEY_MATCHERS'] +DASHBOARD_URL = os.environ.get( + 'DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') +LOG.info('Initialized: %s key, %s matchers' % + (OPSGENIE_SERVICE_KEY, SERVICE_KEY_MATCHERS)) # when using with OpsGenie Edge connector setting a known source is useful -OPSGENIE_ALERT_SOURCE = os.environ.get('OPSGENIE_ALERT_SOURCE') or app.config.get('OPSGENIE_ALERT_SOURCE', 'Alerta') +OPSGENIE_ALERT_SOURCE = os.environ.get( + 'OPSGENIE_ALERT_SOURCE') or app.config.get('OPSGENIE_ALERT_SOURCE', 'Alerta') + class TriggerEvent(PluginBase): def opsgenie_service_key(self, resource): if not SERVICE_KEY_MATCHERS: - LOG.debug('No matchers defined! Default service key: %s' % (OPSGENIE_SERVICE_KEY)) + LOG.debug('No matchers defined! Default service key: %s' % + (OPSGENIE_SERVICE_KEY)) return OPSGENIE_SERVICE_KEY for mapping in SERVICE_KEY_MATCHERS: if re.match(mapping['regex'], resource): - LOG.debug('Matched regex: %s, service key: %s' % (mapping['regex'], mapping['api_key'])) + LOG.debug('Matched regex: %s, service key: %s' % + (mapping['regex'], mapping['api_key'])) return mapping['api_key'] - LOG.debug('No regex match! Default service key: %s' % (OPSGENIE_SERVICE_KEY)) + LOG.debug('No regex match! Default service key: %s' % + (OPSGENIE_SERVICE_KEY)) return OPSGENIE_SERVICE_KEY def opsgenie_close_alert(self, alert, why): headers = { - "Authorization": 'GenieKey ' + self.opsgenie_service_key(alert.resource) + 'Authorization': 'GenieKey ' + self.opsgenie_service_key(alert.resource) } closeUrl = OPSGENIE_EVENTS_CLOSE_URL % alert.id - LOG.debug('OpsGenie close %s: %s %s' % (why, alert.id, closeUrl)) + LOG.debug('OpsGenie close {}: {} {}'.format(why, alert.id, closeUrl)) try: r = requests.post(closeUrl, json={}, headers=headers, timeout=2) except Exception as e: - raise RuntimeError("OpsGenie connection error: %s" % e) + raise RuntimeError('OpsGenie connection error: %s' % e) return r def opsgenie_ack_alert(self, alert, why): headers = { - "Authorization": 'GenieKey ' + self.opsgenie_service_key(alert.resource) + 'Authorization': 'GenieKey ' + self.opsgenie_service_key(alert.resource) } ackUrl = OPSGENIE_EVENTS_ACK_URL % alert.id - LOG.debug('OpsGenie ack %s: %s %s' % (why, alert.id, ackUrl)) + LOG.debug('OpsGenie ack {}: {} {}'.format(why, alert.id, ackUrl)) try: r = requests.post(ackUrl, json={}, headers=headers, timeout=2) except Exception as e: - raise RuntimeError("OpsGenie connection error: %s" % e) + raise RuntimeError('OpsGenie connection error: %s' % e) return r def pre_receive(self, alert): return alert def post_receive(self, alert): - LOG.debug('Alert receive %s: %s' % (alert.id, alert.get_body(history=False))) + LOG.debug('Alert receive %s: %s' % + (alert.id, alert.get_body(history=False))) if alert.repeat: LOG.debug('Alert repeating; ignored') return @@ -86,13 +99,14 @@ class TriggerEvent(PluginBase): LOG.info('Just informational or warning not sending to OpsGenie') else: headers = { - "Authorization": 'GenieKey ' + self.opsgenie_service_key(alert.resource) + 'Authorization': 'GenieKey ' + self.opsgenie_service_key(alert.resource) } # Send all alert data as details to opsgenie body = alert.get_body(history=False) details = {} - details['web_url'] = '%s/#/alert/%s' % (DASHBOARD_URL, alert.id) + details['web_url'] = '{}/#/alert/{}'.format( + DASHBOARD_URL, alert.id) details['service'] = alert.service[0] details['origin'] = body['origin'] details['event'] = body['event'] @@ -103,37 +117,40 @@ class TriggerEvent(PluginBase): details['duplicateCount'] = body['duplicateCount'] payload = { - "alias": alert.id, - "message": "[ %s ]: %s: %s" % (alert.environment, alert.severity, alert.text), - "entity": alert.environment, - "responders" : self.get_opsgenie_teams(), - "tags": [alert.environment, alert.resource, alert.service[0], alert.event], - "source": "{}".format(OPSGENIE_ALERT_SOURCE), - "details": details + 'alias': alert.id, + 'message': '[ {} ]: {}: {}'.format(alert.environment, alert.severity, alert.text), + 'entity': alert.environment, + 'responders': self.get_opsgenie_teams(), + 'tags': [alert.environment, alert.resource, alert.service[0], alert.event], + 'source': '{}'.format(OPSGENIE_ALERT_SOURCE), + 'details': details } LOG.debug('OpsGenie CREATE payload: %s' % payload) try: - r = requests.post(OPSGENIE_EVENTS_CREATE_URL, json=payload, headers=headers, timeout=2) + r = requests.post(OPSGENIE_EVENTS_CREATE_URL, + json=payload, headers=headers, timeout=2) except Exception as e: - raise RuntimeError("OpsGenie connection error: %s" % e) + raise RuntimeError('OpsGenie connection error: %s' % e) - LOG.debug('OpsGenie response: %s - %s' % (r.status_code, r.text)) + LOG.debug('OpsGenie response: {} - {}'.format(r.status_code, r.text)) # generate list of responders from OPSGENIE_TEAMS env var def get_opsgenie_teams(self): - teams = OPSGENIE_TEAMS.replace(' ', '') # remove whitespace + teams = OPSGENIE_TEAMS.replace(' ', '') # remove whitespace if len(teams) == 0: - return [] # no teams specified + return [] # no teams specified teams = teams.split(',') - return [{"name": team, "type": "team"} for team in teams] + return [{'name': team, 'type': 'team'} for team in teams] def status_change(self, alert, status, text): - LOG.debug('Alert change %s to %s: %s' % (alert.id, status, alert.get_body(history=False))) + LOG.debug('Alert change %s to %s: %s' % + (alert.id, status, alert.get_body(history=False))) if status not in ['ack', 'assign', 'closed']: - LOG.debug('Not sending status change to opsgenie: %s to %s' % (alert.id, status)) + LOG.debug('Not sending status change to opsgenie: %s to %s' % + (alert.id, status)) return if status == 'closed': @@ -141,4 +158,4 @@ class TriggerEvent(PluginBase): elif status == 'ack': r = self.opsgenie_ack_alert(alert, 'STATUS-ACK') - LOG.debug('OpsGenie response: %s - %s' % (r.status_code, r.text)) + LOG.debug('OpsGenie response: {} - {}'.format(r.status_code, r.text)) diff --git a/plugins/opsgenie/setup.py b/plugins/opsgenie/setup.py index 5cff54a..dea1107 100644 --- a/plugins/opsgenie/setup.py +++ b/plugins/opsgenie/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.3' setup( - name="alerta-opsgenie", + name='alerta-opsgenie', version=version, description='Alerta plugin for OpsGenie', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/pagerduty/alerta_pagerduty.py b/plugins/pagerduty/alerta_pagerduty.py index 89a029b..22add95 100644 --- a/plugins/pagerduty/alerta_pagerduty.py +++ b/plugins/pagerduty/alerta_pagerduty.py @@ -1,36 +1,42 @@ - import logging import os import re + import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.pagerduty') PAGERDUTY_EVENTS_URL = 'https://events.pagerduty.com/generic/2010-04-15/create_event.json' -PAGERDUTY_SERVICE_KEY = os.environ.get('PAGERDUTY_SERVICE_KEY') or app.config['PAGERDUTY_SERVICE_KEY'] -SERVICE_KEY_MATCHERS = os.environ.get('SERVICE_KEY_MATCHERS') or app.config['SERVICE_KEY_MATCHERS'] -DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') +PAGERDUTY_SERVICE_KEY = os.environ.get( + 'PAGERDUTY_SERVICE_KEY') or app.config['PAGERDUTY_SERVICE_KEY'] +SERVICE_KEY_MATCHERS = os.environ.get( + 'SERVICE_KEY_MATCHERS') or app.config['SERVICE_KEY_MATCHERS'] +DASHBOARD_URL = os.environ.get( + 'DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') class TriggerEvent(PluginBase): def pagerduty_service_key(self, resource): if not SERVICE_KEY_MATCHERS: - LOG.debug('No matchers defined! Default service key: %s' % (PAGERDUTY_SERVICE_KEY)) + LOG.debug('No matchers defined! Default service key: %s' % + (PAGERDUTY_SERVICE_KEY)) return PAGERDUTY_SERVICE_KEY for mapping in SERVICE_KEY_MATCHERS: if re.match(mapping['regex'], resource): - LOG.debug('Matched regex: %s, service key: %s' % (mapping['regex'], mapping['api_key'])) + LOG.debug('Matched regex: %s, service key: %s' % + (mapping['regex'], mapping['api_key'])) return mapping['api_key'] - LOG.debug('No regex match! Default service key: %s' % (PAGERDUTY_SERVICE_KEY)) + LOG.debug('No regex match! Default service key: %s' % + (PAGERDUTY_SERVICE_KEY)) return PAGERDUTY_SERVICE_KEY def pre_receive(self, alert): @@ -41,24 +47,24 @@ class TriggerEvent(PluginBase): if alert.repeat: return - message = "%s: %s alert for %s - %s is %s" % ( + message = '{}: {} alert for {} - {} is {}'.format( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource, alert.event ) if alert.severity in ['cleared', 'normal', 'ok']: - event_type = "resolve" + event_type = 'resolve' else: - event_type = "trigger" + event_type = 'trigger' payload = { - "service_key": self.pagerduty_service_key(alert.resource), - "incident_key": alert.id, - "event_type": event_type, - "description": message, - "client": "alerta", - "client_url": '%s/#/alert/%s' % (DASHBOARD_URL, alert.id), - "details": alert.get_body(history=False) + 'service_key': self.pagerduty_service_key(alert.resource), + 'incident_key': alert.id, + 'event_type': event_type, + 'description': message, + 'client': 'alerta', + 'client_url': '{}/#/alert/{}'.format(DASHBOARD_URL, alert.id), + 'details': alert.get_body(history=False) } LOG.debug('PagerDuty payload: %s', payload) @@ -66,7 +72,7 @@ class TriggerEvent(PluginBase): try: r = requests.post(PAGERDUTY_EVENTS_URL, json=payload, timeout=2) except Exception as e: - raise RuntimeError("PagerDuty connection error: %s" % e) + raise RuntimeError('PagerDuty connection error: %s' % e) LOG.debug('PagerDuty response: %s - %s', r.status_code, r.text) @@ -76,11 +82,11 @@ class TriggerEvent(PluginBase): return payload = { - "service_key": self.pagerduty_service_key(alert.resource), - "incident_key": alert.id, - "event_type": "acknowledge", - "description": text, - "details": alert.get_body(history=False) + 'service_key': self.pagerduty_service_key(alert.resource), + 'incident_key': alert.id, + 'event_type': 'acknowledge', + 'description': text, + 'details': alert.get_body(history=False) } LOG.debug('PagerDuty payload: %s', payload) @@ -88,6 +94,6 @@ class TriggerEvent(PluginBase): try: r = requests.post(PAGERDUTY_EVENTS_URL, json=payload, timeout=2) except Exception as e: - raise RuntimeError("PagerDuty connection error: %s" % e) + raise RuntimeError('PagerDuty connection error: %s' % e) LOG.debug('PagerDuty response: %s - %s', r.status_code, r.text) diff --git a/plugins/pagerduty/setup.py b/plugins/pagerduty/setup.py index 9012d7c..2deb694 100644 --- a/plugins/pagerduty/setup.py +++ b/plugins/pagerduty/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.1' setup( - name="alerta-pagerduty", + name='alerta-pagerduty', version=version, description='Alerta plugin for PagerDuty', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/prometheus/README.md b/plugins/prometheus/README.md index 42c72c0..6f0e23b 100644 --- a/plugins/prometheus/README.md +++ b/plugins/prometheus/README.md @@ -48,13 +48,13 @@ ALERTMANAGER_SILENCE_FROM_ACK = True ``` -Prometheus docs specify that prometheus should send all alerts to all alertmanagers. If you have configured your -ALERTMANAGER_API_URL to be a load balanced endpoint that mirrors requests to a set of alertmanagers then the following setting +Prometheus docs specify that prometheus should send all alerts to all alertmanagers. If you have configured your +ALERTMANAGER_API_URL to be a load balanced endpoint that mirrors requests to a set of alertmanagers then the following setting will create/remove silences if alertmanager has set the externalUrl, the following will configure alerta to use that for silences - instead of the Alertmanager API URL. + instead of the Alertmanager API URL. -Alertmanager syncs silences across all alertmanagers so only sendng it to one AM is appropriate. Using a load-balanced API that mirrors -requests will create one unique silenceId per alertmanager instance and sync them across all alertmanagers, which is not necessary. +Alertmanager syncs silences across all alertmanagers so only sendng it to one AM is appropriate. Using a load-balanced API that mirrors +requests will create one unique silenceId per alertmanager instance and sync them across all alertmanagers, which is not necessary. ```python ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES = True ``` diff --git a/plugins/prometheus/alerta_prometheus.py b/plugins/prometheus/alerta_prometheus.py index cdcc1d4..40ce97f 100644 --- a/plugins/prometheus/alerta_prometheus.py +++ b/plugins/prometheus/alerta_prometheus.py @@ -1,37 +1,45 @@ import datetime +import json import logging import os -import requests -import json from typing import Any +import requests +from alerta.models.alert import Alert +from alerta.plugins import PluginBase + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.models.alert import Alert -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.prometheus') DEFAULT_ALERTMANAGER_API_URL = 'http://localhost:9093' -ALERTMANAGER_API_URL = os.environ.get('ALERTMANAGER_API_URL') or app.config.get('ALERTMANAGER_API_URL', None) -ALERTMANAGER_USERNAME = os.environ.get('ALERTMANAGER_USERNAME') or app.config.get('ALERTMANAGER_USERNAME', None) -ALERTMANAGER_PASSWORD = os.environ.get('ALERTMANAGER_PASSWORD') or app.config.get('ALERTMANAGER_PASSWORD', None) -ALERTMANAGER_SILENCE_DAYS = os.environ.get('ALERTMANAGER_SILENCE_DAYS') or app.config.get('ALERTMANAGER_SILENCE_DAYS', 1) -ALERTMANAGER_SILENCE_FROM_ACK = os.environ.get('ALERTMANAGER_SILENCE_FROM_ACK') or app.config.get('ALERTMANAGER_SILENCE_FROM_ACK', False) -ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES = os.environ.get('ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES') or app.config.get('ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES',False) +ALERTMANAGER_API_URL = os.environ.get( + 'ALERTMANAGER_API_URL') or app.config.get('ALERTMANAGER_API_URL', None) +ALERTMANAGER_USERNAME = os.environ.get( + 'ALERTMANAGER_USERNAME') or app.config.get('ALERTMANAGER_USERNAME', None) +ALERTMANAGER_PASSWORD = os.environ.get( + 'ALERTMANAGER_PASSWORD') or app.config.get('ALERTMANAGER_PASSWORD', None) +ALERTMANAGER_SILENCE_DAYS = os.environ.get( + 'ALERTMANAGER_SILENCE_DAYS') or app.config.get('ALERTMANAGER_SILENCE_DAYS', 1) +ALERTMANAGER_SILENCE_FROM_ACK = os.environ.get( + 'ALERTMANAGER_SILENCE_FROM_ACK') or app.config.get('ALERTMANAGER_SILENCE_FROM_ACK', False) +ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES = os.environ.get( + 'ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES') or app.config.get('ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES', False) class AlertmanagerSilence(PluginBase): def __init__(self, name=None): - self.auth = (ALERTMANAGER_USERNAME, ALERTMANAGER_PASSWORD) if ALERTMANAGER_USERNAME else None + self.auth = (ALERTMANAGER_USERNAME, + ALERTMANAGER_PASSWORD) if ALERTMANAGER_USERNAME else None - super(AlertmanagerSilence, self).__init__(name) + super().__init__(name) def pre_receive(self, alert): return alert @@ -47,22 +55,25 @@ class AlertmanagerSilence(PluginBase): silenceId = alert.attributes.get('silenceId', None) if silenceId: - LOG.debug('Alertmanager: Remove silence for alertname=%s instance=%s', alert.event, alert.resource) - base_url = ALERTMANAGER_API_URL or alert.attributes.get('externalUrl', DEFAULT_ALERTMANAGER_API_URL) + LOG.debug('Alertmanager: Remove silence for alertname=%s instance=%s', + alert.event, alert.resource) + base_url = ALERTMANAGER_API_URL or alert.attributes.get( + 'externalUrl', DEFAULT_ALERTMANAGER_API_URL) url = base_url + '/api/v1/silence/%s' % silenceId try: r = requests.delete(url, auth=self.auth, timeout=2) except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) + raise RuntimeError('Alertmanager: ERROR - %s' % e) LOG.debug('Alertmanager: %s - %s', r.status_code, r.text) try: alert.attributes['silenceId'] = None except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) - LOG.debug('Alertmanager: Removed silenceId %s from attributes', silenceId) + raise RuntimeError('Alertmanager: ERROR - %s' % e) + LOG.debug( + 'Alertmanager: Removed silenceId %s from attributes', silenceId) if status == 'closed': - LOG.warning("Status is now closed") + LOG.warning('Status is now closed') return alert @@ -74,22 +85,26 @@ class AlertmanagerSilence(PluginBase): if alert.event_type != 'prometheusAlert': return alert - - base_url = ALERTMANAGER_API_URL or alert.attributes.get('externalUrl', DEFAULT_ALERTMANAGER_API_URL) + base_url = ALERTMANAGER_API_URL or alert.attributes.get( + 'externalUrl', DEFAULT_ALERTMANAGER_API_URL) if action == 'close': - LOG.warning("Got a close action so trying to close this in alertmanager too") + LOG.warning( + 'Got a close action so trying to close this in alertmanager too') url = base_url + '/api/v1/alerts' raw_data_string = alert.raw_data raw_data = json.loads(raw_data_string) # set the endsAt to now so alertmanager will consider it expired or whatever - raw_data["endsAt"] = (datetime.datetime.utcnow() - datetime.timedelta(minutes=5)).replace(microsecond=0).isoformat() + ".000Z" - LOG.debug("Raw data type: {}, Raw data contents: {}".format(type(raw_data),raw_data)) - data = [ raw_data ] + raw_data['endsAt'] = (datetime.datetime.utcnow( + ) - datetime.timedelta(minutes=5)).replace(microsecond=0).isoformat() + '.000Z' + LOG.debug('Raw data type: {}, Raw data contents: {}'.format( + type(raw_data), raw_data)) + data = [raw_data] try: r = requests.post(url, json=data, auth=self.auth, timeout=2) except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) - LOG.debug('Alertmanager response was: %s - %s', r.status_code, r.text) + raise RuntimeError('Alertmanager: ERROR - %s' % e) + LOG.debug('Alertmanager response was: %s - %s', + r.status_code, r.text) elif action == 'ack' and ALERTMANAGER_SILENCE_FROM_ACK: @@ -108,37 +123,38 @@ class AlertmanagerSilence(PluginBase): LOG.debug('Alertmanager: Add silence for alertname=%s instance=%s timeout=%s', alert.event, alert.resource, str(silence_seconds)) data = { - "matchers": [ + 'matchers': [ { - "name": "alertname", - "value": alert.event + 'name': 'alertname', + 'value': alert.event }, { - "name": "instance", - "value": alert.resource + 'name': 'instance', + 'value': alert.resource } ], - "startsAt": datetime.datetime.utcnow().replace(microsecond=0).isoformat() + ".000Z", - "endsAt": (datetime.datetime.utcnow() + datetime.timedelta(seconds=silence_seconds)) - .replace(microsecond=0).isoformat() + ".000Z", - "createdBy": "alerta", - "comment": text if text != '' else "silenced by alerta" + 'startsAt': datetime.datetime.utcnow().replace(microsecond=0).isoformat() + '.000Z', + 'endsAt': (datetime.datetime.utcnow() + datetime.timedelta(seconds=silence_seconds)) + .replace(microsecond=0).isoformat() + '.000Z', + 'createdBy': 'alerta', + 'comment': text if text != '' else 'silenced by alerta' } - # if alertmanager is clustered behind a load balancer that mirrors requests we should prefer to create one silence - # rather than many + # if alertmanager is clustered behind a load balancer that mirrors requests we should prefer to create one silence + # rather than many if ALERTMANAGER_USE_EXTERNALURL_FOR_SILENCES: - base_url = alert.attributes.get('externalUrl', DEFAULT_ALERTMANAGER_API_URL) or ALERTMANAGER_API_URL - else: - base_url = ALERTMANAGER_API_URL or alert.attributes.get('externalUrl', DEFAULT_ALERTMANAGER_API_URL) + base_url = alert.attributes.get( + 'externalUrl', DEFAULT_ALERTMANAGER_API_URL) or ALERTMANAGER_API_URL + else: + base_url = ALERTMANAGER_API_URL or alert.attributes.get( + 'externalUrl', DEFAULT_ALERTMANAGER_API_URL) url = base_url + '/api/v1/silences' - try: r = requests.post(url, json=data, auth=self.auth, timeout=2) except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) + raise RuntimeError('Alertmanager: ERROR - %s' % e) LOG.debug('Alertmanager: %s - %s', r.status_code, r.text) # example r={"status":"success","data":{"silenceId":8}} @@ -148,29 +164,32 @@ class AlertmanagerSilence(PluginBase): silenceId = data['silenceId'] alert.attributes['silenceId'] = silenceId else: - silenceId = alert.attributes.get('silenceId', "unknown") + silenceId = alert.attributes.get('silenceId', 'unknown') text = text + ' (silenced in Alertmanager)' except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) + raise RuntimeError('Alertmanager: ERROR - %s' % e) LOG.debug('Alertmanager: Added silenceId %s to attributes', silenceId) elif action == 'unack': - LOG.debug('Alertmanager: Remove silence for alertname=%s instance=%s', alert.event, alert.resource) + LOG.debug('Alertmanager: Remove silence for alertname=%s instance=%s', + alert.event, alert.resource) silenceId = alert.attributes.get('silenceId', None) if silenceId: - base_url = ALERTMANAGER_API_URL or alert.attributes.get('externalUrl', DEFAULT_ALERTMANAGER_API_URL) + base_url = ALERTMANAGER_API_URL or alert.attributes.get( + 'externalUrl', DEFAULT_ALERTMANAGER_API_URL) url = base_url + '/api/v1/silence/%s' % silenceId try: r = requests.delete(url, auth=self.auth, timeout=2) except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) + raise RuntimeError('Alertmanager: ERROR - %s' % e) LOG.debug('Alertmanager: %s - %s', r.status_code, r.text) try: alert.attributes['silenceId'] = None except Exception as e: - raise RuntimeError("Alertmanager: ERROR - %s" % e) - LOG.debug('Alertmanager: Removed silenceId %s from attributes', silenceId) + raise RuntimeError('Alertmanager: ERROR - %s' % e) + LOG.debug( + 'Alertmanager: Removed silenceId %s from attributes', silenceId) return alert diff --git a/plugins/prometheus/setup.py b/plugins/prometheus/setup.py index 8b96cd8..1d6a398 100644 --- a/plugins/prometheus/setup.py +++ b/plugins/prometheus/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.4.0' setup( - name="alerta-prometheus", + name='alerta-prometheus', version=version, description='Alerta plugin for Prometheus Alertmanager', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/pubsub/README.md b/plugins/pubsub/README.md index 3633324..2eae999 100644 --- a/plugins/pubsub/README.md +++ b/plugins/pubsub/README.md @@ -14,7 +14,7 @@ You need to install following python packages: $ sudo pip install --upgrade oauth2client $ sudo pip install grpcio==1.30.0 -There are issues running never versions of grpcio (Confirmed stable and working with grpcio 1.30.0). +There are issues running never versions of grpcio (Confirmed stable and working with grpcio 1.30.0). Follow this to configure [authentication](https://googlecloudplatform.github.io/google-cloud-python/stable/pubsub-usage.html#authentication-configuration) diff --git a/plugins/pubsub/alerta_pubsub.py b/plugins/pubsub/alerta_pubsub.py index 1db0027..b1fcb2b 100644 --- a/plugins/pubsub/alerta_pubsub.py +++ b/plugins/pubsub/alerta_pubsub.py @@ -1,27 +1,28 @@ -import os -import logging -import base64 import json +import logging +import os from datetime import datetime +from alerta.plugins import PluginBase +from google.cloud import pubsub_v1 +from google.oauth2 import service_account + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase - -from google.cloud import pubsub_v1 -from google.oauth2 import service_account LOG = logging.getLogger('alerta.plugins.pubsub') PROJECT_ID = os.environ.get('PROJECT_ID') or app.config.get('PROJECT_ID') TOPIC_NAME = os.environ.get('TOPIC_NAME') or app.config.get('TOPIC_NAME') -SERVICE_ACCOUNT_JSON = os.environ.get('SERVICE_ACCOUNT_JSON') or app.config.get('SERVICE_ACCOUNT_JSON', None) +SERVICE_ACCOUNT_JSON = os.environ.get( + 'SERVICE_ACCOUNT_JSON') or app.config.get('SERVICE_ACCOUNT_JSON', None) + +PUBSUB_SCOPES = ['https://www.googleapis.com/auth/pubsub'] -PUBSUB_SCOPES = ["https://www.googleapis.com/auth/pubsub"] class SendToPubsub(PluginBase): @@ -29,13 +30,14 @@ class SendToPubsub(PluginBase): LOG.info('creating pubsub client') self.publisher = self.get_client() self.topic = 'projects/{}/topics/{}'.format(PROJECT_ID, TOPIC_NAME) - super(SendToPubsub, self).__init__(name) + super().__init__(name) def get_client(self): if SERVICE_ACCOUNT_JSON: json_dict = json.loads(SERVICE_ACCOUNT_JSON) LOG.info('using service account JSON : %s', json_dict) - credential = service_account.Credentials.from_service_account_info(json_dict) + credential = service_account.Credentials.from_service_account_info( + json_dict) scoped_credential = credential.with_scopes(PUBSUB_SCOPES) return pubsub_v1.PublisherClient(credentials=scoped_credential) @@ -47,31 +49,32 @@ class SendToPubsub(PluginBase): def post_receive(self, alert): body = alert.get_body(history=False) - if not alert.severity in ['cleared', 'normal', 'ok']: + if alert.severity not in ['cleared', 'normal', 'ok']: try: encoded_message = json.dumps(body) - encoded_message = encoded_message.encode("utf-8") - LOG.info("post receive") + encoded_message = encoded_message.encode('utf-8') + LOG.info('post receive') LOG.info(encoded_message) future = self.publisher.publish(self.topic, encoded_message) future.result() except Exception as excp: LOG.exception(excp) - raise RuntimeError("pubsub exception: %s - %s" % (excp, body)) + raise RuntimeError( + 'pubsub exception: {} - {}'.format(excp, body)) def status_change(self, alert, status, text): body = alert.get_body(history=False) - if not alert.severity in ['normal','ok']: + if alert.severity not in ['normal', 'ok']: try: body['status'] = status body['updateTime'] = datetime.utcnow().isoformat() encoded_message = json.dumps(body) - encoded_message = encoded_message.encode("utf-8") - LOG.info("status change: %s", status) + encoded_message = encoded_message.encode('utf-8') + LOG.info('status change: %s', status) LOG.info(encoded_message) future = self.publisher.publish(self.topic, encoded_message) future.result() except Exception as excp: LOG.exception(excp) - raise RuntimeError("pubsub exception: %s - %s" % (excp, body)) - + raise RuntimeError( + 'pubsub exception: {} - {}'.format(excp, body)) diff --git a/plugins/pubsub/setup.py b/plugins/pubsub/setup.py index 5ceb474..88802b9 100644 --- a/plugins/pubsub/setup.py +++ b/plugins/pubsub/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.2.2' setup( - name="alerta-pubsub", + name='alerta-pubsub', version=version, description='Alerta plugin for sending alerts to pubsub', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/pushover/alerta_pushover.py b/plugins/pushover/alerta_pushover.py index 4b31c9c..5749651 100644 --- a/plugins/pushover/alerta_pushover.py +++ b/plugins/pushover/alerta_pushover.py @@ -1,21 +1,23 @@ - import logging import os + import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.pushover') PUSHOVER_URL = 'https://api.pushover.net/1/messages.json' -PUSHOVER_TOKEN = os.environ.get('PUSHOVER_TOKEN') or app.config['PUSHOVER_TOKEN'] +PUSHOVER_TOKEN = os.environ.get( + 'PUSHOVER_TOKEN') or app.config['PUSHOVER_TOKEN'] PUSHOVER_USER = os.environ.get('PUSHOVER_USER') or app.config['PUSHOVER_USER'] -DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') +DASHBOARD_URL = os.environ.get( + 'DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') PUSHOVER_EMERG = 2 # requires user ack PUSHOVER_HIGH = 1 @@ -26,9 +28,9 @@ PUSHOVER_BADGE = -2 # no notification # See https://pushover.net/api#priority PRIORITY_MAP = { 'critical': PUSHOVER_EMERG, - 'major': PUSHOVER_HIGH, - 'minor': PUSHOVER_NORMAL, - 'warning': PUSHOVER_LOW + 'major': PUSHOVER_HIGH, + 'minor': PUSHOVER_NORMAL, + 'warning': PUSHOVER_LOW } @@ -42,7 +44,7 @@ class PushMessage(PluginBase): if alert.repeat: return - title = "%s: %s alert for %s - %s is %s" % ( + title = '{}: {} alert for {} - {} is {}'.format( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource, alert.event ) @@ -50,15 +52,15 @@ class PushMessage(PluginBase): priority = PRIORITY_MAP.get(alert.severity, PUSHOVER_BADGE) payload = { - "token": PUSHOVER_TOKEN, - "user": PUSHOVER_USER, - "title": title, - "message": alert.text, - "url": '%s/#/alert/%s' % (DASHBOARD_URL, alert.id), - "url_title": "View alert", - "priority": priority, - "timestamp": alert.create_time, - "sound": "tugboat" + 'token': PUSHOVER_TOKEN, + 'user': PUSHOVER_USER, + 'title': title, + 'message': alert.text, + 'url': '{}/#/alert/{}'.format(DASHBOARD_URL, alert.id), + 'url_title': 'View alert', + 'priority': priority, + 'timestamp': alert.create_time, + 'sound': 'tugboat' } if priority == PUSHOVER_EMERG: @@ -70,7 +72,7 @@ class PushMessage(PluginBase): try: r = requests.post(PUSHOVER_URL, data=payload, timeout=2) except Exception as e: - raise RuntimeError("Pushover.net: ERROR - %s" % e) + raise RuntimeError('Pushover.net: ERROR - %s' % e) LOG.debug('Pushover.net: %s - %s', r.status_code, r.text) diff --git a/plugins/pushover/setup.py b/plugins/pushover/setup.py index 027e0ab..5c76f4f 100644 --- a/plugins/pushover/setup.py +++ b/plugins/pushover/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.2' setup( - name="alerta-pushover", + name='alerta-pushover', version=version, description='Alerta plugin for Pushover', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/rocketchat/alerta_rocketchat.py b/plugins/rocketchat/alerta_rocketchat.py index 8920f18..343b9ab 100644 --- a/plugins/rocketchat/alerta_rocketchat.py +++ b/plugins/rocketchat/alerta_rocketchat.py @@ -1,29 +1,34 @@ - import logging import os + import requests +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.rocketchat') -ROCKETCHAT_WEBHOOK_URL = os.environ.get('ROCKETCHAT_WEBHOOK_URL') or app.config['ROCKETCHAT_WEBHOOK_URL'] -ROCKETCHAT_CHANNEL = os.environ.get('ROCKETCHAT_CHANNEL') or app.config.get('ROCKETCHAT_CHANNEL', '') -ALERTA_USERNAME = os.environ.get('ALERTA_USERNAME') or app.config.get('ALERTA_USERNAME', 'alerta') -ICON_EMOJI = os.environ.get('ICON_EMOJI') or app.config.get('ICON_EMOJI', ':rocket:') -DASHBOARD_URL = os.environ.get('DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') +ROCKETCHAT_WEBHOOK_URL = os.environ.get( + 'ROCKETCHAT_WEBHOOK_URL') or app.config['ROCKETCHAT_WEBHOOK_URL'] +ROCKETCHAT_CHANNEL = os.environ.get( + 'ROCKETCHAT_CHANNEL') or app.config.get('ROCKETCHAT_CHANNEL', '') +ALERTA_USERNAME = os.environ.get( + 'ALERTA_USERNAME') or app.config.get('ALERTA_USERNAME', 'alerta') +ICON_EMOJI = os.environ.get('ICON_EMOJI') or app.config.get( + 'ICON_EMOJI', ':rocket:') +DASHBOARD_URL = os.environ.get( + 'DASHBOARD_URL') or app.config.get('DASHBOARD_URL', '') DEFAULT_SEVERITY_MAP = { 'security': '#000000', # black 'critical': '#FF0000', # red 'major': '#FFA500', # orange 'minor': '#FFFF00', # yellow - 'warning': '#1E90FF', #blue - 'informational': '#808080', #gray + 'warning': '#1E90FF', # blue + 'informational': '#808080', # gray 'debug': '#808080', # gray 'trace': '#808080', # gray 'ok': '#00CC00' # green @@ -50,27 +55,29 @@ class PostMessage(PluginBase): title = '[{status}] {environment}: {event} on {resource}'.format( status=(status if status else alert.status).capitalize(), environment=alert.environment, - severity=alert.severity, + # severity=alert.severity, event=alert.event, resource=alert.resource ) return { - "channel": ROCKETCHAT_CHANNEL, - "text": text, - "alias": ALERTA_USERNAME, - "emoji": ICON_EMOJI, - "attachments": [{ - "title": title, - "title_link": '%s/#/alert/%s' % (DASHBOARD_URL, alert.id), - "text": alert.text, - "color": DEFAULT_SEVERITY_MAP.get(alert.severity, DEFAULT_SEVERITY_MAP['ok']), - "fields": [ - {"title": "Status", "value": (status if status else alert.status).capitalize(), "short": True}, - {"title": "Environment", "value": alert.environment, "short": True}, - {"title": "Resource", "value": alert.resource, "short": True}, - {"title": "Services", "value": ", ".join(alert.service), "short": True} - ] + 'channel': ROCKETCHAT_CHANNEL, + 'text': text, + 'alias': ALERTA_USERNAME, + 'emoji': ICON_EMOJI, + 'attachments': [{ + 'title': title, + 'title_link': '{}/#/alert/{}'.format(DASHBOARD_URL, alert.id), + 'text': alert.text, + 'color': DEFAULT_SEVERITY_MAP.get(alert.severity, DEFAULT_SEVERITY_MAP['ok']), + 'fields': [ + {'title': 'Status', 'value': ( + status if status else alert.status).capitalize(), 'short': True}, + {'title': 'Environment', 'value': alert.environment, 'short': True}, + {'title': 'Resource', 'value': alert.resource, 'short': True}, + {'title': 'Services', 'value': ', '.join( + alert.service), 'short': True} + ] }] } @@ -81,6 +88,6 @@ class PostMessage(PluginBase): try: r = requests.post(ROCKETCHAT_WEBHOOK_URL, json=payload, timeout=2) except Exception as e: - raise RuntimeError("Rocket.Chat: ERROR - %s" % e) + raise RuntimeError('Rocket.Chat: ERROR - %s' % e) LOG.debug('Rocket.Chat: %s - %s', r.status_code, r.text) diff --git a/plugins/rocketchat/setup.py b/plugins/rocketchat/setup.py index fdf8356..3e656b0 100644 --- a/plugins/rocketchat/setup.py +++ b/plugins/rocketchat/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.0' setup( - name="alerta-rocketchat", + name='alerta-rocketchat', version=version, description='Alerta plugin for Rocket.Chat', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/slack/README.md b/plugins/slack/README.md index 49d7802..ebaf36b 100644 --- a/plugins/slack/README.md +++ b/plugins/slack/README.md @@ -115,8 +115,8 @@ SLACK_PAYLOAD = { Slack Apps API -------------- -To use the Slack "Apps" API instead of an Incoming Webhook, create an application and -obtain its OAuth token. Use that to set ```SLACK_TOKEN``` and specify the +To use the Slack "Apps" API instead of an Incoming Webhook, create an application and +obtain its OAuth token. Use that to set ```SLACK_TOKEN``` and specify the URL endpoint to the new API entrypoint this way: ```python diff --git a/plugins/slack/alerta_slack.py b/plugins/slack/alerta_slack.py index 4e39fdf..fbfc918 100644 --- a/plugins/slack/alerta_slack.py +++ b/plugins/slack/alerta_slack.py @@ -1,24 +1,25 @@ - +import ast import json import logging import os -import requests import traceback -import ast + +import requests +from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.slack') try: from jinja2 import Template except Exception as e: - LOG.error('SLACK: ERROR - Jinja template error: %s, template functionality will be unavailable', e) + LOG.error( + 'SLACK: ERROR - Jinja template error: %s, template functionality will be unavailable', e) try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.slack') @@ -27,46 +28,47 @@ SLACK_ATTACHMENTS = True if os.environ.get( try: SLACK_CHANNEL_ENV_MAP = json.loads( os.environ.get('SLACK_CHANNEL_ENV_MAP')) -except Exception as e: +except Exception: SLACK_CHANNEL_ENV_MAP = app.config.get('SLACK_CHANNEL_ENV_MAP', dict()) try: SLACK_CHANNEL_EVENT_MAP = json.loads( os.environ.get('SLACK_CHANNEL_EVENT_MAP')) -except Exception as e: +except Exception: SLACK_CHANNEL_EVENT_MAP = app.config.get('SLACK_CHANNEL_EVENT_MAP', dict()) try: SLACK_CHANNEL_SEVERITY_MAP = json.loads( os.environ.get('SLACK_CHANNEL_SEVERITY_MAP')) -except Exception as e: - SLACK_CHANNEL_SEVERITY_MAP = app.config.get('SLACK_CHANNEL_SEVERITY_MAP', dict()) +except Exception: + SLACK_CHANNEL_SEVERITY_MAP = app.config.get( + 'SLACK_CHANNEL_SEVERITY_MAP', dict()) try: SLACK_CHANNEL_MAP = json.loads( os.environ.get('SLACK_CHANNEL_MAP')) -except Exception as e: +except Exception: SLACK_CHANNEL_MAP = app.config.get('SLACK_CHANNEL_MAP', dict()) try: SLACK_SEVERITY_FILTER = ast.literal_eval( os.environ.get('SLACK_SEVERITY_FILTER')) -except Exception as e: +except Exception: SLACK_SEVERITY_FILTER = app.config.get('SLACK_SEVERITY_FILTER', list()) SLACK_SEND_ON_ACK = os.environ.get( 'SLACK_SEND_ON_ACK') or app.config.get('SLACK_SEND_ON_ACK', False) SLACK_SEVERITY_MAP = app.config.get('SLACK_SEVERITY_MAP', {}) -SLACK_DEFAULT_SEVERITY_MAP = {'security': '#000000', # black - 'critical': '#FF0000', # red - 'major': '#FFA500', # orange - 'minor': '#FFFF00', # yellow - 'warning': '#1E90FF', #blue - 'informational': '#808080', #gray - 'debug': '#808080', # gray - 'trace': '#808080', # gray - 'ok': '#00CC00'} # green -SLACK_DEFAULT_SUMMARY_FMT='*[{status}] {environment} {service} {severity}* - _{event} on {resource}_ <{dashboard}/#/alert/{alert_id}|{short_id}>' +SLACK_DEFAULT_SEVERITY_MAP = {'security': '#000000', # black + 'critical': '#FF0000', # red + 'major': '#FFA500', # orange + 'minor': '#FFFF00', # yellow + 'warning': '#1E90FF', # blue + 'informational': '#808080', # gray + 'debug': '#808080', # gray + 'trace': '#808080', # gray + 'ok': '#00CC00'} # green +SLACK_DEFAULT_SUMMARY_FMT = '*[{status}] {environment} {service} {severity}* - _{event} on {resource}_ <{dashboard}/#/alert/{alert_id}|{short_id}>' SLACK_HEADERS = { 'Content-Type': 'application/json' } @@ -79,7 +81,7 @@ class ServiceIntegration(PluginBase): self._severities = SLACK_DEFAULT_SEVERITY_MAP self._severities.update(SLACK_SEVERITY_MAP) - super(ServiceIntegration, self).__init__(name) + super().__init__(name) def pre_receive(self, alert): return alert @@ -101,12 +103,17 @@ class ServiceIntegration(PluginBase): return def _slack_prepare_payload(self, alert, status=None, text=None, **kwargs): - SLACK_CHANNEL = self.get_config('SLACK_CHANNEL', default='', type=str, **kwargs) - SLACK_SUMMARY_FMT = self.get_config('SLACK_SUMMARY_FMT', type=str, **kwargs) # Message summary format - SLACK_PAYLOAD = self.get_config('SLACK_PAYLOAD', type=str, **kwargs) # Full API control + SLACK_CHANNEL = self.get_config( + 'SLACK_CHANNEL', default='', type=str, **kwargs) + SLACK_SUMMARY_FMT = self.get_config( + 'SLACK_SUMMARY_FMT', type=str, **kwargs) # Message summary format + SLACK_PAYLOAD = self.get_config( + 'SLACK_PAYLOAD', type=str, **kwargs) # Full API control ICON_EMOJI = self.get_config('ICON_EMOJI', type=str, **kwargs) - ALERTA_USERNAME = self.get_config('ALERTA_USERNAME', default='alerta', type=str, **kwargs) - DASHBOARD_URL = self.get_config('DASHBOARD_URL', default='', type=str, **kwargs) + ALERTA_USERNAME = self.get_config( + 'ALERTA_USERNAME', default='alerta', type=str, **kwargs) + DASHBOARD_URL = self.get_config( + 'DASHBOARD_URL', default='', type=str, **kwargs) SLACK_TOKEN = self.get_config('SLACK_TOKEN', type=str, **kwargs) if SLACK_TOKEN: SLACK_HEADERS['Authorization'] = 'Bearer ' + SLACK_TOKEN @@ -117,24 +124,25 @@ class ServiceIntegration(PluginBase): color = '#00CC00' # green channel = SLACK_CHANNEL_SEVERITY_MAP.get(alert.severity, SLACK_CHANNEL) if SLACK_CHANNEL_SEVERITY_MAP.get(alert.severity): - LOG.debug("Found severity mapping. Channel: %s" % channel) + LOG.debug('Found severity mapping. Channel: %s' % channel) else: - LOG.debug("No severity mapping. Channel: %s" % channel) + LOG.debug('No severity mapping. Channel: %s' % channel) channel = SLACK_CHANNEL_ENV_MAP.get(alert.environment, channel) if SLACK_CHANNEL_ENV_MAP.get(alert.environment): - LOG.debug("Found env mapping. Channel: %s" % channel) + LOG.debug('Found env mapping. Channel: %s' % channel) else: - LOG.debug("No env mapping. Channel: %s" % channel) + LOG.debug('No env mapping. Channel: %s' % channel) channel = SLACK_CHANNEL_EVENT_MAP.get(alert.event, channel) if SLACK_CHANNEL_EVENT_MAP.get(alert.event): - LOG.debug("Found event mapping. Channel: %s" % channel) + LOG.debug('Found event mapping. Channel: %s' % channel) else: - LOG.debug("No event mapping. Channel: %s" % channel) - channel = SLACK_CHANNEL_MAP.get(alert.environment, dict()).get(alert.severity, channel) + LOG.debug('No event mapping. Channel: %s' % channel) + channel = SLACK_CHANNEL_MAP.get( + alert.environment, dict()).get(alert.severity, channel) if SLACK_CHANNEL_MAP.get(alert.environment, dict()).get(alert.severity, channel): - LOG.debug("Found env-severity mapping. Channel: %s" % channel) + LOG.debug('Found env-severity mapping. Channel: %s' % channel) else: - LOG.debug("No env-severity mapping. Channel: %s" % channel) + LOG.debug('No env-severity mapping. Channel: %s' % channel) templateVars = { 'alert': alert, @@ -146,13 +154,15 @@ class ServiceIntegration(PluginBase): } if SLACK_PAYLOAD: - LOG.debug("Formatting with slack payload template") - formattedPayload = self._format_template(json.dumps(SLACK_PAYLOAD), templateVars).replace('\n', '\\n') - LOG.debug("Formatted slack payload:\n%s" % formattedPayload) + LOG.debug('Formatting with slack payload template') + formattedPayload = self._format_template(json.dumps( + SLACK_PAYLOAD), templateVars).replace('\n', '\\n') + LOG.debug('Formatted slack payload:\n%s' % formattedPayload) payload = json.loads(formattedPayload) else: if type(SLACK_SUMMARY_FMT) is str: - summary = self._format_template(SLACK_SUMMARY_FMT, templateVars) + summary = self._format_template( + SLACK_SUMMARY_FMT, templateVars) else: summary = SLACK_DEFAULT_SUMMARY_FMT.format( status=alert.status.capitalize(), @@ -173,68 +183,75 @@ class ServiceIntegration(PluginBase): payload['icon_emoji'] = ICON_EMOJI if SLACK_ATTACHMENTS: payload['attachments'] = [{ - "fallback": summary, - "color": color, - "fields": [ - {"title": "Status", "value": (status if status else alert.status).capitalize(), - "short": True}, - {"title": "Environment", - "value": alert.environment, "short": True}, - {"title": "Resource", "value": alert.resource, "short": True}, - {"title": "Services", "value": ", ".join( - alert.service), "short": True} + 'fallback': summary, + 'color': color, + 'fields': [ + {'title': 'Status', 'value': (status if status else alert.status).capitalize(), + 'short': True}, + {'title': 'Environment', + 'value': alert.environment, 'short': True}, + {'title': 'Resource', 'value': alert.resource, 'short': True}, + {'title': 'Services', 'value': ', '.join( + alert.service), 'short': True} ] }] return payload def post_receive(self, alert, **kwargs): - SLACK_WEBHOOK_URL = self.get_config('SLACK_WEBHOOK_URL', type=str, **kwargs) + SLACK_WEBHOOK_URL = self.get_config( + 'SLACK_WEBHOOK_URL', type=str, **kwargs) if alert.repeat: return if alert.severity in SLACK_SEVERITY_FILTER: - LOG.debug("Alert severity %s is included in SLACK_SEVERITY_FILTER list, thus it will not be forwarded to Slack." % alert.severity) + LOG.debug( + 'Alert severity %s is included in SLACK_SEVERITY_FILTER list, thus it will not be forwarded to Slack.' % alert.severity) return if alert.severity in ['ok', 'normal', 'cleared', app.config.get('DEFAULT_NORMAL_SEVERITY')] and alert.previous_severity in SLACK_SEVERITY_FILTER: - LOG.debug("Alert severity is %s but previous_severity was %s (included in SLACK_SEVERITY_FILTER list), thus it will not be forwarded to Slack." % (alert.severity, alert.previous_severity)) + LOG.debug('Alert severity is {} but previous_severity was {} (included in SLACK_SEVERITY_FILTER list), thus it will not be forwarded to Slack.'.format( + alert.severity, alert.previous_severity)) return try: payload = self._slack_prepare_payload(alert, **kwargs) LOG.debug('Slack payload: %s', payload) except Exception as e: - LOG.error('Exception formatting payload: %s\n%s' % (e, traceback.format_exc())) + LOG.error('Exception formatting payload: %s\n%s' % + (e, traceback.format_exc())) return try: r = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(payload), headers=SLACK_HEADERS, timeout=2) except Exception as e: - raise RuntimeError("Slack connection error: %s", e) + raise RuntimeError('Slack connection error: %s', e) - LOG.debug('Slack response: %s\n%s' % (r.status_code, r.text)) + LOG.debug('Slack response: {}\n{}'.format(r.status_code, r.text)) def status_change(self, alert, status, text, **kwargs): - SLACK_WEBHOOK_URL = self.get_config('SLACK_WEBHOOK_URL', type=str, **kwargs) + SLACK_WEBHOOK_URL = self.get_config( + 'SLACK_WEBHOOK_URL', type=str, **kwargs) - if SLACK_SEND_ON_ACK == False or status not in ['ack', 'assign']: + if not SLACK_SEND_ON_ACK or status not in ['ack', 'assign']: return try: - payload = self._slack_prepare_payload(alert, status, text, **kwargs) + payload = self._slack_prepare_payload( + alert, status, text, **kwargs) LOG.debug('Slack payload: %s', payload) except Exception as e: - LOG.error('Exception formatting payload: %s\n%s' % (e, traceback.format_exc())) + LOG.error('Exception formatting payload: %s\n%s' % + (e, traceback.format_exc())) return try: r = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(payload), headers=SLACK_HEADERS, timeout=2) except Exception as e: - raise RuntimeError("Slack connection error: %s", e) + raise RuntimeError('Slack connection error: %s', e) - LOG.debug('Slack response: %s\n%s' % (r.status_code, r.text)) + LOG.debug('Slack response: {}\n{}'.format(r.status_code, r.text)) diff --git a/plugins/slack/setup.py b/plugins/slack/setup.py index cb65e13..3f03e1b 100644 --- a/plugins/slack/setup.py +++ b/plugins/slack/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.5.2' setup( - name="alerta-slack", + name='alerta-slack', version=version, description='Alerta plugin for Slack', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/slack/test_slack.py b/plugins/slack/test_slack.py index 2db524a..90e644d 100644 --- a/plugins/slack/test_slack.py +++ b/plugins/slack/test_slack.py @@ -70,8 +70,10 @@ class SlackPluginTestCase(unittest.TestCase): 'tags': [] } - response = self.client.post('/alert', data=json.dumps(self.alert), headers={'Content-type': 'application/json'}) + response = self.client.post( + '/alert', data=json.dumps(self.alert), headers={'Content-type': 'application/json'}) self.assertEqual(response.status_code, 201) data = json.loads(response.data.decode('utf-8')) self.assertEqual(data['status'], 'ok') - self.assertRegex(data['id'], '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') + self.assertRegex( + data['id'], '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') diff --git a/plugins/sns/alerta_sns.py b/plugins/sns/alerta_sns.py index e242c9e..9cb190d 100644 --- a/plugins/sns/alerta_sns.py +++ b/plugins/sns/alerta_sns.py @@ -1,24 +1,28 @@ +import logging +import os import boto.exception import boto.sns -import logging -import os +from alerta.plugins import PluginBase try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.sns') DEFAULT_AWS_REGION = 'eu-west-1' DEFAULT_AWS_SNS_TOPIC = 'notify' -AWS_REGION = os.environ.get('AWS_REGION') or app.config.get('AWS_REGION', DEFAULT_AWS_REGION) -AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') or app.config.get('AWS_ACCESS_KEY_ID') -AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') or app.config.get('AWS_SECRET_ACCESS_KEY') -AWS_SNS_TOPIC = os.environ.get('AWS_SNS_TOPIC') or app.config.get('AWS_SNS_TOPIC', DEFAULT_AWS_SNS_TOPIC) +AWS_REGION = os.environ.get('AWS_REGION') or app.config.get( + 'AWS_REGION', DEFAULT_AWS_REGION) +AWS_ACCESS_KEY_ID = os.environ.get( + 'AWS_ACCESS_KEY_ID') or app.config.get('AWS_ACCESS_KEY_ID') +AWS_SECRET_ACCESS_KEY = os.environ.get( + 'AWS_SECRET_ACCESS_KEY') or app.config.get('AWS_SECRET_ACCESS_KEY') +AWS_SNS_TOPIC = os.environ.get('AWS_SNS_TOPIC') or app.config.get( + 'AWS_SNS_TOPIC', DEFAULT_AWS_SNS_TOPIC) class SnsTopicPublisher(PluginBase): @@ -35,7 +39,8 @@ class SnsTopicPublisher(PluginBase): raise RuntimeError if not self.connection: - LOG.error('Failed to connect to SNS topic %s - check AWS credentials and region', AWS_SNS_TOPIC) + LOG.error( + 'Failed to connect to SNS topic %s - check AWS credentials and region', AWS_SNS_TOPIC) raise RuntimeError try: @@ -50,7 +55,7 @@ class SnsTopicPublisher(PluginBase): LOG.error('Failed to get SNS TopicArn for %s', AWS_SNS_TOPIC) raise RuntimeError - super(SnsTopicPublisher, self).__init__(name) + super().__init__(name) LOG.info('Configured SNS publisher on topic "%s"', self.topic_arn) @@ -59,10 +64,12 @@ class SnsTopicPublisher(PluginBase): def post_receive(self, alert): - LOG.info('Sending message %s to SNS topic "%s"', alert.get_id(), self.topic_arn) + LOG.info('Sending message %s to SNS topic "%s"', + alert.get_id(), self.topic_arn) LOG.debug('Message: %s', alert.get_body()) - response = self.connection.publish(topic=self.topic_arn, message=alert.get_body()) + response = self.connection.publish( + topic=self.topic_arn, message=alert.get_body()) LOG.debug('Response: %s', response) def status_change(self, alert, status, text): diff --git a/plugins/sns/setup.py b/plugins/sns/setup.py index 9730971..68b3c30 100644 --- a/plugins/sns/setup.py +++ b/plugins/sns/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.1' setup( - name="alerta-sns", + name='alerta-sns', version=version, description='Alerta plugin for AWS SNS', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/syslog/alerta_logger.py b/plugins/syslog/alerta_logger.py index 74aed27..83d41b7 100644 --- a/plugins/syslog/alerta_logger.py +++ b/plugins/syslog/alerta_logger.py @@ -1,14 +1,14 @@ - import logging import os import sys from logging.handlers import SysLogHandler +from alerta.plugins import PluginBase + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.logger') @@ -16,9 +16,12 @@ DEFAULT_SYSLOG_FORMAT = '%(name)s[%(process)d]: %(levelname)s - %(message)s' DEFAULT_SYSLOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' DEFAULT_SYSLOG_FACILITY = 'local7' -LOGGER_SYSLOG_FORMAT = os.environ.get('LOGGER_SYSLOG_FORMAT') or app.config.get('SYSLOG_FORMAT', DEFAULT_SYSLOG_FORMAT) -LOGGER_SYSLOG_DATE_FORMAT = os.environ.get('LOGGER_SYSLOG_DATE_FORMAT') or app.config.get('SYSLOG_DATE_FORMAT', DEFAULT_SYSLOG_DATE_FORMAT) -LOGGER_SYSLOG_FACILITY = os.environ.get('LOGGER_SYSLOG_FACILITY') or app.config.get('SYSLOG_FACILITY', DEFAULT_SYSLOG_FACILITY) +LOGGER_SYSLOG_FORMAT = os.environ.get('LOGGER_SYSLOG_FORMAT') or app.config.get( + 'SYSLOG_FORMAT', DEFAULT_SYSLOG_FORMAT) +LOGGER_SYSLOG_DATE_FORMAT = os.environ.get('LOGGER_SYSLOG_DATE_FORMAT') or app.config.get( + 'SYSLOG_DATE_FORMAT', DEFAULT_SYSLOG_DATE_FORMAT) +LOGGER_SYSLOG_FACILITY = os.environ.get('LOGGER_SYSLOG_FACILITY') or app.config.get( + 'SYSLOG_FACILITY', DEFAULT_SYSLOG_FACILITY) class Syslog(PluginBase): @@ -27,17 +30,18 @@ class Syslog(PluginBase): self.logger = logging.getLogger(name) - if sys.platform == "darwin": + if sys.platform == 'darwin': socket = '/var/run/syslog' else: socket = '/dev/log' facility = LOGGER_SYSLOG_FACILITY syslog = SysLogHandler(address=socket, facility=facility) - syslog.setFormatter(logging.Formatter(fmt=LOGGER_SYSLOG_FORMAT, datefmt=LOGGER_SYSLOG_DATE_FORMAT)) + syslog.setFormatter(logging.Formatter( + fmt=LOGGER_SYSLOG_FORMAT, datefmt=LOGGER_SYSLOG_DATE_FORMAT)) self.logger.addHandler(syslog) - super(Syslog, self).__init__(name) + super().__init__(name) def pre_receive(self, alert): return alert diff --git a/plugins/syslog/setup.py b/plugins/syslog/setup.py index e638226..d513bb3 100644 --- a/plugins/syslog/setup.py +++ b/plugins/syslog/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.3.2' setup( - name="alerta-logger", + name='alerta-logger', version=version, description='Alerta plugin for syslog logging', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/telegram/alerta_telegram.py b/plugins/telegram/alerta_telegram.py index de81506..9779e79 100644 --- a/plugins/telegram/alerta_telegram.py +++ b/plugins/telegram/alerta_telegram.py @@ -1,16 +1,17 @@ import logging import os +import telepot +from alerta.plugins import PluginBase +from jinja2 import Template, UndefinedError + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase -import telepot -from jinja2 import Template, UndefinedError -DEFAULT_TMPL = """ +DEFAULT_TMPL = r""" {% if customer %}Customer: `{{customer}}` {% endif %} *[{{ status.capitalize() }}] {{ environment }} {{ severity.capitalize() }}* @@ -24,26 +25,26 @@ DEFAULT_TMPL = """ LOG = logging.getLogger('alerta.plugins.telegram') TELEGRAM_TOKEN = app.config.get('TELEGRAM_TOKEN') \ - or os.environ.get('TELEGRAM_TOKEN') + or os.environ.get('TELEGRAM_TOKEN') TELEGRAM_CHAT_ID = app.config.get('TELEGRAM_CHAT_ID') \ - or os.environ.get('TELEGRAM_CHAT_ID') + or os.environ.get('TELEGRAM_CHAT_ID') TELEGRAM_WEBHOOK_URL = app.config.get('TELEGRAM_WEBHOOK_URL', None) \ - or os.environ.get('TELEGRAM_WEBHOOK_URL') + or os.environ.get('TELEGRAM_WEBHOOK_URL') TELEGRAM_TEMPLATE = app.config.get('TELEGRAM_TEMPLATE') \ - or os.environ.get('TELEGRAM_TEMPLATE') + or os.environ.get('TELEGRAM_TEMPLATE') TELEGRAM_PROXY = app.config.get('TELEGRAM_PROXY') \ - or os.environ.get('TELEGRAM_PROXY') + or os.environ.get('TELEGRAM_PROXY') TELEGRAM_PROXY_USERNAME = app.config.get('TELEGRAM_PROXY_USERNAME') \ - or os.environ.get('TELEGRAM_PROXY_USERNAME') + or os.environ.get('TELEGRAM_PROXY_USERNAME') TELEGRAM_PROXY_PASSWORD = app.config.get('TELEGRAM_PROXY_PASSWORD') \ - or os.environ.get('TELEGRAM_PROXY_PASSWORD') + or os.environ.get('TELEGRAM_PROXY_PASSWORD') TELEGRAM_SOUND_NOTIFICATION_SEVERITY = app.config.get('TELEGRAM_SOUND_NOTIFICATION_SEVERITY') \ - or os.environ.get('TELEGRAM_SOUND_NOTIFICATION_SEVERITY') + or os.environ.get('TELEGRAM_SOUND_NOTIFICATION_SEVERITY') TELEGRAM_DISABLE_NOTIFICATION_SEVERITY = app.config.get('TELEGRAM_DISABLE_NOTIFICATION_SEVERITY') \ - or os.environ.get('TELEGRAM_DISABLE_NOTIFICATION_SEVERITY') + or os.environ.get('TELEGRAM_DISABLE_NOTIFICATION_SEVERITY') DASHBOARD_URL = app.config.get('DASHBOARD_URL', '') \ - or os.environ.get('DASHBOARD_URL') + or os.environ.get('DASHBOARD_URL') # use all the same, but telepot.aio.api.set_proxy for async telepot if all([TELEGRAM_PROXY, TELEGRAM_PROXY_USERNAME, TELEGRAM_PROXY_PASSWORD]): @@ -54,6 +55,7 @@ elif TELEGRAM_PROXY is not None: telepot.api.set_proxy(TELEGRAM_PROXY) LOG.debug('Telegram: using proxy %s', TELEGRAM_PROXY) + class TelegramBot(PluginBase): def __init__(self, name=None): @@ -61,14 +63,14 @@ class TelegramBot(PluginBase): LOG.debug('Telegram: %s', self.bot.getMe()) if TELEGRAM_WEBHOOK_URL and \ - TELEGRAM_WEBHOOK_URL != self.bot.getWebhookInfo()['url']: + TELEGRAM_WEBHOOK_URL != self.bot.getWebhookInfo()['url']: self.bot.setWebhook(TELEGRAM_WEBHOOK_URL) LOG.debug('Telegram: %s', self.bot.getWebhookInfo()) - super(TelegramBot, self).__init__(name) + super().__init__(name) if TELEGRAM_TEMPLATE: if os.path.exists(TELEGRAM_TEMPLATE): - with open(TELEGRAM_TEMPLATE, 'r') as f: + with open(TELEGRAM_TEMPLATE) as f: self.template = Template(f.read()) else: self.template = Template(TELEGRAM_TEMPLATE) @@ -86,7 +88,7 @@ class TelegramBot(PluginBase): try: text = self.template.render(alert.__dict__) except UndefinedError: - text = "Something bad has happened but also we " \ + text = 'Something bad has happened but also we ' \ "can't handle your telegram template message." LOG.debug('Telegram: message=%s', text) @@ -117,12 +119,14 @@ class TelegramBot(PluginBase): return if alert.severity in ['ok', 'normal', 'cleared', app.config.get('DEFAULT_NORMAL_SEVERITY')] and alert.previous_severity in TELEGRAM_DISABLE_NOTIFICATION_SEVERITY: - LOG.debug("Alert severity is %s but previous_severity was %s (included in DEFAULT_NORMAL_SEVERITY list), thus it will not be forwarded to Telegram." % (alert.severity, alert.previous_severity)) + LOG.debug('Alert severity is {} but previous_severity was {} (included in DEFAULT_NORMAL_SEVERITY list), thus it will not be forwarded to Telegram.'.format( + alert.severity, alert.previous_severity)) return - LOG.debug('Telegram: post_receive sendMessage disable_notification=%s', str(disable_notification)) + LOG.debug('Telegram: post_receive sendMessage disable_notification=%s', str( + disable_notification)) - chat_ids = TELEGRAM_CHAT_ID.split(",") + chat_ids = TELEGRAM_CHAT_ID.split(',') for chat_id in chat_ids: try: response = self.bot.sendMessage(chat_id, @@ -131,13 +135,13 @@ class TelegramBot(PluginBase): disable_notification=disable_notification, reply_markup=keyboard) except telepot.exception.TelegramError as e: - raise RuntimeError("Telegram (ChatId: %s): ERROR - %s, description= %s, json=%s", - chat_id, - e.error_code, - e.description, - e.json) + raise RuntimeError('Telegram (ChatId: %s): ERROR - %s, description= %s, json=%s', + chat_id, + e.error_code, + e.description, + e.json) except Exception as e: - raise RuntimeError("Telegram: ERROR - %s", e) + raise RuntimeError('Telegram: ERROR - %s', e) LOG.debug('Telegram (ChatId: %s): %s', chat_id, response) diff --git a/plugins/telegram/setup.py b/plugins/telegram/setup.py index 1ed98d1..35ba6ea 100644 --- a/plugins/telegram/setup.py +++ b/plugins/telegram/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.1.3' setup( - name="alerta-telegram", + name='alerta-telegram', version=version, description='Alerta plugin for Telegram', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/timeout/README.md b/plugins/timeout/README.md index 311b573..1cd75ae 100644 --- a/plugins/timeout/README.md +++ b/plugins/timeout/README.md @@ -16,7 +16,7 @@ setting and the default global timeout value may not be too large. Timeout actions: - + * The alert 'timeout' attribute is (re)set for each alert to the value specified @@ -57,4 +57,3 @@ Restart Alerta API and confirm that the plugin has been loaded and enabled. Set `DEBUG=True` in the `alertad.conf` configuration file and look for log entries referencing `Setting timeout for alert to 2400` - diff --git a/plugins/timeout/alerta_timeout.py b/plugins/timeout/alerta_timeout.py index 3218eac..f91c774 100644 --- a/plugins/timeout/alerta_timeout.py +++ b/plugins/timeout/alerta_timeout.py @@ -1,22 +1,24 @@ - import logging import os +from alerta.plugins import PluginBase + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.timeout') -TIMEOUT = os.environ.get('ALERT_TIMEOUT') or app.config.get('ALERT_TIMEOUT', '2600') +TIMEOUT = os.environ.get('ALERT_TIMEOUT') or app.config.get( + 'ALERT_TIMEOUT', '2600') + class Timeout(PluginBase): def pre_receive(self, alert): - LOG.debug("Setting timeout for alert to %s ",TIMEOUT) + LOG.debug('Setting timeout for alert to %s ', TIMEOUT) alert.timeout = TIMEOUT return alert @@ -25,4 +27,4 @@ class Timeout(PluginBase): return def status_change(self, alert, status, text): - return \ No newline at end of file + return diff --git a/plugins/timeout/setup.py b/plugins/timeout/setup.py index 3979718..f1fb5ea 100644 --- a/plugins/timeout/setup.py +++ b/plugins/timeout/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '4.0.3' setup( - name="alerta-timeout", + name='alerta-timeout', version=version, description='Alerta plugin to permit a global custom timeout to be supplied via alerta config', url='https://github.com/alerta/alerta-contrib', @@ -20,4 +20,4 @@ setup( 'timeout = alerta_timeout:Timeout' ] } -) \ No newline at end of file +) diff --git a/plugins/twilio/alerta_twilio_sms.py b/plugins/twilio/alerta_twilio_sms.py index 14114f5..d84862c 100644 --- a/plugins/twilio/alerta_twilio_sms.py +++ b/plugins/twilio/alerta_twilio_sms.py @@ -1,22 +1,26 @@ - import logging import os -from twilio.rest import Client + +from alerta.plugins import PluginBase from twilio.base.exceptions import TwilioRestException +from twilio.rest import Client try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase LOG = logging.getLogger('alerta.plugins.twilio') -TWILIO_ACCOUNT_SID = os.environ.get('TWILIO_ACCOUNT_SID') or app.config['TWILIO_ACCOUNT_SID'] -TWILIO_AUTH_TOKEN = os.environ.get('TWILIO_AUTH_TOKEN') or app.config['TWILIO_AUTH_TOKEN'] +TWILIO_ACCOUNT_SID = os.environ.get( + 'TWILIO_ACCOUNT_SID') or app.config['TWILIO_ACCOUNT_SID'] +TWILIO_AUTH_TOKEN = os.environ.get( + 'TWILIO_AUTH_TOKEN') or app.config['TWILIO_AUTH_TOKEN'] -TWILIO_TO_NUMBER = os.environ.get('TWILIO_TO_NUMBER') or app.config['TWILIO_TO_NUMBER'] -TWILIO_FROM_NUMBER = os.environ.get('TWILIO_FROM_NUMBER') or app.config['TWILIO_FROM_NUMBER'] +TWILIO_TO_NUMBER = os.environ.get( + 'TWILIO_TO_NUMBER') or app.config['TWILIO_TO_NUMBER'] +TWILIO_FROM_NUMBER = os.environ.get( + 'TWILIO_FROM_NUMBER') or app.config['TWILIO_FROM_NUMBER'] class SendSMSMessage(PluginBase): @@ -29,20 +33,22 @@ class SendSMSMessage(PluginBase): if alert.repeat: return - message = "%s: %s alert for %s - %s is %s" % ( + message = '{}: {} alert for {} - {} is {}'.format( alert.environment, alert.severity.capitalize(), ','.join(alert.service), alert.resource, alert.event ) client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN) for twilio_to in TWILIO_TO_NUMBER.split(','): - LOG.debug('Twilio SMS: Send message from {}, to {}'.format(TWILIO_FROM_NUMBER, twilio_to)) + LOG.debug('Twilio SMS: Send message from {}, to {}'.format( + TWILIO_FROM_NUMBER, twilio_to)) try: - message = client.messages.create(body=message, to=twilio_to, from_=TWILIO_FROM_NUMBER) + message = client.messages.create( + body=message, to=twilio_to, from_=TWILIO_FROM_NUMBER) except TwilioRestException as e: LOG.error('Twilio SMS: ERROR - {}'.format(str(e))) else: - LOG.info("Twilio SMS: Message ID: %s", message.sid) + LOG.info('Twilio SMS: Message ID: %s', message.sid) def status_change(self, alert, status, text): return diff --git a/plugins/twilio/setup.py b/plugins/twilio/setup.py index 9e534d7..a654b72 100644 --- a/plugins/twilio/setup.py +++ b/plugins/twilio/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.4.0' setup( - name="alerta-twilio", + name='alerta-twilio', version=version, description='Alerta plugin for Twilio SMS', url='https://github.com/alerta/alerta-contrib', diff --git a/plugins/zabbix/README.md b/plugins/zabbix/README.md index d73d960..6fd3c17 100644 --- a/plugins/zabbix/README.md +++ b/plugins/zabbix/README.md @@ -83,7 +83,3 @@ License ------- Copyright (c) 2017 Nick Satterly. Available under the MIT License. - - - - diff --git a/plugins/zabbix/alerta_zabbix.py b/plugins/zabbix/alerta_zabbix.py index 498b231..a05dd27 100644 --- a/plugins/zabbix/alerta_zabbix.py +++ b/plugins/zabbix/alerta_zabbix.py @@ -1,22 +1,24 @@ - import logging import os +from alerta.plugins import PluginBase +from pyzabbix import ZabbixAPI, ZabbixAPIException + try: from alerta.plugins import app # alerta >= 5.0 except ImportError: from alerta.app import app # alerta < 5.0 -from alerta.plugins import PluginBase -from pyzabbix import ZabbixAPI, ZabbixAPIException LOG = logging.getLogger('alerta.plugins.zabbix') DEFAULT_ZABBIX_API_URL = 'http://localhost:10080' -ZABBIX_API_URL = os.environ.get('ZABBIX_API_URL') or app.config.get('ZABBIX_API_URL', None) +ZABBIX_API_URL = os.environ.get( + 'ZABBIX_API_URL') or app.config.get('ZABBIX_API_URL', None) ZABBIX_USER = os.environ.get('ZABBIX_USER') or app.config['ZABBIX_USER'] -ZABBIX_PASSWORD = os.environ.get('ZABBIX_PASSWORD') or app.config['ZABBIX_PASSWORD'] +ZABBIX_PASSWORD = os.environ.get( + 'ZABBIX_PASSWORD') or app.config['ZABBIX_PASSWORD'] # See https://www.zabbix.com/documentation/4.0/manual/api/reference/event/acknowledge @@ -40,7 +42,7 @@ class ZabbixEventAck(PluginBase): if alert.event_type != 'zabbixAlert': return - if alert.status == status or not status in ['ack', 'closed']: + if alert.status == status or status not in ['ack', 'closed']: return event_id = alert.attributes.get('eventId', None) @@ -51,31 +53,38 @@ class ZabbixEventAck(PluginBase): return # login to zabbix - zabbix_api_url = ZABBIX_API_URL or alert.attributes.get('zabbixUrl', DEFAULT_ZABBIX_API_URL) + zabbix_api_url = ZABBIX_API_URL or alert.attributes.get( + 'zabbixUrl', DEFAULT_ZABBIX_API_URL) self.zapi = ZabbixAPI(zabbix_api_url) self.zapi.login(ZABBIX_USER, ZABBIX_PASSWORD) - LOG.debug('Zabbix: acknowledge (%s) event=%s, resource=%s (triggerId=%s, eventId=%s) ', status, alert.event, alert.resource, trigger_id, event_id) + LOG.debug('Zabbix: acknowledge (%s) event=%s, resource=%s (triggerId=%s, eventId=%s) ', + status, alert.event, alert.resource, trigger_id, event_id) if status == 'ack': try: - r = self.zapi.event.get(objectids=trigger_id, acknowledged=False, output='extend', sortfield='clock', sortorder='DESC', limit=10) + r = self.zapi.event.get(objectids=trigger_id, acknowledged=False, + output='extend', sortfield='clock', sortorder='DESC', limit=10) event_ids = [e['eventid'] for e in r] except ZabbixAPIException: - LOG.error(f"No eventids retrieved from Zabbix for {trigger_id}") + LOG.error( + f'No eventids retrieved from Zabbix for {trigger_id}') return - LOG.debug('Zabbix: status=ack; triggerId %s => eventIds %s', trigger_id, ','.join(event_ids)) + LOG.debug('Zabbix: status=ack; triggerId %s => eventIds %s', + trigger_id, ','.join(event_ids)) try: LOG.debug('Zabbix: ack all events for trigger...') - r = self.zapi.event.acknowledge(eventids=event_ids, action=(ACTION_ACK | ACTION_MSG), message='%s: %s' % (status, text)) + r = self.zapi.event.acknowledge(eventids=event_ids, action=( + ACTION_ACK | ACTION_MSG), message='{}: {}'.format(status, text)) except ZabbixAPIException: try: LOG.debug('Zabbix: ack all failed, ack only the one event') - r = self.zapi.event.acknowledge(eventids=event_id, action=(ACTION_ACK | ACTION_MSG), message='%s: %s' % (status, text)) + r = self.zapi.event.acknowledge(eventids=event_id, action=( + ACTION_ACK | ACTION_MSG), message='{}: {}'.format(status, text)) except ZabbixAPIException as e: - raise RuntimeError("Zabbix: ERROR - %s", e) + raise RuntimeError('Zabbix: ERROR - %s', e) finally: self.zapi.do_request('user.logout') @@ -85,23 +94,29 @@ class ZabbixEventAck(PluginBase): elif status == 'closed': try: - r = self.zapi.event.get(objectids=trigger_id, output='extend', sortfield='clock', sortorder='DESC', limit=10) + r = self.zapi.event.get( + objectids=trigger_id, output='extend', sortfield='clock', sortorder='DESC', limit=10) event_ids = [e['eventid'] for e in r] except ZabbixAPIException: - LOG.error(f"No eventids retrieved from Zabbix for {trigger_id}") + LOG.error( + f'No eventids retrieved from Zabbix for {trigger_id}') return - LOG.debug('Zabbix: status=closed; triggerId %s => eventIds %s', trigger_id, ','.join(event_ids)) + LOG.debug('Zabbix: status=closed; triggerId %s => eventIds %s', + trigger_id, ','.join(event_ids)) try: LOG.debug('Zabbix: close all events for trigger...') - r = self.zapi.event.acknowledge(eventids=event_ids, action=(ACTION_CLOSE | ACTION_MSG), message='%s: %s' % (status, text)) + r = self.zapi.event.acknowledge(eventids=event_ids, action=( + ACTION_CLOSE | ACTION_MSG), message='{}: {}'.format(status, text)) except ZabbixAPIException: try: - LOG.debug('Zabbix: ack all failed, close only the one event') - r = self.zapi.event.acknowledge(eventids=event_id, action=(ACTION_CLOSE | ACTION_MSG), message='%s: %s' % (status, text)) + LOG.debug( + 'Zabbix: ack all failed, close only the one event') + r = self.zapi.event.acknowledge(eventids=event_id, action=( + ACTION_CLOSE | ACTION_MSG), message='{}: {}'.format(status, text)) except ZabbixAPIException as e: - raise RuntimeError("Zabbix: ERROR - %s", e) + raise RuntimeError('Zabbix: ERROR - %s', e) finally: self.zapi.do_request('user.logout') diff --git a/plugins/zabbix/setup.py b/plugins/zabbix/setup.py index a68b012..bbf21f5 100644 --- a/plugins/zabbix/setup.py +++ b/plugins/zabbix/setup.py @@ -1,10 +1,9 @@ - -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.1.2' setup( - name="alerta-zabbix", + name='alerta-zabbix', version=version, description='Alerta plugin for Zabbix', url='https://github.com/alerta/alerta-contrib', diff --git a/pylintrc b/pylintrc index c29efc4..a4de30a 100644 --- a/pylintrc +++ b/pylintrc @@ -247,4 +247,3 @@ valid-classmethod-first-arg=cls # Exceptions that will emit a warning when being caught. Defaults to # "Exception" overgeneral-exceptions=Exception - diff --git a/requirements-dev.txt b/requirements-dev.txt index 2c04c27..5da3c92 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,14 +1,14 @@ -alerta-server[postgres] -alerta-server[mongodb] alerta +alerta-server[mongodb] +alerta-server[postgres] kombu -six mock mypy==1.0.1 pre-commit==2.20.0 PyJWT>=2.0.0 pylint==2.16.2 -pytest>=5.4.3 pytest-cov +pytest>=5.4.3 python-dotenv requests_mock +six diff --git a/webhooks/azuremonitor/alerta_azuremonitor.py b/webhooks/azuremonitor/alerta_azuremonitor.py index e109f51..a921490 100644 --- a/webhooks/azuremonitor/alerta_azuremonitor.py +++ b/webhooks/azuremonitor/alerta_azuremonitor.py @@ -1,9 +1,8 @@ import json -from dateutil.parser import parse as parse_date - from alerta.models.alert import Alert from alerta.webhooks import WebhookBase +from dateutil.parser import parse as parse_date SEVERITY_MAP = { '0': 'critical', # Critical @@ -32,7 +31,8 @@ class AzureMonitorWebhook(WebhookBase): if status == 'Resolved' or status == 'Deactivated': severity = 'ok' else: - severity = SEVERITY_MAP[context.get('severity', DEFAULT_SEVERITY_LEVEL)] + severity = SEVERITY_MAP[context.get( + 'severity', DEFAULT_SEVERITY_LEVEL)] resource = context['resourceName'] event = context['name'] @@ -40,7 +40,7 @@ class AzureMonitorWebhook(WebhookBase): service = [context['resourceType']] group = context['resourceGroupName'] tags = [] if payload['data']['properties'] is None else ['{}={}'.format(k, v) for k, v in - payload['data']['properties'].items()] + payload['data']['properties'].items()] create_time = parse_date(context['timestamp']) if payload['schemaId'] == 'AzureMonitorMetricAlert': diff --git a/webhooks/azuremonitor/setup.py b/webhooks/azuremonitor/setup.py index 26e5661..18e0e1d 100644 --- a/webhooks/azuremonitor/setup.py +++ b/webhooks/azuremonitor/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.2' setup( - name="alerta-azure-monitor", + name='alerta-azure-monitor', version=version, description='Alerta webhook for Azure Monitor', url='https://github.com/alerta/alerta-contrib', diff --git a/webhooks/azuremonitor/test_azuremonitor.py b/webhooks/azuremonitor/test_azuremonitor.py index f1ecf84..c0a79af 100644 --- a/webhooks/azuremonitor/test_azuremonitor.py +++ b/webhooks/azuremonitor/test_azuremonitor.py @@ -1,4 +1,3 @@ - import json import unittest @@ -17,10 +16,10 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): self.app = create_app(test_config) self.client = self.app.test_client() - custom_webhooks.webhooks['azuremonitor'] = alerta_azuremonitor.AzureMonitorWebhook() + custom_webhooks.webhooks['azuremonitor'] = alerta_azuremonitor.AzureMonitorWebhook( + ) def test_azuremonitor_webhook_classic(self): - """ See https://docs.microsoft.com/en-us/azure/azure-monitor/platform/alerts-webhooks """ classic_metric_alert = r""" @@ -56,7 +55,8 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): } """ - response = self.client.post('/webhooks/azuremonitor', data=classic_metric_alert, content_type='application/json') + response = self.client.post( + '/webhooks/azuremonitor', data=classic_metric_alert, content_type='application/json') self.assertEqual(response.status_code, 201, response.data) data = json.loads(response.data.decode('utf-8')) @@ -68,8 +68,10 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): self.assertEqual(data['alert']['service'], ['microsoft.foo/sites']) self.assertEqual(data['alert']['group'], 'useast') self.assertEqual(data['alert']['value'], '10 Requests') - self.assertEqual(data['alert']['text'], 'CRITICAL: 10 Requests (GreaterThanOrEqual 10)') - self.assertEqual(sorted(data['alert']['tags']), ['key1=value1', 'key2=value2']) + self.assertEqual(data['alert']['text'], + 'CRITICAL: 10 Requests (GreaterThanOrEqual 10)') + self.assertEqual(sorted(data['alert']['tags']), [ + 'key1=value1', 'key2=value2']) classic_metric_alert = r""" { @@ -108,7 +110,8 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): } """ - response = self.client.post('/webhooks/azuremonitor', data=classic_metric_alert, content_type='application/json') + response = self.client.post( + '/webhooks/azuremonitor', data=classic_metric_alert, content_type='application/json') self.assertEqual(response.status_code, 201, response.data) data = json.loads(response.data.decode('utf-8')) @@ -117,10 +120,12 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): self.assertEqual(data['alert']['environment'], 'Production') self.assertEqual(data['alert']['severity'], 'critical') self.assertEqual(data['alert']['status'], 'open') - self.assertEqual(data['alert']['service'], ['microsoft.compute/virtualmachines']) + self.assertEqual(data['alert']['service'], [ + 'microsoft.compute/virtualmachines']) self.assertEqual(data['alert']['group'], 'montest') self.assertEqual(data['alert']['value'], '1032190976 Memory available') - self.assertEqual(data['alert']['text'], 'CRITICAL: 1032190976 Memory available (GreaterThan 2)') + self.assertEqual( + data['alert']['text'], 'CRITICAL: 1032190976 Memory available (GreaterThan 2)') self.assertEqual(sorted(data['alert']['tags']), [ 'customId=wd39ue9832ue9iuhd9iuewhd9edh', 'hello1=World1!', @@ -129,7 +134,6 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): ) def test_azuremonitor_webhook_new(self): - """ See https://docs.microsoft.com/en-us/azure/azure-monitor/platform/alerts-metric-near-real-time """ new_metric_alert = r""" @@ -181,7 +185,8 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): } """ - response = self.client.post('/webhooks/azuremonitor', data=new_metric_alert, content_type='application/json') + response = self.client.post( + '/webhooks/azuremonitor', data=new_metric_alert, content_type='application/json') self.assertEqual(response.status_code, 201, response.data) data = json.loads(response.data.decode('utf-8')) @@ -190,11 +195,14 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): self.assertEqual(data['alert']['environment'], 'Production') self.assertEqual(data['alert']['severity'], 'informational') self.assertEqual(data['alert']['status'], 'open') - self.assertEqual(data['alert']['service'], ['Microsoft.Storage/storageAccounts']) + self.assertEqual(data['alert']['service'], [ + 'Microsoft.Storage/storageAccounts']) self.assertEqual(data['alert']['group'], 'Contoso') self.assertEqual(data['alert']['value'], '1 Transactions') - self.assertEqual(data['alert']['text'], 'INFORMATIONAL: 1 Transactions (GreaterThan 0)') - self.assertEqual(sorted(data['alert']['tags']), ['key1=value1', 'key2=value2']) + self.assertEqual(data['alert']['text'], + 'INFORMATIONAL: 1 Transactions (GreaterThan 0)') + self.assertEqual(sorted(data['alert']['tags']), [ + 'key1=value1', 'key2=value2']) new_metric_alert = r""" { @@ -244,7 +252,8 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): } """ - response = self.client.post('/webhooks/azuremonitor?environment=Development', data=new_metric_alert, content_type='application/json') + response = self.client.post('/webhooks/azuremonitor?environment=Development', + data=new_metric_alert, content_type='application/json') self.assertEqual(response.status_code, 201, response.data) data = json.loads(response.data.decode('utf-8')) @@ -253,8 +262,10 @@ class AzureMonitoringWebhookTestCase(unittest.TestCase): self.assertEqual(data['alert']['environment'], 'Development') self.assertEqual(data['alert']['severity'], 'ok') self.assertEqual(data['alert']['status'], 'closed') - self.assertEqual(data['alert']['service'], ['Microsoft.Compute/virtualMachines']) + self.assertEqual(data['alert']['service'], [ + 'Microsoft.Compute/virtualMachines']) self.assertEqual(data['alert']['group'], 'Web') self.assertEqual(data['alert']['value'], '85 Percentage CPU') - self.assertEqual(data['alert']['text'], 'OK: 85 Percentage CPU (GreaterThan 90)') + self.assertEqual(data['alert']['text'], + 'OK: 85 Percentage CPU (GreaterThan 90)') self.assertEqual(sorted(data['alert']['tags']), []) diff --git a/webhooks/fail2ban/alerta_fail2ban.py b/webhooks/fail2ban/alerta_fail2ban.py index b369a97..942329f 100644 --- a/webhooks/fail2ban/alerta_fail2ban.py +++ b/webhooks/fail2ban/alerta_fail2ban.py @@ -1,7 +1,8 @@ +import json from alerta.models.alert import Alert from alerta.webhooks import WebhookBase -import json + class Fail2BanWebhook(WebhookBase): @@ -9,8 +10,8 @@ class Fail2BanWebhook(WebhookBase): # Default parameters environment = 'Production' - severity ='security' - group ='Fail2Ban' + severity = 'security' + group = 'Fail2Ban' text = '' tags = [] attributes = {} diff --git a/webhooks/fail2ban/setup.py b/webhooks/fail2ban/setup.py index 6647953..1311ce4 100644 --- a/webhooks/fail2ban/setup.py +++ b/webhooks/fail2ban/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '1.0.0' setup( - name="alerta-fail2ban", + name='alerta-fail2ban', version=version, description='Alerta Webhook for Fail2Ban', url='https://github.com/alerta/alerta-contrib', diff --git a/webhooks/mailgun/alerta_mailgun.py b/webhooks/mailgun/alerta_mailgun.py index 6a59276..1a9ca4b 100644 --- a/webhooks/mailgun/alerta_mailgun.py +++ b/webhooks/mailgun/alerta_mailgun.py @@ -1,26 +1,25 @@ +import json from alerta.models.alert import Alert from alerta.webhooks import WebhookBase -import json -import datetime class MailgunWebhook(WebhookBase): def incoming(self, query_string, payload): - + # Load variables from querystring try: environment = query_string['environment'] - except: + except Exception: environment = 'Production' try: severity = query_string['severity'] - except: + except Exception: severity = 'major' try: group = query_string['group'] - except: + except Exception: group = 'Email' return Alert( diff --git a/webhooks/mailgun/setup.py b/webhooks/mailgun/setup.py index 83570c2..85babf9 100644 --- a/webhooks/mailgun/setup.py +++ b/webhooks/mailgun/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.0' setup( - name="alerta-mailgun", + name='alerta-mailgun', version=version, description='Alerta webhook for Mailgun', url='https://github.com/alerta/alerta-contrib', diff --git a/webhooks/msteams/alerta_msteamswebhook.py b/webhooks/msteams/alerta_msteamswebhook.py index 3b0a4b0..164bd82 100644 --- a/webhooks/msteams/alerta_msteamswebhook.py +++ b/webhooks/msteams/alerta_msteamswebhook.py @@ -1,11 +1,11 @@ - -from flask import current_app, g, jsonify, request, make_response +from uuid import UUID from alerta.models.alert import Alert from alerta.models.blackout import Blackout from alerta.utils.audit import write_audit_trail from alerta.webhooks import WebhookBase -from uuid import UUID +from flask import current_app, g, jsonify, make_response, request + class MsteamsWebhook(WebhookBase): @@ -14,13 +14,15 @@ class MsteamsWebhook(WebhookBase): # instead we're relying on alerta to validate X-API-Key header action = payload.get('action', 'missing') - if action not in [ 'ack', 'close', 'blackout' ]: - resp = make_response(jsonify(status='error', message='Invalid action'), 400) + if action not in ['ack', 'close', 'blackout']: + resp = make_response( + jsonify(status='error', message='Invalid action'), 400) return resp - if action in [ 'ack', 'close' ]: + if action in ['ack', 'close']: alert_id = payload.get('alert_id', None) - err = make_response(jsonify(status='error', message='Missing/invalid alert_id'), 400) + err = make_response( + jsonify(status='error', message='Missing/invalid alert_id'), 400) if not alert_id: return err @@ -32,13 +34,17 @@ class MsteamsWebhook(WebhookBase): except Exception: return err - alert = Alert.find_by_id(alert_id, customers=g.get('customers', None)) + alert = Alert.find_by_id( + alert_id, customers=g.get('customers', None)) if not alert: return err else: - alert.from_action(action, text='status changed via MS Teams webhook') - resp = make_response(jsonify(status='ok', message='status changed'), 200) - resp.headers['CARD-ACTION-STATUS'] = 'Alert {}d'.format(action.capitalize()) + alert.from_action( + action, text='status changed via MS Teams webhook') + resp = make_response( + jsonify(status='ok', message='status changed'), 200) + resp.headers['CARD-ACTION-STATUS'] = 'Alert {}d'.format( + action.capitalize()) text = 'alert updated via msteams webhook' write_audit_trail.send(current_app._get_current_object(), event='webhook-updated', message=text, @@ -51,7 +57,8 @@ class MsteamsWebhook(WebhookBase): event = payload.get('event', None) if environment and resource and event: - duration = payload.get('duration', None) or current_app.config['BLACKOUT_DURATION'] + duration = payload.get( + 'duration', None) or current_app.config['BLACKOUT_DURATION'] try: if not duration or float(duration) < 0.0: # Should not happen: set default duration @@ -59,13 +66,17 @@ class MsteamsWebhook(WebhookBase): except ValueError: # Should not happen: set default duration duration = 3600 - blackout = Blackout(environment, resource=resource, event=event, duration=duration) + blackout = Blackout( + environment, resource=resource, event=event, duration=duration) blackout.create() - resp = make_response(jsonify(status='ok', message='blackout created'), 201) - resp.headers['CARD-ACTION-STATUS'] = 'Blackout created for {0:.1f} hours'.format(float(duration) / 3600) + resp = make_response( + jsonify(status='ok', message='blackout created'), 201) + resp.headers['CARD-ACTION-STATUS'] = 'Blackout created for {:.1f} hours'.format( + float(duration) / 3600) else: # Missging env, resource or event - resp = make_response(jsonify(status='error', message='Missing blackout params'), 412) + resp = make_response( + jsonify(status='error', message='Missing blackout params'), 412) return resp diff --git a/webhooks/msteams/setup.py b/webhooks/msteams/setup.py index 61107ab..bd5071b 100644 --- a/webhooks/msteams/setup.py +++ b/webhooks/msteams/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.0' setup( - name="alerta-msteamswebhook", + name='alerta-msteamswebhook', version=version, description='Alerta webhook for MS Teams', url='https://github.com/alerta/alerta-contrib', diff --git a/webhooks/msteams/test_msteamswebhook.py b/webhooks/msteams/test_msteamswebhook.py index 06cc5f2..bcf2cf2 100644 --- a/webhooks/msteams/test_msteamswebhook.py +++ b/webhooks/msteams/test_msteamswebhook.py @@ -1,11 +1,9 @@ - import json import unittest -from alerta.app import create_app, custom_webhooks -from uuid import uuid4 - import alerta_msteamswebhook +from alerta.app import create_app, custom_webhooks + class MsteamsWebhookTestCase(unittest.TestCase): @@ -17,7 +15,8 @@ class MsteamsWebhookTestCase(unittest.TestCase): } self.app = create_app(test_config) self.client = self.app.test_client() - custom_webhooks.webhooks['msteams'] = alerta_msteamswebhook.MsteamsWebhook() + custom_webhooks.webhooks['msteams'] = alerta_msteamswebhook.MsteamsWebhook( + ) self.headers = { 'Content-Type': 'application/json' @@ -41,33 +40,35 @@ class MsteamsWebhookTestCase(unittest.TestCase): } """ - payload_blackout = r""" - { - "action": "blackout", - "environment": "Production", - "resource": "webhooktest1", - "event": "DiskUtilHigh" - } - """ + # payload_blackout = r""" + # { + # "action": "blackout", + # "environment": "Production", + # "resource": "webhooktest1", + # "event": "DiskUtilHigh" + # } + # """ # ack # TODO: needs to mock Alert.find_by_id / alert.set_status - #response = self.client.post('/webhooks/msteams', data=payload_cmd % ('ack', self.alert_id), content_type='application/json', headers=self.headers) - #self.assertEqual(response.status_code, 200) - #data = json.loads(response.data.decode('utf-8')) - #self.assertEqual(data['status'], 'ok') - #self.assertEqual(data['message'], 'status changed') - #self.assertTrue(bool(response.headers.get('CARD-ACTION-STATUS', False))) + # response = self.client.post('/webhooks/msteams', data=payload_cmd % ('ack', self.alert_id), content_type='application/json', headers=self.headers) + # self.assertEqual(response.status_code, 200) + # data = json.loads(response.data.decode('utf-8')) + # self.assertEqual(data['status'], 'ok') + # self.assertEqual(data['message'], 'status changed') + # self.assertTrue(bool(response.headers.get('CARD-ACTION-STATUS', False))) # ack with missing alert_id - response = self.client.post('/webhooks/msteams', data=payload_invalidcmd % 'ack', content_type='application/json', headers=self.headers) + response = self.client.post('/webhooks/msteams', data=payload_invalidcmd % + 'ack', content_type='application/json', headers=self.headers) self.assertEqual(response.status_code, 400) data = json.loads(response.data.decode('utf-8')) self.assertEqual(data['status'], 'error') self.assertEqual(data['message'], 'Missing/invalid alert_id') # ack with bogus alert_id - response = self.client.post('/webhooks/msteams', data=payload_cmd % ('ack', '7a0e3ee1-fbaa-45th-isis-bogus'), content_type='application/json', headers=self.headers) + response = self.client.post('/webhooks/msteams', data=payload_cmd % ( + 'ack', '7a0e3ee1-fbaa-45th-isis-bogus'), content_type='application/json', headers=self.headers) self.assertEqual(response.status_code, 400) data = json.loads(response.data.decode('utf-8')) self.assertEqual(data['status'], 'error') @@ -75,19 +76,18 @@ class MsteamsWebhookTestCase(unittest.TestCase): # close alert # TODO: needs to mock Alert.find_by_id / alert.set_status - #response = self.client.post('/webhooks/msteams', data=payload_cmd % ('close', self.alert_id), content_type='application/json', headers=self.headers) - #self.assertEqual(response.status_code, 200) - #data = json.loads(response.data.decode('utf-8')) - #self.assertEqual(data['status'], 'ok') - #self.assertEqual(data['message'], 'status changed') - #self.assertTrue(bool(response.headers.get('CARD-ACTION-STATUS', False))) + # response = self.client.post('/webhooks/msteams', data=payload_cmd % ('close', self.alert_id), content_type='application/json', headers=self.headers) + # self.assertEqual(response.status_code, 200) + # data = json.loads(response.data.decode('utf-8')) + # self.assertEqual(data['status'], 'ok') + # self.assertEqual(data['message'], 'status changed') + # self.assertTrue(bool(response.headers.get('CARD-ACTION-STATUS', False))) # create blackout # TODO: needs to mock: blackout.create() - #response = self.client.post('/webhooks/msteams', data=payload_blackout, content_type='application/json', headers=self.headers) - #self.assertEqual(response.status_code, 201) - #data = json.loads(response.data.decode('utf-8')) - #self.assertEqual(data['status'], 'ok') - #self.assertEqual(data['message'], 'blackout created') - #self.assertTrue(bool(response.headers.get('CARD-ACTION-STATUS', False))) - + # response = self.client.post('/webhooks/msteams', data=payload_blackout, content_type='application/json', headers=self.headers) + # self.assertEqual(response.status_code, 201) + # data = json.loads(response.data.decode('utf-8')) + # self.assertEqual(data['status'], 'ok') + # self.assertEqual(data['message'], 'blackout created') + # self.assertTrue(bool(response.headers.get('CARD-ACTION-STATUS', False))) diff --git a/webhooks/query/README.md b/webhooks/query/README.md index f255c47..89db238 100644 --- a/webhooks/query/README.md +++ b/webhooks/query/README.md @@ -23,7 +23,7 @@ discover them. Configuration ------------- -You can specify all the parameters on the query except Attributes for the moment. +You can specify all the parameters on the query except Attributes for the moment. https://[ALERTA-URL]/api/webhooks/query?api-key=[YOUR-API-KEY]&service=TheForce&severity=critical&resource=Alderaan&event=SomethingTerribleHappened&text=AGreatDisturbanceInTheForce&tags=Force,Alderaan,DeathStar&origin=ObiWanKenobi&group=Jedi&timeout=300 References diff --git a/webhooks/query/alerta_query.py b/webhooks/query/alerta_query.py index b4df93e..6c26b58 100644 --- a/webhooks/query/alerta_query.py +++ b/webhooks/query/alerta_query.py @@ -1,4 +1,3 @@ - from alerta.models.alert import Alert from alerta.webhooks import WebhookBase @@ -8,60 +7,60 @@ class QueryWebhook(WebhookBase): def incoming(self, query_string, payload): # Load variables from querystring - # resource + # resource try: resource = query_string['resource'] - except: + except Exception: resource = 'QueryDefaultResource' - # environment + # environment try: environment = query_string['environment'] - except: + except Exception: environment = 'Production' - # severity + # severity try: severity = query_string['severity'] - except: + except Exception: severity = 'major' - # group + # group try: group = query_string['group'] - except: + except Exception: group = 'QueryDefaultGroup' - # event + # event try: event = query_string['event'] - except: + except Exception: event = 'QueryDefaultEvent' - # service (Must be an array) + # service (Must be an array) try: service = [query_string['service']] - except: + except Exception: service = ['QueryDefaultService'] - # value + # value try: value = query_string['value'] - except: + except Exception: value = '' - # text + # text try: text = query_string['text'] - except: + except Exception: text = '' - # tags (Must be an array) + # tags (Must be an array) try: tags = query_string['tags'].split(',') - except: + except Exception: tags = [] - # origin + # origin try: origin = query_string['origin'] - except: + except Exception: origin = 'QueryDefaultOrigin' - # timeout + # timeout try: timeout = int(query_string['timeout']) - except: + except Exception: timeout = 86400 return Alert( @@ -75,6 +74,6 @@ class QueryWebhook(WebhookBase): text=text, tags=tags, origin=origin, - timeout=timeout, + timeout=timeout, raw_data=query_string ) diff --git a/webhooks/query/setup.py b/webhooks/query/setup.py index 73bbcea..1a69960 100644 --- a/webhooks/query/setup.py +++ b/webhooks/query/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '1.0.0' setup( - name="alerta-query", + name='alerta-query', version=version, description='Alerta Generic Webhook by query parameters', url='https://github.com/alerta/alerta-contrib', @@ -21,4 +21,3 @@ setup( ] } ) - diff --git a/webhooks/sentry/alerta_sentry.py b/webhooks/sentry/alerta_sentry.py index 9db61c5..9da5ddf 100644 --- a/webhooks/sentry/alerta_sentry.py +++ b/webhooks/sentry/alerta_sentry.py @@ -1,4 +1,3 @@ - from alerta.models.alert import Alert from alerta.webhooks import WebhookBase @@ -34,7 +33,8 @@ class SentryWebhook(WebhookBase): value=payload['level'], text='{} {}'.format(payload['message'], payload['url']), tags=['{}={}'.format(k, v) for k, v in payload['event']['tags']], - attributes={'modules': ['{}=={}'.format(k, v) for k, v in payload['event']['modules'].items()]}, + attributes={'modules': ['{}=={}'.format( + k, v) for k, v in payload['event']['modules'].items()]}, origin='sentry.io', raw_data=str(payload) ) diff --git a/webhooks/sentry/setup.py b/webhooks/sentry/setup.py index bb4f7fe..6e11593 100644 --- a/webhooks/sentry/setup.py +++ b/webhooks/sentry/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '5.0.0' setup( - name="alerta-sentry", + name='alerta-sentry', version=version, description='Alerta webhook for Sentry', url='https://github.com/alerta/alerta-contrib', diff --git a/webhooks/sentry/test_sentry.py b/webhooks/sentry/test_sentry.py index 41fa838..0928f97 100644 --- a/webhooks/sentry/test_sentry.py +++ b/webhooks/sentry/test_sentry.py @@ -1,10 +1,8 @@ - import json import unittest -from alerta.app import create_app, custom_webhooks - import alerta_sentry +from alerta.app import create_app, custom_webhooks class SentryWebhookTestCase(unittest.TestCase): @@ -405,17 +403,22 @@ class SentryWebhookTestCase(unittest.TestCase): } """ - response = self.client.post('/webhooks/sentry', data=payload, content_type='application/json') + response = self.client.post( + '/webhooks/sentry', data=payload, content_type='application/json') self.assertEqual(response.status_code, 201) data = json.loads(response.data.decode('utf-8')) - self.assertEqual(data['alert']['resource'], 'raven.scripts.runner in main') - self.assertEqual(data['alert']['event'], '0476467adf8f499ea795a48fcc4bf290') + self.assertEqual(data['alert']['resource'], + 'raven.scripts.runner in main') + self.assertEqual(data['alert']['event'], + '0476467adf8f499ea795a48fcc4bf290') self.assertEqual(data['alert']['environment'], 'Production') self.assertEqual(data['alert']['value'], 'error') - self.assertEqual(data['alert']['text'], 'This is an example Python exception https://sentry.io/alertaio/alerta5/issues/541485531/') - self.assertEqual(sorted(data['alert']['tags']), ['browser=Chrome 28.0', 'device=Other', 'level=error', 'os=Windows 8', 'sentry:user=id:1', 'url=http://example.com/foo']) - self.assertEqual(data['alert']['attributes']['modules'], ['my.package==1.0.0']) - + self.assertEqual( + data['alert']['text'], 'This is an example Python exception https://sentry.io/alertaio/alerta5/issues/541485531/') + self.assertEqual(sorted(data['alert']['tags']), ['browser=Chrome 28.0', 'device=Other', + 'level=error', 'os=Windows 8', 'sentry:user=id:1', 'url=http://example.com/foo']) + self.assertEqual(data['alert']['attributes'] + ['modules'], ['my.package==1.0.0']) def test_sentry_webhook_v9(self): @@ -822,13 +825,19 @@ class SentryWebhookTestCase(unittest.TestCase): } """ - response = self.client.post('/webhooks/sentry', data=payload_v9, content_type='application/json') + response = self.client.post( + '/webhooks/sentry', data=payload_v9, content_type='application/json') self.assertEqual(response.status_code, 201) data = json.loads(response.data.decode('utf-8')) - self.assertEqual(data['alert']['resource'], 'raven.scripts.runner in main') - self.assertEqual(data['alert']['event'], '2c02659ec60b48f3b31f8de77e1a2510') + self.assertEqual(data['alert']['resource'], + 'raven.scripts.runner in main') + self.assertEqual(data['alert']['event'], + '2c02659ec60b48f3b31f8de77e1a2510') self.assertEqual(data['alert']['environment'], 'Production') self.assertEqual(data['alert']['value'], 'error') - self.assertEqual(data['alert']['text'], 'This is an example Python exception https://sentry.io/alertaio/alerta5/issues/541485531/') - self.assertEqual(sorted(data['alert']['tags']), ['browser.name=Chrome', 'browser=Chrome 28.0.1500', 'level=error', 'os.name=Windows 8', 'sentry:user=id:1', 'url=http://example.com/foo']) - self.assertEqual(data['alert']['attributes']['modules'], ['my.package==1.0.0']) + self.assertEqual( + data['alert']['text'], 'This is an example Python exception https://sentry.io/alertaio/alerta5/issues/541485531/') + self.assertEqual(sorted(data['alert']['tags']), ['browser.name=Chrome', 'browser=Chrome 28.0.1500', + 'level=error', 'os.name=Windows 8', 'sentry:user=id:1', 'url=http://example.com/foo']) + self.assertEqual(data['alert']['attributes'] + ['modules'], ['my.package==1.0.0']) diff --git a/webhooks/statuscake/README.md b/webhooks/statuscake/README.md index 8c87aef..94a8852 100644 --- a/webhooks/statuscake/README.md +++ b/webhooks/statuscake/README.md @@ -39,4 +39,3 @@ License ------- Copyright (c) 2020 Matthieu Serrepuy. Available under the MIT License. - diff --git a/webhooks/statuscake/alerta_statuscake.py b/webhooks/statuscake/alerta_statuscake.py index 8d8a5d7..ec73944 100644 --- a/webhooks/statuscake/alerta_statuscake.py +++ b/webhooks/statuscake/alerta_statuscake.py @@ -1,15 +1,18 @@ +import hashlib +import os + +from alerta.exceptions import RejectException from alerta.models.alert import Alert from alerta.webhooks import WebhookBase -from alerta.exceptions import RejectException -import os -import hashlib + class StatusCakeWebhook(WebhookBase): def incoming(self, query_string, payload): - alert_severity = os.environ.get('STATUSCAKE_DEFAULT_ALERT_SEVERITY', 'major') + alert_severity = os.environ.get( + 'STATUSCAKE_DEFAULT_ALERT_SEVERITY', 'major') - # If the statuscake username and apikey are provided + # If the statuscake username and apikey are provided # noqa: E265 # We can validate that the webhook call is valid statuscake_username = os.environ.get('STATUSCAKE_USERNAME') statuscake_apikey = os.environ.get('STATUSCAKE_APIKEY') @@ -19,7 +22,6 @@ class StatusCakeWebhook(WebhookBase): statuscake_token = hashlib.md5(decoded_token.encode()).hexdigest() if statuscake_token != payload['Token']: raise RejectException("Provided Token couldn't be verified") - if payload['Status'] == 'UP': severity = 'normal' @@ -34,7 +36,7 @@ class StatusCakeWebhook(WebhookBase): service=['StatusCake'], group='Application', value=payload['StatusCode'], - text="%s is down" % payload['URL'], + text='%s is down' % payload['URL'], tags=payload['Tags'].split(','), origin='statuscake', raw_data=str(payload) diff --git a/webhooks/statuscake/setup.py b/webhooks/statuscake/setup.py index 83a5f2c..5827a55 100644 --- a/webhooks/statuscake/setup.py +++ b/webhooks/statuscake/setup.py @@ -1,9 +1,9 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup version = '0.0.1' setup( - name="alerta-statuscake", + name='alerta-statuscake', version=version, description='Alerta webhook for statuscake', url='https://github.com/alerta/alerta-contrib', @@ -17,7 +17,7 @@ setup( zip_safe=True, entry_points={ 'alerta.webhooks': [ - 'statuscake = alerta_statuscake:StatusCakeWebhook' + 'statuscake = alerta_statuscake:StatusCakeWebhook' ] } )