/* * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. * * 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 * the License. */ package monasca.common.model.alarm; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.List; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMap; import monasca.common.model.metric.MetricDefinition; @Test public class AlarmExpressionTest { private final String restrictedChars = "(){}&|<>=\","; public void shouldParseExpression() { AlarmExpression expr = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3"); List alarms = expr.getSubExpressions(); AlarmSubExpression expected1 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("instance_id", "5") .put("metric_name", "cpu") .put("device", "1") .build()), AlarmOperator.GT, 5, 1, 3); AlarmSubExpression expected2 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("flavor_id", "3") .put("metric_name", "mem") .build()), AlarmOperator.LT, 4, 2, 3); assertEquals(alarms.get(0), expected1); assertEquals(alarms.get(1), expected2); } public void shouldParseString() { AlarmExpression expr = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1, url=\"https://www.google.com/?startpage=3&happygoing\"}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem, specialchars=\"!@#$%^&*()~<>{}[],.\"}, 2) < 4 times 3"); List alarms = expr.getSubExpressions(); AlarmSubExpression expected1 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("instance_id", "5") .put("metric_name", "cpu") .put("url", "\"https://www.google.com/?startpage=3&happygoing\"") .put("device", "1") .build()), AlarmOperator.GT, 5, 1, 3); AlarmSubExpression expected2 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("flavor_id", "3") .put("metric_name", "mem") .put("specialchars", "\"!@#$%^&*()~<>{}[],.\"") .build()), AlarmOperator.LT, 4, 2, 3); assertEquals(alarms.get(0), expected1); assertEquals(alarms.get(1), expected2); } public void shouldParseComplexWithoutQuotes() { AlarmExpression expr = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1, url=https%3A%2F%2Fwww.google.com%2F%3Fstartpage%3D3%26happygoing}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem, specialchars=a!@#/\\$%^*~}, 2) < 4 times 3"); List alarms = expr.getSubExpressions(); AlarmExpression containsDirectories = new AlarmExpression("avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1,global=$_globalVariable,special=__useSparingly,dos=\\system32\\, windows=C:\\system32\\}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem,$globalVariable=global,__useSparingly=special,unix=/opt/vertica/bin/}, 2) < 4 times 3"); List alarmsContainsDirectories = containsDirectories.getSubExpressions(); AlarmSubExpression expected1 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("instance_id", "5") .put("metric_name", "cpu") .put("url", "https%3A%2F%2Fwww.google.com%2F%3Fstartpage%3D3%26happygoing") .put("device", "1") .build()), AlarmOperator.GT, 5, 1, 3); AlarmSubExpression expected2 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("flavor_id", "3") .put("metric_name", "mem") .put("specialchars", "a!@#/\\$%^*~") .build()), AlarmOperator.LT, 4, 2, 3); AlarmSubExpression expected3 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("instance_id", "5") .put("metric_name", "cpu") .put("device", "1") .put("global", "$_globalVariable") .put("special", "__useSparingly") .put("dos", "\\system32\\") .put("windows", "C:\\system32\\") .build()), AlarmOperator.GT, 5, 1, 3); AlarmSubExpression expected4 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("flavor_id", "3") .put("metric_name", "mem") .put("$globalVariable", "global") .put("__useSparingly", "special") .put("unix", "/opt/vertica/bin/") .build()), AlarmOperator.LT, 4, 2, 3); assertEquals(alarms.get(0), expected1); assertEquals(alarms.get(1), expected2); assertEquals(alarmsContainsDirectories.get(0), expected3); assertEquals(alarmsContainsDirectories.get(1), expected4); } public void shouldParseExpressionWithoutType() { AlarmExpression expr = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3"); List alarms = expr.getSubExpressions(); AlarmSubExpression expected1 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("instance_id", "5") .put("metric_name", "cpu") .put("device", "1") .build()), AlarmOperator.GT, 5, 1, 3); AlarmSubExpression expected2 = new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap.builder() .put("flavor_id", "3") .put("metric_name", "mem") .build()), AlarmOperator.LT, 4, 2, 3); assertEquals(alarms.get(0), expected1); assertEquals(alarms.get(1), expected2); } public void shouldEvaluateExpression() { AlarmExpression expr = new AlarmExpression( "sum(hpcs.compute{instance_id=5,metric_name=disk}, 1) > 33 or (avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3)"); List alarms = expr.getSubExpressions(); AlarmSubExpression alarm1 = alarms.get(0); AlarmSubExpression alarm2 = alarms.get(1); AlarmSubExpression alarm3 = alarms.get(2); assertTrue(expr.evaluate(ImmutableMap.builder() .put(alarm1, true) .put(alarm2, false) .put(alarm3, false) .build())); assertTrue(expr.evaluate(ImmutableMap.builder() .put(alarm1, false) .put(alarm2, true) .put(alarm3, true) .build())); assertFalse(expr.evaluate(ImmutableMap.builder() .put(alarm1, false) .put(alarm2, false) .put(alarm3, true) .build())); assertFalse(expr.evaluate(ImmutableMap.builder() .put(alarm1, false) .put(alarm2, true) .put(alarm3, false) .build())); } public void shouldDefaultPeriodAndPeriods() { AlarmExpression expr = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=1}) > 5"); AlarmSubExpression alarm = expr.getSubExpressions().get(0); assertEquals(alarm.getPeriod(), 60); assertEquals(alarm.getPeriods(), 1); } @Test(expectedExceptions = IllegalArgumentException.class) public void shouldThrowOnEvaluateInvalidSubExpressions() { AlarmExpression expr = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=2}, 1) > 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3"); expr.evaluate(ImmutableMap.builder() .put( new AlarmSubExpression(AggregateFunction.AVG, new MetricDefinition("hpcs.compute", ImmutableMap .builder() .put("flavor_id", "3") .put("metric_name", "mem") .build()), AlarmOperator.LT, 4, 2, 3), true) .build()); } public void shouldGetAlarmExpressionTree() { Object expr = AlarmExpression.of( "(avg(foo) > 1 and avg(bar) < 2 and avg(baz) > 3) or (avg(foo) > 4 and avg(bar) < 5 and avg(baz) > 6)") .getExpressionTree(); assertEquals( expr.toString(), "((avg(foo) > 1.0 AND avg(bar) < 2.0 AND avg(baz) > 3.0) OR (avg(foo) > 4.0 AND avg(bar) < 5.0 AND avg(baz) > 6.0))"); expr = AlarmExpression.of( "(avg(foo) > 1 and (avg(bar) < 2 or avg(baz) > 3)) and (avg(foo) > 4 or avg(bar) < 5 or avg(baz) > 6)") .getExpressionTree(); assertEquals( expr.toString(), "(avg(foo) > 1.0 AND (avg(bar) < 2.0 OR avg(baz) > 3.0) AND (avg(foo) > 4.0 OR avg(bar) < 5.0 OR avg(baz) > 6.0))"); } @Test(enabled = false) public void testExpressionEquality() { AlarmExpression expr1 = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=a}, 1) lt 5 times 3 and avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3"); AlarmExpression expr2 = new AlarmExpression( "avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) gt 3 times 3 && avg(hpcs.compute{instance_id=5,metric_name=cpu,device=a}, 1) lt 5 times 3"); assertEquals(expr1, expr2); AlarmExpression expr3 = new AlarmExpression( "avg(hpcs.compute{instance_id=5,metric_name=cpu,device=a}, 1) lt 5 times 444 and avg(hpcs.compute{flavor_id=3,metric_name=mem}, 2) < 4 times 3"); assertNotEquals(expr1, expr3); } public void shouldParseNamesWithUnicode() { AlarmExpression expr1 = new AlarmExpression( "公{此=该,metric_name=mem} > 4" ); AlarmSubExpression alarm1 = expr1.getSubExpressions().get(0); MetricDefinition expected1 = new MetricDefinition("公", ImmutableMap.builder() .put("此", "该") .put("metric_name", "mem") .build()); assertEquals(alarm1.getMetricDefinition(), expected1); } public void shouldParseDimensionsWithSpaces() { AlarmExpression[] expr_list = { new AlarmExpression("test_metric{this_is_a_test=this is a test} > 10"), new AlarmExpression("test_metric{this is also a test = this_is_also_a_test} > 10") }; MetricDefinition[] expected_list = { new MetricDefinition("test_metric", ImmutableMap.builder() .put("this_is_a_test", "this is a test") .build()), new MetricDefinition("test_metric", ImmutableMap.builder() .put("this is also a test", "this_is_also_a_test") .build()) }; for(int i = 0; i < expr_list.length; i++) { AlarmSubExpression expr = expr_list[i].getSubExpressions().get(0); assertEquals(expr.getMetricDefinition(), expected_list[i]); } } public void shouldFailWithRestrictedChars() { String[] expressions = {"%cmetric{foo=bar,metric_name=mem} > 4", "me%ctric{foo=bar,metric_name=mem} > 4", "metric%c{foo=bar,metric_name=mem} > 4", "metric{%cfoo=bar,metric_name=mem} > 4", "metric{f%coo=bar,metric_name=mem} > 4", "metric{foo%c=bar,metric_name=mem} > 4", "metric{foo=%cbar,metric_name=mem} > 4", "metric{foo=b%car,metric_name=mem} > 4", "metric{foo=bar%c,metric_name=mem} > 4"}; for (int i = 0; i < expressions.length; i++) { for (int j = 0; j < restrictedChars.length(); j++) { String exprStr = String.format(expressions[i], restrictedChars.charAt(j)); try { AlarmExpression expr = new AlarmExpression(exprStr); fail(String.format("Successfully parsed invalid expression: %s", exprStr)); } catch (Exception ex) { //System.out.println(ex); } } } } public void shouldParseSpacings() { AlarmExpression expr = new AlarmExpression("avg ( metric { foo = bar , metric_name = mem } ) > 4" + " or avg(metric{foo=bar,metric_name=mem})>4" + " or avg( metric{ foo= bar, metric_name= mem} )> 4" + " or avg (metric {foo =bar ,metric_name =mem }) >4"); List subExpressions = expr.getSubExpressions(); for(int i = 1; i < subExpressions.size(); i++){ assertEquals(subExpressions.get(0),subExpressions.get(i)); } } public void shouldParseComplexExpression() { AlarmExpression expr = new AlarmExpression("max(-_.千幸福的笑脸{घोड़ा=馬," + "dn2=dv2,千幸福的笑脸घ=千幸福的笑脸घ}) gte 100 " + "times 3 && " + "(min(ເຮືອນ{dn3=dv3,家=дом}) < 10 or sum(biz{dn5=dv5}) >9 and " + "count(fizzle) lt 0 or count(baz) > 1)"); } }