(Non)deterministic support for alarms
Alarms can be created with deterministic keyword. 'deterministic' can be passed as part of alarm expression using: * keyword deterministic If 'deterministic' is not found in expression it is assumed to be 'false'. Implements: blueprint alarmsonlogs Change-Id: Ia42f9a1be37c31416bdac341b092fe527f860c16
This commit is contained in:
parent
91469d6ebf
commit
b338b78907
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 FUJITSU LIMITED
|
||||
* Copyright 2015-2016 FUJITSU LIMITED
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
@ -29,6 +29,7 @@ import org.hibernate.annotations.OnDeleteAction;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import monasca.common.model.alarm.AlarmOperator;
|
||||
import monasca.common.model.alarm.AlarmSubExpression;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sub_alarm_definition")
|
||||
@ -81,6 +82,9 @@ public class SubAlarmDefinitionDb
|
||||
@Column(name = "periods", length = 11, nullable = false)
|
||||
private Integer periods;
|
||||
|
||||
@Column(name = "is_deterministic", length = 1, nullable = false)
|
||||
private boolean deterministic = AlarmSubExpression.DEFAULT_DETERMINISTIC;
|
||||
|
||||
public SubAlarmDefinitionDb() {
|
||||
super();
|
||||
}
|
||||
@ -95,6 +99,21 @@ public class SubAlarmDefinitionDb
|
||||
Integer periods,
|
||||
DateTime created_at,
|
||||
DateTime updated_at) {
|
||||
this(id, alarmDefinition, function, metricName, operator, threshold, period, periods, created_at,
|
||||
updated_at, AlarmSubExpression.DEFAULT_DETERMINISTIC);
|
||||
}
|
||||
|
||||
public SubAlarmDefinitionDb(String id,
|
||||
AlarmDefinitionDb alarmDefinition,
|
||||
String function,
|
||||
String metricName,
|
||||
String operator,
|
||||
Double threshold,
|
||||
Integer period,
|
||||
Integer periods,
|
||||
DateTime created_at,
|
||||
DateTime updated_at,
|
||||
boolean deterministic) {
|
||||
super(id, created_at, updated_at);
|
||||
this.alarmDefinition = alarmDefinition;
|
||||
this.function = function;
|
||||
@ -103,6 +122,7 @@ public class SubAlarmDefinitionDb
|
||||
this.threshold = threshold;
|
||||
this.period = period;
|
||||
this.periods = periods;
|
||||
this.deterministic = deterministic;
|
||||
}
|
||||
|
||||
public SubAlarmDefinitionDb setPeriods(final Integer periods) {
|
||||
@ -172,6 +192,15 @@ public class SubAlarmDefinitionDb
|
||||
return this.periods;
|
||||
}
|
||||
|
||||
public boolean isDeterministic() {
|
||||
return this.deterministic;
|
||||
}
|
||||
|
||||
public SubAlarmDefinitionDb setDeterministic(final boolean isDeterministic) {
|
||||
this.deterministic = isDeterministic;
|
||||
return this;
|
||||
}
|
||||
|
||||
public interface Queries {
|
||||
String BY_ALARMDEFINITION_ID = "SubAlarmDefinition.byAlarmDefinitionId";
|
||||
String BY_ALARMDEFINITIONDIMENSION_SUBEXPRESSION_ID = "SubAlarmDefinition.byAlarmDefinitionDimension.subExpressionId";
|
||||
|
@ -151,7 +151,7 @@ public class AlarmExpression {
|
||||
public List<AlarmSubExpression> getSubExpressions() {
|
||||
if (subExpressions != null)
|
||||
return subExpressions;
|
||||
List<AlarmSubExpression> subExpressions = new ArrayList<AlarmSubExpression>();
|
||||
List<AlarmSubExpression> subExpressions = new ArrayList<>(elements.size());
|
||||
for (Object element : elements)
|
||||
if (element instanceof AlarmSubExpression)
|
||||
subExpressions.add((AlarmSubExpression) element);
|
||||
@ -159,6 +159,30 @@ public class AlarmExpression {
|
||||
return subExpressions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if expression is deterministic or non-deterministic.
|
||||
*
|
||||
* All {@link AlarmSubExpression} must be deterministic in order for entire expression
|
||||
* to be such. Otherwise expression is non-deterministic.
|
||||
*
|
||||
* @return true/false
|
||||
*
|
||||
* @see #getSubExpressions()
|
||||
* @see AlarmSubExpression#DEFAULT_DETERMINISTIC
|
||||
*/
|
||||
public boolean isDeterministic() {
|
||||
final List<AlarmSubExpression> subExpressions = this.getSubExpressions();
|
||||
if (subExpressions == null || subExpressions.isEmpty()) {
|
||||
return AlarmSubExpression.DEFAULT_DETERMINISTIC;
|
||||
}
|
||||
for (final AlarmSubExpression alarmSubExpression : subExpressions) {
|
||||
if (!alarmSubExpression.isDeterministic()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2016 FUJITSU LIMITED
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -21,17 +22,13 @@ import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
import monasca.common.model.alarm.AlarmExpressionLexer;
|
||||
import monasca.common.model.alarm.AlarmExpressionParser;
|
||||
import monasca.common.model.metric.MetricDefinition;
|
||||
|
||||
/**
|
||||
@ -41,6 +38,7 @@ public class AlarmSubExpression implements Serializable {
|
||||
private static final long serialVersionUID = -7458129503846747592L;
|
||||
public static final int DEFAULT_PERIOD = 60;
|
||||
public static final int DEFAULT_PERIODS = 1;
|
||||
public static final boolean DEFAULT_DETERMINISTIC = false;
|
||||
|
||||
private AggregateFunction function;
|
||||
private MetricDefinition metricDefinition;
|
||||
@ -48,6 +46,8 @@ public class AlarmSubExpression implements Serializable {
|
||||
private double threshold;
|
||||
private int period;
|
||||
private int periods;
|
||||
private boolean deterministic = DEFAULT_DETERMINISTIC;
|
||||
|
||||
// Use a DecimalFormatter for threshold because the standard double format starts using scientific notation when
|
||||
// threshold is very large and that scientific notation can't be parsed when recreating the SubExpression
|
||||
private static final DecimalFormat formatter;
|
||||
@ -57,14 +57,29 @@ public class AlarmSubExpression implements Serializable {
|
||||
formatter.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.US));
|
||||
}
|
||||
|
||||
public AlarmSubExpression(AggregateFunction function, MetricDefinition metricDefinition,
|
||||
AlarmOperator operator, double threshold, int period, int periods) {
|
||||
public AlarmSubExpression(AggregateFunction function,
|
||||
MetricDefinition metricDefinition,
|
||||
AlarmOperator operator,
|
||||
double threshold,
|
||||
int period,
|
||||
int periods) {
|
||||
this(function, metricDefinition, operator, threshold, period, periods, DEFAULT_DETERMINISTIC);
|
||||
}
|
||||
|
||||
public AlarmSubExpression(AggregateFunction function,
|
||||
MetricDefinition metricDefinition,
|
||||
AlarmOperator operator,
|
||||
double threshold,
|
||||
int period,
|
||||
int periods,
|
||||
boolean deterministic) {
|
||||
this.function = function;
|
||||
this.metricDefinition = metricDefinition;
|
||||
this.operator = operator;
|
||||
this.threshold = threshold;
|
||||
this.period = period;
|
||||
this.periods = periods;
|
||||
this.deterministic = deterministic;
|
||||
}
|
||||
|
||||
AlarmSubExpression() {
|
||||
@ -111,6 +126,8 @@ public class AlarmSubExpression implements Serializable {
|
||||
return false;
|
||||
if (periods != other.periods)
|
||||
return false;
|
||||
if (deterministic != other.deterministic)
|
||||
return false;
|
||||
if (Double.doubleToLongBits(threshold) != Double.doubleToLongBits(other.threshold))
|
||||
return false;
|
||||
return true;
|
||||
@ -130,8 +147,12 @@ public class AlarmSubExpression implements Serializable {
|
||||
public String getExpression() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(function).append('(').append(metricDefinition.toExpression());
|
||||
if (period != 60)
|
||||
if (this.isDeterministic()) {
|
||||
sb.append(", deterministic"); // present only non-default value
|
||||
}
|
||||
if (period != 60) {
|
||||
sb.append(", ").append(period);
|
||||
}
|
||||
sb.append(") ").append(operator).append(' ').append(formatter.format(threshold));
|
||||
if (periods != 1)
|
||||
sb.append(" times ").append(periods);
|
||||
@ -162,6 +183,10 @@ public class AlarmSubExpression implements Serializable {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public boolean isDeterministic() {
|
||||
return this.deterministic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
@ -171,6 +196,7 @@ public class AlarmSubExpression implements Serializable {
|
||||
result = prime * result + ((operator == null) ? 0 : operator.hashCode());
|
||||
result = prime * result + period;
|
||||
result = prime * result + periods;
|
||||
result = prime * result + Boolean.valueOf(this.deterministic).hashCode();
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(threshold);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
@ -201,6 +227,10 @@ public class AlarmSubExpression implements Serializable {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public void setDeterministic(final boolean deterministic) {
|
||||
this.deterministic = deterministic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getExpression();
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2016 FUJITSU LIMITED
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -27,6 +28,7 @@ import monasca.common.model.metric.MetricDefinition;
|
||||
* Complex alarm parser lister for sub expression extraction.
|
||||
*/
|
||||
class AlarmSubExpressionListener extends AlarmExpressionBaseListener {
|
||||
|
||||
private final boolean simpleExpression;
|
||||
private AggregateFunction function;
|
||||
private String namespace;
|
||||
@ -36,23 +38,36 @@ class AlarmSubExpressionListener extends AlarmExpressionBaseListener {
|
||||
private int period = AlarmSubExpression.DEFAULT_PERIOD;
|
||||
private int periods = AlarmSubExpression.DEFAULT_PERIODS;
|
||||
private List<Object> elements = new ArrayList<Object>();
|
||||
private boolean deterministic = AlarmSubExpression.DEFAULT_DETERMINISTIC;
|
||||
|
||||
AlarmSubExpressionListener(boolean simpleExpression) {
|
||||
this.simpleExpression = simpleExpression;
|
||||
}
|
||||
|
||||
private void saveSubExpression() {
|
||||
AlarmSubExpression subExpression = new AlarmSubExpression(function, new MetricDefinition(
|
||||
namespace, dimensions), operator, threshold, period, periods);
|
||||
// not possible to establish if metric is sporadic from expression, so we go with default
|
||||
final MetricDefinition metricDefinition = new MetricDefinition(
|
||||
namespace,
|
||||
dimensions
|
||||
);
|
||||
final AlarmSubExpression subExpression = new AlarmSubExpression(function,
|
||||
metricDefinition,
|
||||
operator,
|
||||
threshold,
|
||||
period,
|
||||
periods,
|
||||
deterministic
|
||||
);
|
||||
elements.add(subExpression);
|
||||
|
||||
function = null;
|
||||
namespace = null;
|
||||
dimensions = new TreeMap<String, String>();
|
||||
dimensions = new TreeMap<>();
|
||||
operator = null;
|
||||
threshold = 0;
|
||||
period = AlarmSubExpression.DEFAULT_PERIOD;
|
||||
periods = AlarmSubExpression.DEFAULT_PERIODS;
|
||||
deterministic = AlarmSubExpression.DEFAULT_DETERMINISTIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -155,6 +170,11 @@ class AlarmSubExpressionListener extends AlarmExpressionBaseListener {
|
||||
elements.add(BooleanOperator.AND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterDeterministic(final AlarmExpressionParser.DeterministicContext ctx) {
|
||||
this.deterministic = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operator and operand elements of the expression in postfix order. Elements will be
|
||||
* of types AlarmSubExpression and BooleanOperator.
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright 2016 FUJITSU LIMITED
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -31,7 +32,7 @@ expression
|
||||
|
||||
|
||||
function
|
||||
: functionType '(' compoundIdentifier (',' period)? ')'
|
||||
: functionType '(' compoundIdentifier (',' deterministic)? (',' period)? ')'
|
||||
;
|
||||
|
||||
relational_operator
|
||||
@ -118,6 +119,10 @@ period
|
||||
: INTEGER
|
||||
;
|
||||
|
||||
deterministic
|
||||
: 'deterministic'
|
||||
;
|
||||
|
||||
literal
|
||||
: DECIMAL
|
||||
| INTEGER
|
||||
@ -133,6 +138,7 @@ txt
|
||||
| INTEGER
|
||||
| STRING
|
||||
;
|
||||
|
||||
LT
|
||||
: [lL][tT]
|
||||
;
|
||||
@ -226,4 +232,4 @@ WS : [ \t\r\n]+ -> skip ;
|
||||
|
||||
FALL_THROUGH
|
||||
: . {if(true) {throw new IllegalArgumentException("IllegalCharacter: " + getText());}}
|
||||
;
|
||||
;
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Copyright 2016 FUJITSU LIMITED
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
@ -19,11 +20,14 @@ import static org.testng.Assert.assertNotEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.beust.jcommander.internal.Maps;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import monasca.common.model.metric.MetricDefinition;
|
||||
|
||||
@ -315,4 +319,60 @@ public class AlarmExpressionTest {
|
||||
+ "(min(ເຮືອນ{dn3=dv3,家=дом}) < 10 or sum(biz{dn5=dv5}) >9 and "
|
||||
+ "count(fizzle) lt 0 or count(baz) > 1)");
|
||||
}
|
||||
|
||||
public void shouldParseDeterministicExpression() {
|
||||
final Map<String, String> dimensions = Maps.newHashMap();
|
||||
final ArrayList<AlarmExpression> expressions = Lists.newArrayList(
|
||||
new AlarmExpression("count(log.error{},deterministic,20) > 5")
|
||||
);
|
||||
final MetricDefinition metricDefinition = new MetricDefinition("log.error", dimensions);
|
||||
|
||||
final AlarmSubExpression logErrorExpr = new AlarmSubExpression(
|
||||
AggregateFunction.COUNT,
|
||||
metricDefinition,
|
||||
AlarmOperator.GT,
|
||||
5,
|
||||
20,
|
||||
1,
|
||||
true // each expression is deterministic
|
||||
);
|
||||
|
||||
for (final AlarmExpression expr : expressions) {
|
||||
final List<AlarmSubExpression> subExpressions = expr.getSubExpressions();
|
||||
|
||||
assertTrue(expr.isDeterministic()); // each expression is deterministic
|
||||
assertEquals(1, subExpressions.size());
|
||||
assertEquals(subExpressions.get(0), logErrorExpr);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void shouldNotParseInvalidExpressionWrongRightOperand() {
|
||||
AlarmExpression.of("count(log.error{},deterministic=foo,20) > 5");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void shouldNotParseInvalidExpressionMalformedDeterministicKeyword() {
|
||||
AlarmExpression.of("count(log.error{},determ=true,20) > 5");
|
||||
}
|
||||
|
||||
public void shouldBeDeterministicIfAllSubExpressionAreDeterministic() {
|
||||
final String expression1 = "count(log.error{hostname=1,component=A},deterministic,20) > 5";
|
||||
final String expression2 = "count(log.error{hostname=1,component=B},deterministic,20) > 10";
|
||||
final String expression3 = "count(log.error{hostname=1,component=C},deterministic,20) > 15";
|
||||
|
||||
final String expression = String.format("%s OR %s OR %s", expression1, expression2, expression3);
|
||||
|
||||
assertTrue(new AlarmExpression(expression).isDeterministic());
|
||||
}
|
||||
|
||||
public void shouldBeNonDeterministicIfAtLeastOneExpressionIsNonDeterministic() {
|
||||
final String expression1 = "count(log.error{hostname=1,component=A},deterministic,20) > 5";
|
||||
final String expression2 = "count(log.error{hostname=1,component=B},deterministic,20) > 10";
|
||||
final String expression3 = "count(log.error{}) > 15";
|
||||
|
||||
final String expression = String.format("%s OR %s OR %s", expression1, expression2, expression3);
|
||||
|
||||
assertFalse(new AlarmExpression(expression).isDeterministic());
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Copyright 2016 FUJITSU LIMITED
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
@ -17,13 +18,9 @@ import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import monasca.common.model.alarm.AggregateFunction;
|
||||
import monasca.common.model.alarm.AlarmExpression;
|
||||
import monasca.common.model.alarm.AlarmOperator;
|
||||
import monasca.common.model.alarm.AlarmSubExpression;
|
||||
import monasca.common.model.metric.MetricDefinition;
|
||||
|
||||
@Test
|
||||
@ -172,4 +169,18 @@ public class AlarmSubExpressionTest {
|
||||
public void shouldAllowDecimalThresholds() {
|
||||
assertEquals(AlarmSubExpression.of("avg(hpcs.compute) > 2.375").getThreshold(), 2.375);
|
||||
}
|
||||
|
||||
public void shouldBeNonDeterministicByDefault() {
|
||||
assertFalse(AlarmSubExpression.of("count(log.error{}) > 1.0").isDeterministic());
|
||||
}
|
||||
|
||||
public void shouldBeDeterministicIfSet() {
|
||||
assertTrue(AlarmSubExpression.of("count(log.error{}, deterministic) > 1.0").isDeterministic());
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void shouldFailWithMalformedDeterministicKeyword() {
|
||||
AlarmSubExpression.of("count(log.error{}, deterministici) > 1.0");
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user