diff --git a/graphite_beacon/alerts.py b/graphite_beacon/alerts.py index 17604b5..b621a80 100644 --- a/graphite_beacon/alerts.py +++ b/graphite_beacon/alerts.py @@ -91,7 +91,7 @@ def __str__(self): """String representation.""" return "%s (%s)" % (self.name, self.interval) - def configure(self, name=None, rules=None, query=None, **options): + def configure(self, name=None, rules=None, query=None, override=None, **options): """Configure the alert.""" self.name = name if not name: @@ -105,6 +105,8 @@ def configure(self, name=None, rules=None, query=None, **options): assert query, "%s: Alert's query is invalid" % self.name self.query = query + self.override = override + interval_raw = options.get('interval', self.reactor.options['interval']) self.interval = TimeUnit.from_interval(interval_raw) diff --git a/graphite_beacon/handlers/cli.py b/graphite_beacon/handlers/cli.py index c92a504..96df023 100644 --- a/graphite_beacon/handlers/cli.py +++ b/graphite_beacon/handlers/cli.py @@ -18,7 +18,7 @@ def init_handler(self): self.whitelist = self.options.get('alerts_whitelist') assert self.command_template, 'Command line command is not defined.' - def notify(self, level, *args, **kwargs): + def notify(self, level, alert, *args, **kwargs): LOGGER.debug("Handler (%s) %s", self.name, level) def get_alert_name(*args): @@ -26,9 +26,14 @@ def get_alert_name(*args): # remove time characteristics e.g. (1minute) return name.rsplit(' ', 1)[0].strip() + command_template = self.command_template + if alert.override and self.name in alert.override: + override = alert.override[self.name] + command_template = override.get('command', command_template) + # Run only for whitelisted names if specified if not self.whitelist or get_alert_name(*args) in self.whitelist: - command = substitute_variables(self.command_template, level, *args, **kwargs) + command = substitute_variables(command_template, level, alert, *args, **kwargs) subprocess.Popen( command, shell=True, diff --git a/graphite_beacon/handlers/hipchat.py b/graphite_beacon/handlers/hipchat.py index 9a5802a..66d778d 100644 --- a/graphite_beacon/handlers/hipchat.py +++ b/graphite_beacon/handlers/hipchat.py @@ -31,16 +31,21 @@ def init_handler(self): self.client = hc.AsyncHTTPClient() @gen.coroutine - def notify(self, level, *args, **kwargs): + def notify(self, level, alert, *args, **kwargs): LOGGER.debug("Handler (%s) %s", self.name, level) + room = self.room + if alert.override and self.name in alert.override: + override = alert.override[self.name] + room = override.get('room', room) + data = { - 'message': self.get_short(level, *args, **kwargs).decode('UTF-8'), + 'message': self.get_short(level, alert, *args, **kwargs).decode('UTF-8'), 'notify': True, 'color': self.colors.get(level, 'gray'), 'message_format': 'text', } yield self.client.fetch('{url}/v2/room/{room}/notification?auth_token={token}'.format( - url=self.options.get('url'), room=self.room, token=self.key), headers={ + url=self.options.get('url'), room=room, token=self.key), headers={ 'Content-Type': 'application/json'}, method='POST', body=json.dumps(data)) diff --git a/graphite_beacon/handlers/http.py b/graphite_beacon/handlers/http.py index 32339a9..5d45c92 100644 --- a/graphite_beacon/handlers/http.py +++ b/graphite_beacon/handlers/http.py @@ -27,6 +27,15 @@ def init_handler(self): def notify(self, level, alert, value, target=None, ntype=None, rule=None): LOGGER.debug("Handler (%s) %s", self.name, level) + url = self.url + params = self.params + method = self.method + if alert.override and self.name in alert.override: + override = alert.override[self.name] + url = override.get('url', url) + params = override.get('params', params) + method = override.get('method', method) + message = self.get_short(level, alert, value, target=target, ntype=ntype, rule=rule) data = {'alert': alert.name, 'desc': message, 'level': level} if target: @@ -38,6 +47,7 @@ def notify(self, level, alert, value, target=None, ntype=None, rule=None): data['graph_url'] = alert.get_graph_url(target) data['value'] = value - data.update(self.params) + data.update(params) body = urllib.urlencode(data) - yield self.client.fetch(self.url, method=self.method, body=body) + yield self.client.fetch(url, method=method, body=body) + diff --git a/graphite_beacon/handlers/slack.py b/graphite_beacon/handlers/slack.py index 22b9b5c..792a0fa 100644 --- a/graphite_beacon/handlers/slack.py +++ b/graphite_beacon/handlers/slack.py @@ -24,13 +24,17 @@ class SlackHandler(AbstractHandler): 'normal': ':white_check_mark:', } + @staticmethod + def _make_channel_name(channel): + if channel and not channel.startswith(('#', '@')): + channel = '#' + channel + return channel + def init_handler(self): self.webhook = self.options.get('webhook') assert self.webhook, 'Slack webhook is not defined.' - self.channel = self.options.get('channel') - if self.channel and not self.channel.startswith(('#', '@')): - self.channel = '#' + self.channel + self.channel = self._make_channel_name(self.options.get('channel')) self.username = self.options.get('username') self.client = hc.AsyncHTTPClient() @@ -41,16 +45,23 @@ def get_message(self, level, alert, value, target=None, ntype=None, rule=None): level=level, reactor=self.reactor, alert=alert, value=value, target=target).strip() @gen.coroutine - def notify(self, level, *args, **kwargs): + def notify(self, level, alert, *args, **kwargs): LOGGER.debug("Handler (%s) %s", self.name, level) - message = self.get_message(level, *args, **kwargs) + channel = self.channel + username = self.username + if alert.override and self.name in alert.override: + override = alert.override[self.name] + channel = self._make_channel_name(override.get('channel', channel)) + username = override.get('username', username) + + message = self.get_message(level, alert, *args, **kwargs) data = dict() - data['username'] = self.username + data['username'] = username data['text'] = message data['icon_emoji'] = self.emoji.get(level, ':warning:') - if self.channel: - data['channel'] = self.channel + if channel: + data['channel'] = channel body = json.dumps(data) yield self.client.fetch( diff --git a/graphite_beacon/handlers/smtp.py b/graphite_beacon/handlers/smtp.py index d85ad9f..95081cd 100644 --- a/graphite_beacon/handlers/smtp.py +++ b/graphite_beacon/handlers/smtp.py @@ -33,13 +33,22 @@ def init_handler(self): self.options['to'] = [self.options['to']] @gen.coroutine - def notify(self, level, *args, **kwargs): + def notify(self, level, alert, *args, **kwargs): LOGGER.debug("Handler (%s) %s", self.name, level) - msg = self.get_message(level, *args, **kwargs) - msg['Subject'] = self.get_short(level, *args, **kwargs) - msg['From'] = self.options['from'] - msg['To'] = ", ".join(self.options['to']) + sender = self.options['from'] + to = self.options['to'] + if alert.override and self.name in alert.override: + override = alert.override[self.name] + sender = override.get('from', sender) + to = override.get('to', to) + if not isinstance(to, (list, tuple)): + to = [to] + + msg = self.get_message(level, alert, *args, **kwargs) + msg['Subject'] = self.get_short(level, alert, *args, **kwargs) + msg['From'] = sender + msg['To'] = ", ".join(to) smtp = SMTP() yield smtp_connect(smtp, self.options['host'], self.options['port']) # pylint: disable=no-value-for-parameter @@ -53,8 +62,8 @@ def notify(self, level, *args, **kwargs): self.options['password']) try: - LOGGER.debug("Send message to: %s", ", ".join(self.options['to'])) - smtp.sendmail(self.options['from'], self.options['to'], msg.as_string()) + LOGGER.debug("Send message to: %s", ", ".join(to)) + smtp.sendmail(sender, to, msg.as_string()) finally: smtp.quit() diff --git a/graphite_beacon/handlers/victorops.py b/graphite_beacon/handlers/victorops.py index 623082d..ff73fe3 100644 --- a/graphite_beacon/handlers/victorops.py +++ b/graphite_beacon/handlers/victorops.py @@ -20,13 +20,19 @@ def init_handler(self): assert self.url, 'REST Endpoint is not defined' self.routing_key = self.options.get('routing_key', 'everyone') - self.url = urljoin(self.url, self.routing_key) - + self.client = hc.AsyncHTTPClient() @gen.coroutine def notify(self, level, alert, value, target=None, ntype=None, rule=None): LOGGER.debug("Handler (%s) %s", self.name, level) + url = self.url + routing_key = self.routing_key + if alert.override and self.name in alert.override: + override = alert.override[self.name] + url = override.get('url', url) + routing_key = override.get('routing_key', routing_key) + url = urljoin(url, routing_key) message = self.get_short(level, alert, value, target=target, ntype=ntype, rule=rule) data = {'entity_display_name': alert.name, 'state_message': message, 'message_type': level} @@ -36,4 +42,4 @@ def notify(self, level, alert, value, target=None, ntype=None, rule=None): data['rule'] = rule['raw'] body = json.dumps(data) headers = {'Content-Type': 'application/json;'} - yield self.client.fetch(self.url, method="POST", body=body, headers=headers) + yield self.client.fetch(url, method="POST", body=body, headers=headers)