diff --git a/src/main/java/org/elasticsearch/alerting/Alert.java b/src/main/java/org/elasticsearch/alerting/Alert.java index b5551e92c2c..6e6b9876e08 100644 --- a/src/main/java/org/elasticsearch/alerting/Alert.java +++ b/src/main/java/org/elasticsearch/alerting/Alert.java @@ -15,9 +15,6 @@ import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; import java.util.List; -/** - * Created by brian on 8/12/14. - */ public class Alert implements ToXContent{ private final String alertName; private String queryName; @@ -28,6 +25,16 @@ public class Alert implements ToXContent{ private DateTime lastRan; private long version; private DateTime running; + private boolean enabled; + + + public boolean enabled() { + return enabled; + } + + public void enabled(boolean enabled) { + this.enabled = enabled; + } public DateTime running() { return running; @@ -109,7 +116,7 @@ public class Alert implements ToXContent{ public Alert(String alertName, String queryName, AlertTrigger trigger, TimeValue timePeriod, List actions, String schedule, DateTime lastRan, - List indices, DateTime running, long version){ + List indices, DateTime running, long version, boolean enabled){ this.alertName = alertName; this.queryName = queryName; this.trigger = trigger; @@ -120,6 +127,7 @@ public class Alert implements ToXContent{ this.indices = indices; this.version = version; this.running = running; + this.enabled = enabled; } @Override @@ -132,6 +140,8 @@ public class Alert implements ToXContent{ builder.field(AlertManager.TIMEPERIOD_FIELD.getPreferredName(), timePeriod); builder.field(AlertManager.LASTRAN_FIELD.getPreferredName(), lastRan); builder.field(AlertManager.CURRENTLY_RUNNING.getPreferredName(), running); + builder.field(AlertManager.ENABLED.getPreferredName(), enabled); + builder.field(AlertManager.TRIGGER_FIELD.getPreferredName()); trigger.toXContent(builder, params); builder.field(AlertManager.ACTION_FIELD.getPreferredName()); diff --git a/src/main/java/org/elasticsearch/alerting/AlertManager.java b/src/main/java/org/elasticsearch/alerting/AlertManager.java index 9792361ab2d..86d6b0111f8 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertManager.java +++ b/src/main/java/org/elasticsearch/alerting/AlertManager.java @@ -28,17 +28,11 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.joda.time.DateTime; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.*; import org.elasticsearch.search.SearchHit; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; @@ -57,6 +51,7 @@ public class AlertManager extends AbstractLifecycleComponent { public static final ParseField LASTRAN_FIELD = new ParseField("lastRan"); public static final ParseField INDICES = new ParseField("indices"); public static final ParseField CURRENTLY_RUNNING = new ParseField("running"); + public static final ParseField ENABLED = new ParseField("enabled"); private final Client client; private AlertScheduler scheduler; @@ -429,7 +424,26 @@ public class AlertManager extends AbstractLifecycleComponent { logger.warn("Indices : " + fields.get(INDICES.getPreferredName()) + " class " + fields.get(INDICES.getPreferredName()).getClass() ); } - return new Alert(alertId, query, trigger, timePeriod, actions, schedule, lastRan, indices, running, version); + boolean enabled = true; + if (fields.get(ENABLED.getPreferredName()) != null ) { + Object enabledObj = fields.get(ENABLED.getPreferredName()); + if (enabledObj instanceof Boolean){ + enabled = (Boolean)enabledObj; + } else { + if (enabledObj.toString().toLowerCase(Locale.ROOT).equals("true") || + enabledObj.toString().toLowerCase(Locale.ROOT).equals("1")) { + enabled = true; + } else if ( enabledObj.toString().toLowerCase(Locale.ROOT).equals("false") || + enabledObj.toString().toLowerCase(Locale.ROOT).equals("0")) { + enabled = false; + } else { + throw new ElasticsearchIllegalArgumentException("Unable to parse [" + enabledObj + "] as a boolean"); + } + } + } + + + return new Alert(alertId, query, trigger, timePeriod, actions, schedule, lastRan, indices, running, version, enabled); } public Map getSafeAlertMap() { diff --git a/src/main/java/org/elasticsearch/alerting/AlertScheduler.java b/src/main/java/org/elasticsearch/alerting/AlertScheduler.java index f077681b512..540196cbbde 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertScheduler.java +++ b/src/main/java/org/elasticsearch/alerting/AlertScheduler.java @@ -67,6 +67,9 @@ public class AlertScheduler extends AbstractLifecycleComponent { logger.warn("Running [{}]",alertName); Alert alert = alertManager.getAlertForName(alertName); DateTime scheduledTime = new DateTime(jobExecutionContext.getScheduledFireTime()); + if (!alert.enabled()) { + logger.warn("Alert [{}] is not enabled", alertName); + } try { if (!alertManager.claimAlertRun(alertName, scheduledTime) ){ logger.warn("Another process has already run this alert."); diff --git a/src/main/java/org/elasticsearch/alerting/AlertTrigger.java b/src/main/java/org/elasticsearch/alerting/AlertTrigger.java index bb03ffa6473..0342bafc65b 100644 --- a/src/main/java/org/elasticsearch/alerting/AlertTrigger.java +++ b/src/main/java/org/elasticsearch/alerting/AlertTrigger.java @@ -8,8 +8,6 @@ package org.elasticsearch.alerting; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; @@ -19,6 +17,16 @@ public class AlertTrigger implements ToXContent { private TriggerType triggerType; private int value; + public ScriptedAlertTrigger scriptedTrigger() { + return scriptedTrigger; + } + + public void scriptedTrigger(ScriptedAlertTrigger scriptedTrigger) { + this.scriptedTrigger = scriptedTrigger; + } + + private ScriptedAlertTrigger scriptedTrigger; + public SimpleTrigger trigger() { return trigger; } @@ -49,8 +57,17 @@ public class AlertTrigger implements ToXContent { this.value = value; } + public AlertTrigger(ScriptedAlertTrigger scriptedTrigger){ + this.scriptedTrigger = scriptedTrigger; + this.triggerType = TriggerType.SCRIPT; + } + public String toString(){ - return triggerType + " " + trigger + " " + value; + if(triggerType != TriggerType.SCRIPT) { + return triggerType + " " + trigger + " " + value; + } else { + return scriptedTrigger.toString(); + } } public static enum SimpleTrigger { @@ -107,19 +124,26 @@ public class AlertTrigger implements ToXContent { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.field(triggerType.toString(), trigger.toString() + value); - builder.endObject(); - return builder; + if (triggerType != TriggerType.SCRIPT) { + builder.startObject(); + builder.field(triggerType.toString(), trigger.toString() + value); + builder.endObject(); + return builder; + } else { + return scriptedTrigger.toXContent(builder, params); + } } public static enum TriggerType { - NUMBER_OF_EVENTS; + NUMBER_OF_EVENTS, + SCRIPT; public static TriggerType fromString(final String sTriggerType) { switch (sTriggerType) { case "numberOfEvents": return NUMBER_OF_EVENTS; + case "script": + return SCRIPT; default: throw new ElasticsearchIllegalArgumentException("Unknown AlertTrigger:TriggerType [" + sTriggerType + "]"); } @@ -129,6 +153,8 @@ public class AlertTrigger implements ToXContent { switch (triggerType) { case NUMBER_OF_EVENTS: return "numberOfEvents"; + case SCRIPT: + return "script"; default: return "unknown"; } diff --git a/src/main/java/org/elasticsearch/alerting/ScriptedAlertTrigger.java b/src/main/java/org/elasticsearch/alerting/ScriptedAlertTrigger.java new file mode 100644 index 00000000000..cc6dabcc8e8 --- /dev/null +++ b/src/main/java/org/elasticsearch/alerting/ScriptedAlertTrigger.java @@ -0,0 +1,36 @@ +/* + * 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 org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.script.ScriptService; + +import java.io.IOException; + +public class ScriptedAlertTrigger implements ToXContent { + public String script; + public ScriptService.ScriptType scriptType; + public String scriptLang; + + + public ScriptedAlertTrigger(String script, ScriptService.ScriptType scriptType, String scriptLang) { + this.script = script; + this.scriptType = scriptType; + this.scriptLang = scriptLang; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("script",script); + builder.field("script_type", scriptType); + builder.field("script_lang", scriptLang); + builder.endObject(); + return builder; + } +} diff --git a/src/main/java/org/elasticsearch/alerting/TriggerManager.java b/src/main/java/org/elasticsearch/alerting/TriggerManager.java index 4cc57f84a6b..3a580abb235 100644 --- a/src/main/java/org/elasticsearch/alerting/TriggerManager.java +++ b/src/main/java/org/elasticsearch/alerting/TriggerManager.java @@ -10,29 +10,76 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.ScriptService; +import java.util.HashMap; import java.util.Map; + +/* + * TODO : The trigger classes need cleanup and refactoring to be similar to the AlertActions and be pluggable + */ public class TriggerManager extends AbstractComponent { private final AlertManager alertManager; + private final ScriptService scriptService; public static AlertTrigger parseTriggerFromMap(Map triggerMap) { + //For now just trigger on number of events greater than 1 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); + if (type == AlertTrigger.TriggerType.SCRIPT) { + ScriptedAlertTrigger scriptedTrigger = parseScriptedTrigger(entry.getValue()); + return new AlertTrigger(scriptedTrigger); + } else { + 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(); } + private static ScriptedAlertTrigger parseScriptedTrigger(Object value) { + if (value instanceof Map) { + Map valueMap = (Map)value; + try { + return new ScriptedAlertTrigger(valueMap.get("script").toString(), + ScriptService.ScriptType.valueOf(valueMap.get("script_type").toString()), + valueMap.get("script_lang").toString()); + } catch (Exception e){ + throw new ElasticsearchIllegalArgumentException("Unable to parse " + value + " as a ScriptedAlertTrigger"); + } + } else { + throw new ElasticsearchIllegalArgumentException("Unable to parse " + value + " as a ScriptedAlertTrigger"); + } + } @Inject - public TriggerManager(Settings settings, AlertManager alertManager) { + public TriggerManager(Settings settings, AlertManager alertManager, ScriptService scriptService) { super(settings); this.alertManager = alertManager; + this.scriptService = scriptService; + } + + public boolean doScriptTrigger(ScriptedAlertTrigger scriptTrigger, SearchResponse response) { + try { + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder = response.toXContent(builder, ToXContent.EMPTY_PARAMS); + Map responseMap = XContentHelper.convertToMap(builder.bytes(), false).v2(); + ExecutableScript executable = scriptService.executable(scriptTrigger.scriptLang, scriptTrigger.script, + scriptTrigger.scriptType, responseMap); + Object returnValue = executable.run(); + logger.warn("Returned [{}] from script", returnValue); + } catch (Exception e ){ + logger.error("Failed to execute script trigger", e); + } + return false; } public boolean isTriggered(String alertName, SearchResponse response) { @@ -46,6 +93,8 @@ public class TriggerManager extends AbstractComponent { case NUMBER_OF_EVENTS: testValue = response.getHits().getTotalHits(); break; + case SCRIPT: + return doScriptTrigger(alert.trigger().scriptedTrigger(), response); default: throw new ElasticsearchIllegalArgumentException("Bad value for trigger.triggerType [" + alert.trigger().triggerType() + "]"); }