mirror of
https://github.com/alerta/alerta-contrib.git
synced 2024-12-12 15:37:34 +00:00
196 lines
8.0 KiB
Python
196 lines
8.0 KiB
Python
import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
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
|
|
|
|
|
|
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)
|
|
|
|
|
|
class AlertmanagerSilence(PluginBase):
|
|
|
|
def __init__(self, name=None):
|
|
|
|
self.auth = (ALERTMANAGER_USERNAME,
|
|
ALERTMANAGER_PASSWORD) if ALERTMANAGER_USERNAME else None
|
|
|
|
super().__init__(name)
|
|
|
|
def pre_receive(self, alert):
|
|
return alert
|
|
|
|
def post_receive(self, alert):
|
|
return
|
|
|
|
def status_change(self, alert, status, text):
|
|
'''
|
|
If a silence exists for an open or closed alert we probably want to remove it
|
|
'''
|
|
if status in ('open', 'closed'):
|
|
|
|
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)
|
|
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)
|
|
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)
|
|
if status == 'closed':
|
|
LOG.warning('Status is now closed')
|
|
|
|
return alert
|
|
|
|
def take_action(self, alert: Alert, action: str, text: str, **kwargs) -> Any:
|
|
'''
|
|
Set silence in alertmanager.
|
|
'''
|
|
|
|
if alert.event_type != 'prometheusAlert':
|
|
return alert
|
|
|
|
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')
|
|
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]
|
|
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)
|
|
|
|
elif action == 'ack' and ALERTMANAGER_SILENCE_FROM_ACK:
|
|
|
|
if not ALERTMANAGER_SILENCE_DAYS:
|
|
silence_seconds = kwargs.get('timeout', alert.timeout)
|
|
else:
|
|
try:
|
|
silence_days = int(ALERTMANAGER_SILENCE_DAYS)
|
|
except Exception as e:
|
|
LOG.error(
|
|
"Alertmanager: Could not parse 'ALERTMANAGER_SILENCE_DAYS': %s", e)
|
|
raise RuntimeError(
|
|
"Could not parse 'ALERTMANAGER_SILENCE_DAYS': %s" % e)
|
|
silence_seconds = silence_days * 86400
|
|
|
|
LOG.debug('Alertmanager: Add silence for alertname=%s instance=%s timeout=%s',
|
|
alert.event, alert.resource, str(silence_seconds))
|
|
data = {
|
|
'matchers': [
|
|
{
|
|
'name': 'alertname',
|
|
'value': alert.event
|
|
},
|
|
{
|
|
'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'
|
|
}
|
|
|
|
# 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)
|
|
|
|
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)
|
|
LOG.debug('Alertmanager: %s - %s', r.status_code, r.text)
|
|
|
|
# example r={"status":"success","data":{"silenceId":8}}
|
|
try:
|
|
data = r.json().get('data', [])
|
|
if data:
|
|
silenceId = data['silenceId']
|
|
alert.attributes['silenceId'] = silenceId
|
|
else:
|
|
silenceId = alert.attributes.get('silenceId', 'unknown')
|
|
text = text + ' (silenced in Alertmanager)'
|
|
except Exception as 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)
|
|
|
|
silenceId = alert.attributes.get('silenceId', None)
|
|
if silenceId:
|
|
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)
|
|
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)
|
|
|
|
return alert
|