Alerting Plugin : First commit

Very rough non-functional commit of the alerting plugin.
Please be gentle.

Original commit: elastic/x-pack-elasticsearch@98870d0778
This commit is contained in:
Brian Murphy 2014-08-12 13:55:10 +01:00
parent d19f4b5954
commit 1e6d6b58c9
14 changed files with 652 additions and 0 deletions

89
pom.xml Normal file
View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<name>alerting-plugin</name>
<modelVersion>4.0.0</modelVersion>
<groupId>org.elasticsearch.plugin.alerting</groupId>
<artifactId>alerting-plugin</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<elasticsearch.version>1.3.1</elasticsearch.version>
</properties>
<dependencies>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[1.7,)</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<outputDirectory>${project.build.directory}/releases/</outputDirectory>
<descriptors>
<descriptor>${basedir}/src/main/assemblies/plugin.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<fork>true</fork>
<maxmem>512m</maxmem>
<!-- REMOVE WHEN UPGRADE:
see https://jira.codehaus.org/browse/MCOMPILER-209 it's a bug where
incremental compilation doesn't work unless it's set to false causeing
recompilation of the entire codebase each time without any changes. Should
be fixed in version > 3.1
-->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<assembly>
<id>plugin</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<dependencySets>
<dependencySet>
<outputDirectory>/</outputDirectory>
<useProjectArtifact>true</useProjectArtifact>
<useTransitiveFiltering>true</useTransitiveFiltering>
<excludes>
<exclude>org.elasticsearch:elasticsearch</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>

View File

@ -0,0 +1,94 @@
/*
* 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.common.Nullable;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.unit.TimeValue;
import java.util.Calendar;
import java.util.Date;
/**
* Created by brian on 8/12/14.
*/
public class Alert {
private final String alertName;
private String queryName;
private AlertTrigger trigger;
private TimeValue timePeriod;
public String alertName() {
return alertName;
}
public String queryName() {
return queryName;
}
public void queryName(String queryName) {
this.queryName = queryName;
}
public AlertTrigger trigger() {
return trigger;
}
public void trigger(AlertTrigger trigger) {
this.trigger = trigger;
}
public TimeValue timePeriod() {
return timePeriod;
}
public void timePeriod(TimeValue timePeriod) {
this.timePeriod = timePeriod;
}
public AlertAction action() {
return action;
}
public void action(AlertAction action) {
this.action = action;
}
public String schedule() {
return schedule;
}
public void schedule(String schedule) {
this.schedule = schedule;
}
public DateTime lastRan() {
return lastRan;
}
public void lastRan(DateTime lastRan) {
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){
this.alertName = alertName;
this.queryName = queryName;
this.trigger = trigger;
this.timePeriod = timePeriod;
this.action = action;
this.lastRan = lastRan;
this.schedule = schedule;
}
public String toJSON(){
return null;
}
}

View File

@ -0,0 +1,11 @@
/*
* 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 AlertAction {
public boolean doAction(AlertResult alert);
public String getActionType();
}

View File

@ -0,0 +1,17 @@
/*
* 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.search.SearchHitField;
/**
* Created by brian on 8/12/14.
*/
public class AlertActionManager {
public static AlertAction parseActionFromSearchField(SearchHitField hitField) {
return null;
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
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.search.SearchHit;
import org.elasticsearch.search.SearchHitField;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
public class AlertManager extends AbstractLifecycleComponent {
public final String ALERT_INDEX = ".alerts";
public final String ALERT_TYPE = "alert";
public final String QUERY_TYPE = "alertQuery";
public final ParseField QUERY_FIELD = new ParseField("query");
public final ParseField SCHEDULE_FIELD = new ParseField("schedule");
public final ParseField TRIGGER_FIELD = new ParseField("trigger");
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 Client client;
private final Map<String,Alert> alertMap;
@Override
protected void doStart() throws ElasticsearchException {
logger.warn("STARTING");
try {
loadAlerts();
} catch (Throwable t){
logger.error("Failed to load alerts", t);
}
}
@Override
protected void doStop() throws ElasticsearchException {
logger.warn("STOPPING");
}
@Override
protected void doClose() throws ElasticsearchException {
logger.warn("CLOSING");
}
@Inject
public AlertManager(Settings settings, Client client) {
super(settings);
logger.warn("Starting AlertManager");
this.client = client;
alertMap = new HashMap();
//scheduleAlerts();
}
private ClusterHealthStatus clusterHealth() throws InterruptedException, ExecutionException {
ClusterHealthResponse actionGet = client.admin().cluster()
.health(Requests.clusterHealthRequest(ALERT_INDEX).waitForGreenStatus().waitForEvents(Priority.LANGUID).waitForRelocatingShards(0)).actionGet();
return actionGet.getStatus();
}
private ClusterHealthStatus createAlertsIndex() throws InterruptedException, ExecutionException {
client.admin().indices().prepareCreate(ALERT_INDEX).addMapping(ALERT_TYPE).execute().get(); //TODO FIX MAPPINGS
ClusterHealthResponse actionGet = client.admin().cluster()
.health(Requests.clusterHealthRequest(ALERT_INDEX).waitForGreenStatus().waitForEvents(Priority.LANGUID).waitForRelocatingShards(0)).actionGet();
return actionGet.getStatus();
}
private void loadAlerts() throws InterruptedException, ExecutionException{
if (!client.admin().indices().prepareExists(ALERT_INDEX).execute().get().isExists()) {
createAlertsIndex();
}
synchronized (alertMap) {
SearchResponse searchResponse = client.prepareSearch().setSource(
"{ 'query' : " +
"{ 'match_all' : {}}" +
"'size' : 100" +
"}"
).setTypes(ALERT_TYPE).setIndices(ALERT_INDEX).execute().get();
for (SearchHit sh : searchResponse.getHits()) {
String alertId = sh.getId();
Map<String,SearchHitField> fields = sh.getFields();
String query = fields.get(QUERY_FIELD.toString()).toString();
String schedule = fields.get(SCHEDULE_FIELD.toString()).toString();
AlertTrigger trigger = TriggerManager.parseTriggerFromSearchField(fields.get(TRIGGER_FIELD.toString()));
TimeValue timePeriod = new TimeValue(Long.valueOf(fields.get(TIMEPERIOD_FIELD).toString()));
AlertAction action = AlertActionManager.parseActionFromSearchField(fields.get(ACTION_FIELD.toString()));
DateTime lastRan = new DateTime(fields.get(LASTRAN_FIELD.toString().toString()));
Alert alert = new Alert(alertId, query, trigger, timePeriod, action, schedule, lastRan);
alertMap.put(alertId, alert);
}
logger.warn("Loaded [{}] alerts from the alert index.", alertMap.size());
}
}
public Alert getAlertForName(String alertName) {
synchronized (alertMap) {
return alertMap.get(alertName);
}
}
}

View File

@ -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;
public class AlertResult {
public SearchResponse searchResponse;
public AlertTrigger trigger;
public boolean isTriggered;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AlertResult that = (AlertResult) o;
if (isTriggered != that.isTriggered) return false;
if (!searchResponse.equals(that.searchResponse)) return false;
if (!trigger.equals(that.trigger)) return false;
return true;
}
@Override
public int hashCode() {
int result = searchResponse.hashCode();
result = 31 * result + trigger.hashCode();
result = 31 * result + (isTriggered ? 1 : 0);
return result;
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.ElasticsearchIllegalArgumentException;
/**
* Created by brian on 8/12/14.
*/
public class AlertTrigger {
private SimpleTrigger trigger;
private TriggerType triggerType;
private int value;
public SimpleTrigger trigger() {
return trigger;
}
public void trigger(SimpleTrigger trigger) {
this.trigger = trigger;
}
public TriggerType triggerType() {
return triggerType;
}
public void triggerType(TriggerType triggerType) {
this.triggerType = triggerType;
}
public int value() {
return value;
}
public void value(int value) {
this.value = value;
}
public AlertTrigger(SimpleTrigger trigger, TriggerType triggerType, int value){
this.trigger = trigger;
this.triggerType = triggerType;
this.value = value;
}
public static enum SimpleTrigger {
EQUAL,
NOT_EQUAL,
GREATER_THAN,
LESS_THAN,
RISES_BY,
FALLS_BY;
public static SimpleTrigger fromString(final String sAction) {
switch (sAction) {
case ">":
return GREATER_THAN;
case "<":
return LESS_THAN;
case "=":
case "==":
return EQUAL;
case "!=":
return NOT_EQUAL;
case "->":
return RISES_BY;
case "<-":
return FALLS_BY;
default:
throw new ElasticsearchIllegalArgumentException("Unknown AlertAction:SimpleAction [" + sAction + "]");
}
}
}
public static enum TriggerType {
NUMBER_OF_EVENTS;
public static TriggerType fromString(final String sTriggerType) {
switch (sTriggerType) {
case "numberOfEvents":
return NUMBER_OF_EVENTS;
default:
throw new ElasticsearchIllegalArgumentException("Unknown AlertTrigger:TriggerType [" + sTriggerType + "]");
}
}
}
}

View File

@ -0,0 +1,18 @@
/*
* 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.common.inject.AbstractModule;
public class AlertingModule extends AbstractModule {
@Override
protected void configure() {
bind(AlertManager.class).asEagerSingleton();
bind(TriggerManager.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.search.SearchHitField;
import java.util.ArrayList;
import java.util.List;
public class EmailAlertAction implements AlertAction {
List<String> emailAddresses = new ArrayList<>();
String from = "esalertingtest@gmail.com";
String passwd = "elasticsearchforthewin";
String server = "smtp.gmail.com";
int port = 465;
public EmailAlertAction(SearchHitField hitField){
emailAddresses.add("brian.murphy@elasticsearch.com");
}
@Override
public boolean doAction(AlertResult alert) {
//Email here
return true;
}
@Override
public String getActionType() {
return "email";
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.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;
public class TriggerManager extends AbstractComponent {
private final AlertManager alertManager;
//private ESLogger logger = Loggers.getLogger(TriggerManager.class);
public static AlertTrigger parseTriggerFromSearchField(SearchHitField hitField) {
//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;
}
@Inject
public TriggerManager(Settings settings, AlertManager alertManager) {
super(settings);
this.alertManager = alertManager;
}
public boolean isTriggered(String alertName, SearchResponse response) {
Alert alert = this.alertManager.getAlertForName(alertName);
if (alert == null){
logger.warn("Could not find alert named [{}] in alert manager perhaps it has been deleted.", alertName);
return false;
}
int testValue;
switch (alert.trigger().triggerType()) {
case NUMBER_OF_EVENTS:
testValue = response.getHits().getHits().length;
break;
default:
throw new ElasticsearchIllegalArgumentException("Bad value for trigger.triggerType [" + alert.trigger().triggerType() + "]");
}
int triggerValue = alert.trigger().value();
//Move this to SimpleTrigger
switch (alert.trigger().trigger()) {
case GREATER_THAN:
return testValue > triggerValue;
case LESS_THAN:
return testValue < triggerValue;
case EQUAL:
return testValue == triggerValue;
case NOT_EQUAL:
return testValue != triggerValue;
case RISES_BY:
case FALLS_BY:
return false; //TODO FIX THESE
}
return false;
}
}

View File

@ -0,0 +1,9 @@
/*
* 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.
*/
/**
* Created by brian on 8/12/14.
*/
package org.elasticsearch.alerting;

View File

@ -0,0 +1,39 @@
/*
* 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.plugin.alerting;
import org.elasticsearch.alerting.AlertManager;
import org.elasticsearch.alerting.AlertingModule;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.component.LifecycleComponent;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.plugins.AbstractPlugin;
import java.util.Collection;
public class AlertingPlugin extends AbstractPlugin {
@Override public String name() {
return "alerting-plugin";
}
@Override public String description() {
return "Alerting Plugin Description";
}
@Override
public Collection<java.lang.Class<? extends LifecycleComponent>> services() {
Collection<java.lang.Class<? extends LifecycleComponent>> services = Lists.newArrayList();
services.add(AlertManager.class);
return services;
}
@Override
public Collection<Class<? extends Module>> modules() {
Collection<Class<? extends Module>> modules = Lists.newArrayList();
modules.add(AlertingModule.class);
return modules;
}
}

View File

@ -0,0 +1 @@
plugin=org.elasticsearch.plugin.alerting.AlertingPlugin