Adding subalarm values to alarm state transitions
Change-Id: Ifeb8fa8e203adfab8ec84ec3aad13835dc2c62ea
This commit is contained in:
parent
6e4fb0720a
commit
35d3976342
|
@ -20,6 +20,7 @@ package monasca.thresh.domain.model;
|
|||
import monasca.common.model.alarm.AlarmExpression;
|
||||
import monasca.common.model.alarm.AlarmState;
|
||||
import monasca.common.model.alarm.AlarmSubExpression;
|
||||
import monasca.common.model.alarm.AlarmTransitionSubAlarm;
|
||||
import monasca.common.model.domain.common.AbstractEntity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -43,7 +44,7 @@ public class Alarm extends AbstractEntity {
|
|||
private AlarmState state;
|
||||
private String stateChangeReason;
|
||||
private String alarmDefinitionId;
|
||||
|
||||
private List<AlarmTransitionSubAlarm> transitionSubAlarms = new ArrayList<>();
|
||||
public Alarm() {
|
||||
}
|
||||
|
||||
|
@ -59,13 +60,24 @@ public class Alarm extends AbstractEntity {
|
|||
this.alarmDefinitionId = alarmDefinition.getId();
|
||||
}
|
||||
|
||||
static String buildStateChangeReason(AlarmState alarmState, List<String> subAlarmExpressions) {
|
||||
public String buildStateChangeReason(AlarmState alarmState) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for(AlarmTransitionSubAlarm alarmTransitionSubAlarm : transitionSubAlarms){
|
||||
if (alarmTransitionSubAlarm.subAlarmState.equals(alarmState)) {
|
||||
if (stringBuilder.length() != 0) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
stringBuilder.append(alarmTransitionSubAlarm.subAlarmExpression);
|
||||
if (!AlarmState.UNDETERMINED.equals(alarmState))
|
||||
stringBuilder.append(" with the values: ").append(alarmTransitionSubAlarm.currentValues);
|
||||
}
|
||||
}
|
||||
if (AlarmState.UNDETERMINED.equals(alarmState)) {
|
||||
return String.format("No data was present for the sub-alarms: %s", subAlarmExpressions);
|
||||
return String.format("No data was present for the sub-alarms: %s", stringBuilder.toString());
|
||||
} else if (AlarmState.ALARM.equals(alarmState)) {
|
||||
return String.format("Thresholds were exceeded for the sub-alarms: %s", subAlarmExpressions);
|
||||
return String.format("Thresholds were exceeded for the sub-alarms: %s", stringBuilder.toString());
|
||||
} else {
|
||||
return "The alarm threshold(s) have not been exceeded";
|
||||
return String.format("The alarm threshold(s) have not been exceeded for the sub-alarms: %s", stringBuilder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,21 +128,25 @@ public class Alarm extends AbstractEntity {
|
|||
* alarm's state changed, else false.
|
||||
*/
|
||||
public boolean evaluate(AlarmExpression expression) {
|
||||
transitionSubAlarms.clear();
|
||||
AlarmState initialState = state;
|
||||
List<String> unitializedSubAlarms = new ArrayList<String>();
|
||||
boolean uninitialized = false;
|
||||
|
||||
for (SubAlarm subAlarm : subAlarms.values()) {
|
||||
if (AlarmState.UNDETERMINED.equals(subAlarm.getState())) {
|
||||
unitializedSubAlarms.add(subAlarm.getExpression().toString());
|
||||
uninitialized = true;
|
||||
}
|
||||
transitionSubAlarms.add(new AlarmTransitionSubAlarm(subAlarm.getExpression(),
|
||||
subAlarm.getState(), subAlarm.getCurrentValues()));
|
||||
}
|
||||
|
||||
// Handle UNDETERMINED state
|
||||
if (!unitializedSubAlarms.isEmpty()) {
|
||||
if (uninitialized) {
|
||||
if (AlarmState.UNDETERMINED.equals(initialState)) {
|
||||
return false;
|
||||
}
|
||||
state = AlarmState.UNDETERMINED;
|
||||
stateChangeReason = buildStateChangeReason(state, unitializedSubAlarms);
|
||||
stateChangeReason = buildStateChangeReason(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -146,16 +162,8 @@ public class Alarm extends AbstractEntity {
|
|||
if (AlarmState.ALARM.equals(initialState)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> subAlarmExpressions = new ArrayList<String>();
|
||||
for (SubAlarm subAlarm : subAlarms.values()) {
|
||||
if (AlarmState.ALARM.equals(subAlarm.getState())) {
|
||||
subAlarmExpressions.add(subAlarm.getExpression().toString());
|
||||
}
|
||||
}
|
||||
|
||||
state = AlarmState.ALARM;
|
||||
stateChangeReason = buildStateChangeReason(state, subAlarmExpressions);
|
||||
stateChangeReason = buildStateChangeReason(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -163,7 +171,7 @@ public class Alarm extends AbstractEntity {
|
|||
return false;
|
||||
}
|
||||
state = AlarmState.OK;
|
||||
stateChangeReason = buildStateChangeReason(state, null);
|
||||
stateChangeReason = buildStateChangeReason(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -256,4 +264,12 @@ public class Alarm extends AbstractEntity {
|
|||
public void addAlarmedMetric(MetricDefinitionAndTenantId alarmedMetric) {
|
||||
this.alarmedMetrics.add(alarmedMetric);
|
||||
}
|
||||
|
||||
public List<AlarmTransitionSubAlarm> getTransitionSubAlarms() {
|
||||
return transitionSubAlarms;
|
||||
}
|
||||
|
||||
public void setTransitionSubAlarms(List<AlarmTransitionSubAlarm> transitionSubAlarms) {
|
||||
this.transitionSubAlarms = transitionSubAlarms;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import monasca.common.model.alarm.AlarmSubExpression;
|
|||
import monasca.common.model.domain.common.AbstractEntity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sub-alarm. Decorates an AlarmSubExpression.
|
||||
|
@ -34,6 +36,7 @@ public class SubAlarm extends AbstractEntity implements Serializable {
|
|||
private AlarmSubExpression expression;
|
||||
private AlarmState state;
|
||||
private boolean noState;
|
||||
private List<Double> currentValues;
|
||||
/**
|
||||
* Whether metrics for this sub-alarm are received sporadically.
|
||||
*/
|
||||
|
@ -55,6 +58,7 @@ public class SubAlarm extends AbstractEntity implements Serializable {
|
|||
this.expression = expression.getAlarmSubExpression();
|
||||
this.alarmSubExpressionId = expression.getId();
|
||||
this.state = state;
|
||||
this.currentValues = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,6 +117,22 @@ public class SubAlarm extends AbstractEntity implements Serializable {
|
|||
return alarmSubExpressionId;
|
||||
}
|
||||
|
||||
public List<Double> getCurrentValues() {
|
||||
return currentValues;
|
||||
}
|
||||
|
||||
public void setCurrentValues(List<Double> currentValues) {
|
||||
this.currentValues = currentValues;
|
||||
}
|
||||
|
||||
public void addCurrentValue(Double currentValue) {
|
||||
this.currentValues.add(currentValue);
|
||||
}
|
||||
|
||||
public void clearCurrentValues() {
|
||||
this.currentValues.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
@ -146,8 +166,8 @@ public class SubAlarm extends AbstractEntity implements Serializable {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("SubAlarm [id=%s, alarmId=%s, alarmSubExpressionId=%s, expression=%s, state=%s noState=%s]", id,
|
||||
alarmId, alarmSubExpressionId, expression, state, noState);
|
||||
return String.format("SubAlarm [id=%s, alarmId=%s, alarmSubExpressionId=%s, expression=%s, state=%s, noState=%s, currentValues:[", id,
|
||||
alarmId, alarmSubExpressionId, expression, state, noState) + currentValues + "]]";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -122,10 +122,12 @@ public class SubAlarmStats {
|
|||
double[] values = stats.getViewValues();
|
||||
boolean thresholdExceeded = false;
|
||||
boolean hasEmptyWindows = false;
|
||||
subAlarm.clearCurrentValues();
|
||||
for (double value : values) {
|
||||
if (Double.isNaN(value)) {
|
||||
hasEmptyWindows = true;
|
||||
} else {
|
||||
subAlarm.addCurrentValue(value);
|
||||
emptyWindowObservations = 0;
|
||||
|
||||
// Check if value is OK
|
||||
|
@ -173,8 +175,6 @@ public class SubAlarmStats {
|
|||
/**
|
||||
* If this.subAlarm.isCompatible(newExpression) is not true, all data
|
||||
* will be flushed
|
||||
*
|
||||
* @param subAlarm
|
||||
*/
|
||||
public void updateSubAlarm(final AlarmSubExpression newExpression, long viewEndTimestamp) {
|
||||
// Save the old state
|
||||
|
|
|
@ -228,8 +228,8 @@ public class AlarmThresholdingBolt extends BaseRichBolt {
|
|||
new AlarmStateTransitionedEvent(alarmDefinition.getTenantId(), alarm.getId(),
|
||||
alarmDefinition.getId(), alarmedMetrics, alarmDefinition.getName(),
|
||||
alarmDefinition.getDescription(), initialState, alarm.getState(),
|
||||
alarmDefinition.getSeverity(), alarmDefinition.isActionsEnabled(), stateChangeReason,
|
||||
getTimestamp());
|
||||
alarmDefinition.getSeverity(), alarmDefinition.isActionsEnabled(), stateChangeReason,
|
||||
alarm.getTransitionSubAlarms(), getTimestamp());
|
||||
try {
|
||||
alarmEventForwarder.send(Serialization.toJson(event));
|
||||
} catch (Exception ignore) {
|
||||
|
@ -257,10 +257,6 @@ public class AlarmThresholdingBolt extends BaseRichBolt {
|
|||
|
||||
}
|
||||
|
||||
String buildStateChangeReason() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Alarm getOrCreateAlarm(String alarmId) {
|
||||
Alarm alarm = alarms.get(alarmId);
|
||||
if (alarm == null) {
|
||||
|
|
|
@ -20,11 +20,14 @@ package monasca.thresh.domain.model;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import monasca.common.model.alarm.AggregateFunction;
|
||||
import monasca.common.model.alarm.AlarmExpression;
|
||||
import monasca.common.model.alarm.AlarmOperator;
|
||||
import monasca.common.model.alarm.AlarmState;
|
||||
import monasca.common.model.alarm.AlarmSubExpression;
|
||||
import monasca.common.model.alarm.AlarmTransitionSubAlarm;
|
||||
|
||||
import monasca.common.model.metric.MetricDefinition;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
@ -143,22 +146,20 @@ public class AlarmTest {
|
|||
AlarmExpression expr =
|
||||
new AlarmExpression(
|
||||
"avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1}, 1) > 5 times 3 OR avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3");
|
||||
SubAlarm subAlarm1 =
|
||||
new SubAlarm("123", TEST_ALARM_ID, new SubExpression(UUID.randomUUID().toString(), expr
|
||||
.getSubExpressions().get(0)));
|
||||
SubAlarm subAlarm2 =
|
||||
new SubAlarm("456", TEST_ALARM_ID, new SubExpression(UUID.randomUUID().toString(), expr
|
||||
.getSubExpressions().get(1)));
|
||||
List<String> expressions =
|
||||
Arrays.asList(subAlarm1.getExpression().toString(), subAlarm2.getExpression().toString());
|
||||
Alarm alarm = new Alarm();
|
||||
|
||||
List<AlarmTransitionSubAlarm> transitionSubAlarms = new ArrayList<>();
|
||||
transitionSubAlarms.add(new AlarmTransitionSubAlarm(expr.getSubExpressions().get(0), AlarmState.UNDETERMINED, new ArrayList<Double>()));
|
||||
transitionSubAlarms.add(new AlarmTransitionSubAlarm(expr.getSubExpressions().get(1), AlarmState.ALARM, new ArrayList<Double>()));
|
||||
alarm.setTransitionSubAlarms(transitionSubAlarms);
|
||||
|
||||
assertEquals(
|
||||
Alarm.buildStateChangeReason(AlarmState.UNDETERMINED, expressions),
|
||||
"No data was present for the sub-alarms: [avg(hpcs.compute{device=1, instance_id=5, metric_name=cpu}, 1) > 5.0 times 3, avg(hpcs.compute{flavor_id=3, metric_name=mem}, 2) < 4.0 times 3]");
|
||||
alarm.buildStateChangeReason(AlarmState.UNDETERMINED),
|
||||
"No data was present for the sub-alarms: avg(hpcs.compute{device=1, instance_id=5, metric_name=cpu}, 1) > 5.0 times 3");
|
||||
|
||||
assertEquals(
|
||||
Alarm.buildStateChangeReason(AlarmState.ALARM, expressions),
|
||||
"Thresholds were exceeded for the sub-alarms: [avg(hpcs.compute{device=1, instance_id=5, metric_name=cpu}, 1) > 5.0 times 3, avg(hpcs.compute{flavor_id=3, metric_name=mem}, 2) < 4.0 times 3]");
|
||||
alarm.buildStateChangeReason(AlarmState.ALARM),
|
||||
"Thresholds were exceeded for the sub-alarms: avg(hpcs.compute{flavor_id=3, metric_name=mem}, 2) < 4.0 times 3 with the values: []");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@ import monasca.common.model.alarm.AlarmExpression;
|
|||
import monasca.common.model.alarm.AlarmState;
|
||||
import monasca.common.model.alarm.AlarmSubExpression;
|
||||
import monasca.common.streaming.storm.Streams;
|
||||
import monasca.common.util.Serialization;
|
||||
import monasca.thresh.domain.model.Alarm;
|
||||
import monasca.thresh.domain.model.AlarmDefinition;
|
||||
import monasca.thresh.domain.model.SubAlarm;
|
||||
|
@ -49,6 +50,7 @@ import org.testng.annotations.Test;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -62,7 +64,7 @@ public class AlarmThresholdingBoltTest {
|
|||
private AlarmDefinition alarmDefinition;
|
||||
private Alarm alarm;
|
||||
private List<SubAlarm> subAlarms;
|
||||
|
||||
private String subAlarmJson;
|
||||
private AlarmEventForwarder alarmEventForwarder;
|
||||
private AlarmDAO alarmDAO;
|
||||
private AlarmDefinitionDAO alarmDefinitionDAO;
|
||||
|
@ -121,8 +123,10 @@ public class AlarmThresholdingBoltTest {
|
|||
+ "\"alarmName\":\"Test CPU Alarm\","
|
||||
+ "\"alarmDescription\":\"Description of Alarm\",\"oldState\":\"OK\",\"newState\":\"ALARM\","
|
||||
+ "\"actionsEnabled\":true,"
|
||||
+ "\"stateChangeReason\":\"Thresholds were exceeded for the sub-alarms: ["
|
||||
+ subAlarm.getExpression().getExpression() + "]\"," + "\"severity\":\"LOW\",\"timestamp\":1395587091}}";
|
||||
+ "\"stateChangeReason\":\"Thresholds were exceeded for the sub-alarms: "
|
||||
+ subAlarm.getExpression().getExpression() + " with the values: []\"," + "\"severity\":\"LOW\","
|
||||
+ "\"subAlarms\":[" + buildSubAlarmJson(alarm.getSubAlarms()) + "],"
|
||||
+ "\"timestamp\":1395587091}}";
|
||||
|
||||
verify(alarmEventForwarder, times(1)).send(alarmJson);
|
||||
verify(alarmDAO, times(1)).updateState(alarmId, AlarmState.ALARM);
|
||||
|
@ -141,7 +145,13 @@ public class AlarmThresholdingBoltTest {
|
|||
+ "\"alarmName\":\"Test CPU Alarm\","
|
||||
+ "\"alarmDescription\":\"Description of Alarm\",\"oldState\":\"ALARM\",\"newState\":\"OK\","
|
||||
+ "\"actionsEnabled\":true,"
|
||||
+ "\"stateChangeReason\":\"The alarm threshold(s) have not been exceeded\",\"severity\":\"LOW\",\"timestamp\":1395587091}}";
|
||||
+ "\"stateChangeReason\":\"The alarm threshold(s) have not been exceeded for the sub-alarms: "
|
||||
+ subAlarm.getExpression().getExpression() + " with the values: [], "
|
||||
+ subAlarms.get(1).getExpression().getExpression() + " with the values: [], "
|
||||
+ subAlarms.get(2).getExpression().getExpression() + " with the values: []"
|
||||
+ "\",\"severity\":\"LOW\","
|
||||
+ "\"subAlarms\":[" + buildSubAlarmJson(alarm.getSubAlarms()) + "],"
|
||||
+ "\"timestamp\":1395587091}}";
|
||||
verify(alarmEventForwarder, times(1)).send(okJson);
|
||||
verify(alarmDAO, times(1)).updateState(alarmId, AlarmState.OK);
|
||||
}
|
||||
|
@ -256,6 +266,19 @@ public class AlarmThresholdingBoltTest {
|
|||
return alarmId;
|
||||
}
|
||||
|
||||
private String buildSubAlarmJson(Collection<SubAlarm> subAlarms){
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for(SubAlarm subAlarm: subAlarms){
|
||||
if (stringBuilder.length() != 0) {
|
||||
stringBuilder.append(",");
|
||||
}
|
||||
stringBuilder.append(Serialization.toJson(subAlarm.getExpression())).setCharAt(stringBuilder.length()-1, ',');
|
||||
stringBuilder.append("\"subAlarmState\":\"").append(subAlarm.getState()).append("\",");
|
||||
stringBuilder.append("\"currentValues\":").append(subAlarm.getCurrentValues()).append("}");
|
||||
}
|
||||
return stringBuilder.toString().replace("AlarmSubExpression","subAlarmExpression");
|
||||
}
|
||||
|
||||
private void emitSubAlarmStateChange(String alarmId, final SubAlarm subAlarm, AlarmState state) {
|
||||
// Create a copy so changing the state doesn't directly update the ones in the bolt
|
||||
final SubAlarm toEmit =
|
||||
|
|
Loading…
Reference in New Issue