From 88ed7a56245bbbe4ad68bb7bda0927d3f41ed276 Mon Sep 17 00:00:00 2001 From: Brian Murphy Date: Wed, 13 Aug 2014 10:11:51 +0100 Subject: [PATCH] Alerting : Add parsing and indices. This commit adds parsing of the triggers and actions, and allows alerts to be restricted to indices. Original commit: elastic/x-pack-elasticsearch@44a6a51b17bc3a862bbf3276ac3bcaba52df0f85 --- .../org/elasticsearch/alerting/Alert.java | 36 +++++++++++------- .../elasticsearch/alerting/AlertAction.java | 3 +- .../alerting/AlertActionFactory.java | 10 +++++ .../alerting/AlertActionManager.java | 24 ++++++++++-- .../elasticsearch/alerting/AlertManager.java | 37 ++++++++++++++++--- .../alerting/AlertScheduler.java | 7 +++- .../alerting/EmailAlertAction.java | 34 ++++++++--------- .../alerting/EmailAlertActionFactory.java | 22 +++++++++++ .../alerting/TriggerManager.java | 18 +++++---- 9 files changed, 141 insertions(+), 50 deletions(-) create mode 100644 src/main/java/org/elasticsearch/alerting/AlertActionFactory.java create mode 100644 src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java diff --git a/src/main/java/org/elasticsearch/alerting/Alert.java b/src/main/java/org/elasticsearch/alerting/Alert.java index dd4ed973f36..b48ed508d22 100644 --- a/src/main/java/org/elasticsearch/alerting/Alert.java +++ b/src/main/java/org/elasticsearch/alerting/Alert.java @@ -5,12 +5,10 @@ */ package org.elasticsearch.alerting; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.unit.TimeValue; -import java.util.Calendar; -import java.util.Date; +import java.util.List; /** * Created by brian on 8/12/14. @@ -20,6 +18,20 @@ public class Alert { private String queryName; private AlertTrigger trigger; private TimeValue timePeriod; + private List actions; + private String schedule; + private DateTime lastRan; + + public List indices() { + return indices; + } + + public void indices(List indices) { + this.indices = indices; + } + + private List indices; + public String alertName() { return alertName; @@ -49,12 +61,12 @@ public class Alert { this.timePeriod = timePeriod; } - public AlertAction action() { - return action; + public List actions() { + return actions; } - public void action(AlertAction action) { - this.action = action; + public void actions(List action) { + this.actions = action; } public String schedule() { @@ -73,19 +85,17 @@ public class Alert { this.lastRan = lastRan; } - private AlertAction action; - private String schedule; - private DateTime lastRan; - public Alert(String alertName, String queryName, AlertTrigger trigger, - TimeValue timePeriod, AlertAction action, String schedule, DateTime lastRan){ + TimeValue timePeriod, List actions, String schedule, DateTime lastRan, + List indices){ this.alertName = alertName; this.queryName = queryName; this.trigger = trigger; this.timePeriod = timePeriod; - this.action = action; + this.actions = actions; this.lastRan = lastRan; this.schedule = schedule; + this.indices = indices; } public String toJSON(){ diff --git a/src/main/java/org/elasticsearch/alerting/AlertAction.java b/src/main/java/org/elasticsearch/alerting/AlertAction.java index adaa473303d..a27269ef713 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertAction.java +++ b/src/main/java/org/elasticsearch/alerting/AlertAction.java @@ -6,6 +6,5 @@ package org.elasticsearch.alerting; public interface AlertAction { - public boolean doAction(AlertResult alert); - public String getActionType(); + public boolean doAction(String alertName, AlertResult alert); } diff --git a/src/main/java/org/elasticsearch/alerting/AlertActionFactory.java b/src/main/java/org/elasticsearch/alerting/AlertActionFactory.java new file mode 100644 index 00000000000..a8c70310fe8 --- /dev/null +++ b/src/main/java/org/elasticsearch/alerting/AlertActionFactory.java @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.alerting; + +public interface AlertActionFactory { + AlertAction createAction(Object parameters); +} diff --git a/src/main/java/org/elasticsearch/alerting/AlertActionManager.java b/src/main/java/org/elasticsearch/alerting/AlertActionManager.java index 52da8cdd597..a22c311e64a 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertActionManager.java +++ b/src/main/java/org/elasticsearch/alerting/AlertActionManager.java @@ -11,22 +11,40 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.search.SearchHitField; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class AlertActionManager extends AbstractComponent { private final AlertManager alertManager; + private final Map actionImplemented; @Inject public AlertActionManager(Settings settings, AlertManager alertManager) { super(settings); this.alertManager = alertManager; + this.actionImplemented = new HashMap<>(); + actionImplemented.put("email", new EmailAlertActionFactory()); } - public static AlertAction parseActionFromSearchField(SearchHitField hitField) { - return null; + public void registerAction(String name, AlertActionFactory actionFactory){ + actionImplemented.put(name, actionFactory); + } + + public List parseActionsFromMap(Map actionMap) { + List actions = new ArrayList<>(); + for (Map.Entry actionEntry : actionMap.entrySet() ) { + actions.add(actionImplemented.get(actionEntry.getKey()).createAction(actionEntry.getValue())); + } + return actions; } public void doAction(String alertName, AlertResult alertResult){ Alert alert = alertManager.getAlertForName(alertName); - alert.action().doAction(alertResult); + for (AlertAction action : alert.actions()) { + action.doAction(alertName, alertResult); + } } } diff --git a/src/main/java/org/elasticsearch/alerting/AlertManager.java b/src/main/java/org/elasticsearch/alerting/AlertManager.java index 1acb417388d..d7ca11d360f 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertManager.java +++ b/src/main/java/org/elasticsearch/alerting/AlertManager.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.search.SearchHit; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; @@ -41,6 +42,7 @@ public class AlertManager extends AbstractLifecycleComponent { public final ParseField TIMEPERIOD_FIELD = new ParseField("timeperiod"); public final ParseField ACTION_FIELD = new ParseField("action"); public final ParseField LASTRAN_FIELD = new ParseField("lastRan"); + public final ParseField INDICES = new ParseField("indices"); private final Client client; private AlertScheduler scheduler; @@ -50,6 +52,7 @@ public class AlertManager extends AbstractLifecycleComponent { private AtomicBoolean started = new AtomicBoolean(false); private final Thread starter; + private AlertActionManager actionManager; class StarterThread implements Runnable { @Override @@ -83,7 +86,10 @@ public class AlertManager extends AbstractLifecycleComponent { } } - + @Inject + public void setActionManager(AlertActionManager actionManager){ + this.actionManager = actionManager; + } @Override protected void doStart() throws ElasticsearchException { @@ -156,14 +162,33 @@ public class AlertManager extends AbstractLifecycleComponent { String query = fields.get(QUERY_FIELD.getPreferredName()).toString(); String schedule = fields.get(SCHEDULE_FIELD.getPreferredName()).toString(); - //AlertTrigger trigger = TriggerManager.parseTriggerFromSearchField(fields.get(TRIGGER_FIELD.toString())); - AlertTrigger trigger = new AlertTrigger(AlertTrigger.SimpleTrigger.GREATER_THAN, AlertTrigger.TriggerType.NUMBER_OF_EVENTS, 1); + Object triggerObj = fields.get(TRIGGER_FIELD.getPreferredName()); + AlertTrigger trigger = null; + if (triggerObj instanceof Map) { + Map triggerMap = (Map) triggerObj; + trigger = TriggerManager.parseTriggerFromMap(triggerMap); + } else { + throw new ElasticsearchException("Unable to parse trigger [" + triggerObj + "]"); + } TimeValue timePeriod = new TimeValue(Long.valueOf(fields.get(TIMEPERIOD_FIELD.getPreferredName()).toString())); - //AlertAction action = AlertActionManager.parseActionFromSearchField(fields.get(ACTION_FIELD.toString())); - AlertAction action = new EmailAlertAction("brian.murphy@elasticsearch.com"); + + Object actionObj = fields.get(ACTION_FIELD.getPreferredName()); + List actions = null; + if (actionObj instanceof Map) { + Map actionMap = (Map) actionObj; + actions = actionManager.parseActionsFromMap(actionMap); + } else { + throw new ElasticsearchException("Unable to parse actions [" + triggerObj + "]"); + } + DateTime lastRan = new DateTime(fields.get("lastRan").toString()); - Alert alert = new Alert(alertId, query, trigger, timePeriod, action, schedule, lastRan); + List indices = null; + if (fields.get(INDICES.getPreferredName()) != null && fields.get(INDICES.getPreferredName()) instanceof List){ + indices = (List)fields.get(INDICES.getPreferredName()); + } + + Alert alert = new Alert(alertId, query, trigger, timePeriod, actions, schedule, lastRan, indices); alertMap.put(alertId, alert); } logger.warn("Loaded [{}] alerts from the alert index.", alertMap.size()); diff --git a/src/main/java/org/elasticsearch/alerting/AlertScheduler.java b/src/main/java/org/elasticsearch/alerting/AlertScheduler.java index dfd7c1127d8..0959b2e9116 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertScheduler.java +++ b/src/main/java/org/elasticsearch/alerting/AlertScheduler.java @@ -7,6 +7,7 @@ package org.elasticsearch.alerting; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse; +import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.bytes.BytesReference; @@ -67,7 +68,11 @@ public class AlertScheduler extends AbstractLifecycleComponent { ExecutableScript executable = scriptService.executable(compiledTemplate, new HashMap()); BytesReference processedQuery = (BytesReference) executable.run(); logger.warn("Compiled to [{}]", processedQuery); - SearchResponse sr = client.prepareSearch().setSource(processedQuery).execute().get(); + SearchRequestBuilder srb = client.prepareSearch().setSource(processedQuery); + if (alert.indices() != null ){ + srb.setIndices(alert.indices().toArray(new String[0])); + } + SearchResponse sr = srb.execute().get(); logger.warn("Got search response"); AlertResult result = new AlertResult(); result.isTriggered = triggerManager.isTriggered(alertName,sr); diff --git a/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java b/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java index 564cabab129..49684c33e96 100644 --- a/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java +++ b/src/main/java/org/elasticsearch/alerting/EmailAlertAction.java @@ -12,37 +12,40 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.mail.*; +import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; public class EmailAlertAction implements AlertAction { - List emailAddresses = new ArrayList<>(); + List
emailAddresses = new ArrayList<>(); String from = "esalertingtest@gmail.com"; String passwd = "elasticsearchforthewin"; String server = "smtp.gmail.com"; - int port = 465; + int port = 587; - - public EmailAlertAction(SearchHitField hitField){ - emailAddresses.add("brian.murphy@elasticsearch.com"); - } - public EmailAlertAction(String ... addresses){ for (String address : addresses) { - emailAddresses.add(address); + addEmailAddress(address); } } + public void addEmailAddress(String address) { + try { + emailAddresses.add(InternetAddress.parse(address)[0]); + } catch (AddressException addressException) { + throw new ElasticsearchException("Unable to parse address : [" + address + "]"); + } + } @Override - public boolean doAction(AlertResult result) { + public boolean doAction(String alertName, AlertResult result) { Properties props = new Properties(); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); - props.put("mail.smtp.host", "smtp.gmail.com"); - props.put("mail.smtp.port", "587"); + props.put("mail.smtp.host", server); + props.put("mail.smtp.port", port); Session session = Session.getInstance(props, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { @@ -53,8 +56,8 @@ public class EmailAlertAction implements AlertAction { try { message.setFrom(new InternetAddress(from)); message.setRecipients(Message.RecipientType.TO, - InternetAddress.parse(emailAddresses.get(0))); - message.setSubject("Elasticsearch Alert!"); + emailAddresses.toArray(new Address[1])); + message.setSubject("Elasticsearch Alert from " + alertName); message.setText(result.searchResponse.toString()); Transport.send(message); } catch (Exception e){ @@ -63,9 +66,4 @@ public class EmailAlertAction implements AlertAction { return true; } - - @Override - public String getActionType() { - return "email"; - } } diff --git a/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java b/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java new file mode 100644 index 00000000000..8bd16bbac6a --- /dev/null +++ b/src/main/java/org/elasticsearch/alerting/EmailAlertActionFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +package org.elasticsearch.alerting; + +import java.util.List; + +public class EmailAlertActionFactory implements AlertActionFactory{ + + @Override + public AlertAction createAction(Object parameters) { + EmailAlertAction action = new EmailAlertAction(); + if (parameters instanceof List){ + for (String emailAddress : (List)parameters) { + action.addEmailAddress(emailAddress); + } + } + return action; + } +} diff --git a/src/main/java/org/elasticsearch/alerting/TriggerManager.java b/src/main/java/org/elasticsearch/alerting/TriggerManager.java index 1552338a140..a530a33eaa6 100644 --- a/src/main/java/org/elasticsearch/alerting/TriggerManager.java +++ b/src/main/java/org/elasticsearch/alerting/TriggerManager.java @@ -9,20 +9,24 @@ import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.logging.ESLogger; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.search.SearchHitField; + +import java.util.Map; public class TriggerManager extends AbstractComponent { private final AlertManager alertManager; - //private ESLogger logger = Loggers.getLogger(TriggerManager.class); - public static AlertTrigger parseTriggerFromSearchField(SearchHitField hitField) { + public static AlertTrigger parseTriggerFromMap(Map triggerMap) { //For now just trigger on number of events greater than 1 - return new AlertTrigger(AlertTrigger.SimpleTrigger.GREATER_THAN, AlertTrigger.TriggerType.NUMBER_OF_EVENTS, 1); - //return null; + for (Map.Entry entry : triggerMap.entrySet()){ + AlertTrigger.TriggerType type = AlertTrigger.TriggerType.fromString(entry.getKey()); + + AlertTrigger.SimpleTrigger simpleTrigger = AlertTrigger.SimpleTrigger.fromString(entry.getValue().toString().substring(0, 1)); + int value = Integer.valueOf(entry.getValue().toString().substring(1)); + return new AlertTrigger(simpleTrigger, type, value); + } + throw new ElasticsearchIllegalArgumentException(); } @Inject