You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by am...@apache.org on 2018/01/05 07:56:34 UTC
[38/45] ambari git commit: AMBARI-22236. Expression parser support
for JMXServerSide alerts (amagyar)
AMBARI-22236. Expression parser support for JMXServerSide alerts (amagyar)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/3011a482
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/3011a482
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/3011a482
Branch: refs/heads/branch-feature-AMBARI-22008-isilon
Commit: 3011a4823ad1cf1dd5fb958437bf85d7a4eb8f4d
Parents: 3287d95
Author: Attila Magyar <am...@hortonworks.com>
Authored: Tue Oct 17 16:15:15 2017 +0200
Committer: Attila Magyar <am...@hortonworks.com>
Committed: Fri Jan 5 08:54:45 2018 +0100
----------------------------------------------------------------------
.../server/alerts/JmxServerSideAlert.java | 42 ++++++------
.../server/state/alert/AlertDefinition.java | 13 ++++
.../ambari/server/state/alert/MetricSource.java | 59 ++++++++++++++++-
.../ambari/server/state/alert/Reporting.java | 16 ++---
.../AlertDefinitionResourceProviderTest.java | 4 +-
.../ambari/server/state/alert/JmxInfoTest.java | 68 ++++++++++++++++++++
6 files changed, 162 insertions(+), 40 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/3011a482/ambari-server/src/main/java/org/apache/ambari/server/alerts/JmxServerSideAlert.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/alerts/JmxServerSideAlert.java b/ambari-server/src/main/java/org/apache/ambari/server/alerts/JmxServerSideAlert.java
index a4b86f8..09eb0a4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/alerts/JmxServerSideAlert.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/alerts/JmxServerSideAlert.java
@@ -23,6 +23,7 @@ import static java.util.Collections.singletonList;
import java.net.URI;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.configuration.ComponentSSLConfiguration;
@@ -35,7 +36,6 @@ import org.apache.ambari.server.state.ConfigHelper;
import org.apache.ambari.server.state.alert.AlertDefinition;
import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
import org.apache.ambari.server.state.alert.MetricSource;
-import org.apache.ambari.server.state.alert.Reporting;
import org.apache.ambari.server.state.alert.ServerSource;
import org.apache.ambari.server.state.services.MetricsRetrievalService;
import org.slf4j.Logger;
@@ -64,41 +64,35 @@ public class JmxServerSideAlert extends AlertRunnable {
List<Alert> execute(Cluster cluster, AlertDefinitionEntity entity) throws AmbariException {
AlertDefinition alertDef = definitionFactory.coerce(entity);
ServerSource serverSource = (ServerSource) alertDef.getSource();
- URI jmxUrl = jmxUrl(cluster, serverSource);
- JMXMetricHolder metricHolder = jmxMetric(serverSource, jmxUrl);
- return metricHolder == null
- ? emptyList()
- : alerts(alertDef, serverSource.getJmxInfo(), metricHolder, serverSource.getReporting());
+ return buildAlert(jmxMetric(serverSource, cluster), serverSource.getJmxInfo(), alertDef)
+ .map(alert -> singletonList(alert))
+ .orElse(emptyList());
}
- private URI jmxUrl(Cluster cluster, ServerSource serverSource) throws AmbariException {
- return serverSource.getUri().resolve(config(cluster)).resolve(serverSource.getJmxInfo().getUrlSuffix());
+ public Optional<Alert> buildAlert(Optional<JMXMetricHolder> metricHolder, MetricSource.JmxInfo jmxInfo, AlertDefinition alertDef) {
+ return metricHolder.flatMap(metric -> buildAlert(metric, jmxInfo, alertDef));
}
- private Map<String, Map<String, String>> config(Cluster cluster) throws AmbariException {
- return configHelper.getEffectiveConfigProperties(cluster, configHelper.getEffectiveDesiredTags(cluster, null));
+ private Optional<Alert> buildAlert(JMXMetricHolder metricHolder, MetricSource.JmxInfo jmxInfo, AlertDefinition alertDef) {
+ List<Object> allMetrics = metricHolder.findAll(jmxInfo.getPropertyList());
+ return jmxInfo.eval(metricHolder).map(val -> alertDef.buildAlert(val.doubleValue(), allMetrics));
}
- private JMXMetricHolder jmxMetric(ServerSource serverSource, URI jmxUri) {
+ private Optional<JMXMetricHolder> jmxMetric(ServerSource serverSource, Cluster cluster) throws AmbariException {
+ URI jmxUri = jmxUrl(cluster, serverSource);
URLStreamProvider streamProvider = new URLStreamProvider(
serverSource.getUri().getConnectionTimeoutMsec(),
serverSource.getUri().getReadTimeoutMsec(),
ComponentSSLConfiguration.instance());
metricsRetrievalService.submitRequest(MetricsRetrievalService.MetricSourceType.JMX, streamProvider, jmxUri.toString());
- return metricsRetrievalService.getCachedJMXMetric(jmxUri.toString());
+ return Optional.ofNullable(metricsRetrievalService.getCachedJMXMetric(jmxUri.toString()));
+ }
+
+ private URI jmxUrl(Cluster cluster, ServerSource serverSource) throws AmbariException {
+ return serverSource.getUri().resolve(config(cluster)).resolve(serverSource.getJmxInfo().getUrlSuffix());
}
- private List<Alert> alerts(AlertDefinition alertDef, MetricSource.JmxInfo jmxInfo, JMXMetricHolder jmxMetricHolder, Reporting reporting) throws AmbariException {
- List<Object> metrics = jmxMetricHolder.findAll(jmxInfo.getPropertyList());
- if (metrics.isEmpty()) {
- return emptyList();
- }
- if (metrics.get(0) instanceof Number) {
- Alert alert = reporting.alert(((Number) metrics.get(0)).doubleValue(), metrics, alertDef);
- return singletonList(alert);
- } else {
- LOG.info("Unsupported metrics value: {} when running alert: {}", metrics.get(0), alertDef);
- return emptyList();
- }
+ private Map<String, Map<String, String>> config(Cluster cluster) throws AmbariException {
+ return configHelper.getEffectiveConfigProperties(cluster, configHelper.getEffectiveDesiredTags(cluster, null));
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/3011a482/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinition.java
index 665430d..f1f21a4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinition.java
@@ -18,7 +18,10 @@
package org.apache.ambari.server.state.alert;
import java.util.HashSet;
+import java.util.List;
+import org.apache.ambari.server.state.Alert;
+import org.apache.ambari.server.state.AlertState;
import org.apache.commons.lang.StringUtils;
import com.google.gson.annotations.SerializedName;
@@ -354,6 +357,16 @@ public class AlertDefinition {
}
/**
+ * Map the incoming value to {@link AlertState} and generate an alert with that state.
+ */
+ public Alert buildAlert(double value, List<Object> args) {
+ Reporting reporting = source.getReporting();
+ Alert alert = new Alert(name, null, serviceName, componentName, null, reporting.state(value));
+ alert.setText(reporting.formatMessage(value, args));
+ return alert;
+ }
+
+ /**
* Gets equality based on name only.
*
* @see #deeplyEquals(Object)
http://git-wip-us.apache.org/repos/asf/ambari/blob/3011a482/ambari-server/src/main/java/org/apache/ambari/server/state/alert/MetricSource.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/MetricSource.java b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/MetricSource.java
index d7283fe..019f3b1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/MetricSource.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/MetricSource.java
@@ -17,8 +17,16 @@
*/
package org.apache.ambari.server.state.alert;
+import static java.util.stream.Collectors.toMap;
+import static java.util.stream.IntStream.range;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+
+import org.apache.ambari.server.controller.jmx.JMXMetricHolder;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
import com.google.gson.annotations.SerializedName;
@@ -127,7 +135,7 @@ public class MetricSource extends Source {
@SerializedName("property_list")
private List<String> propertyList;
- private String value;
+ private String value = "{0}";
@SerializedName("url_suffix")
private String urlSuffix = "/jmx";
@@ -136,8 +144,16 @@ public class MetricSource extends Source {
return propertyList;
}
- public String getValue() {
- return value;
+ public void setPropertyList(List<String> propertyList) {
+ this.propertyList = propertyList;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public Value getValue() {
+ return new Value(value);
}
@Override
@@ -159,5 +175,42 @@ public class MetricSource extends Source {
public String getUrlSuffix() {
return urlSuffix;
}
+
+ public Optional<Number> eval(JMXMetricHolder jmxMetricHolder) {
+ List<Object> metrics = jmxMetricHolder.findAll(propertyList);
+ if (metrics.isEmpty()) {
+ return Optional.empty();
+ } else {
+ Object value = getValue().eval(metrics);
+ return value instanceof Number ? Optional.of((Number)value) : Optional.empty();
+ }
+ }
+ }
+
+ public static class Value {
+ private final String value;
+
+ public Value(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Evaluate an expression like "{0}/({0} + {1}) * 100.0" where each positional argument represent a metrics value.
+ * The value is converted to SpEL syntax:
+ * #var0/(#var0 + #var1) * 100.0
+ * then it is evaluated in the context of the metrics parameters.
+ */
+ public Object eval(List<Object> metrics) {
+ StandardEvaluationContext context = new StandardEvaluationContext();
+ context.setVariables(range(0, metrics.size()).boxed().collect(toMap(i -> "var" + i, metrics::get)));
+ return new SpelExpressionParser()
+ .parseExpression(value.replaceAll("(\\{(\\d+)\\})", "#var$2"))
+ .getValue(context);
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/3011a482/ambari-server/src/main/java/org/apache/ambari/server/state/alert/Reporting.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/Reporting.java b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/Reporting.java
index 51d074e..a7e11e1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/Reporting.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/Reporting.java
@@ -23,7 +23,6 @@ import java.text.MessageFormat;
import java.util.List;
import org.apache.ambari.server.alerts.Threshold;
-import org.apache.ambari.server.state.Alert;
import org.apache.ambari.server.state.AlertState;
import com.google.gson.annotations.SerializedName;
@@ -208,16 +207,7 @@ public class Reporting {
return true;
}
- /**
- * Map the incoming value to {@link AlertState} and generate an alert with that state.
- */
- public Alert alert(double value, List<Object> args, AlertDefinition alertDef) {
- Alert alert = new Alert(alertDef.getName(), null, alertDef.getServiceName(), alertDef.getComponentName(), null, state(value));
- alert.setText(MessageFormat.format(message(value), args.toArray()));
- return alert;
- }
-
- private AlertState state(double value) {
+ public AlertState state(double value) {
return getThreshold().state(PERCENT == getType() ? value * 100 : value);
}
@@ -225,6 +215,10 @@ public class Reporting {
return new Threshold(getOk().getValue(), getWarning().getValue(), getCritical().getValue());
}
+ public String formatMessage(double value, List<Object> args) {
+ return MessageFormat.format(message(value), args.toArray());
+ }
+
private String message(double value) {
switch (state(value)) {
case OK:
http://git-wip-us.apache.org/repos/asf/ambari/blob/3011a482/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
index 3ef2c48..e8ad651 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
@@ -457,7 +457,7 @@ public class AlertDefinitionResourceProviderTest {
// JMX
requestProps.put("AlertDefinition/source/jmx/value",
- source.getJmxInfo().getValue());
+ source.getJmxInfo().getValue().toString());
requestProps.put("AlertDefinition/source/jmx/property_list",
source.getJmxInfo().getPropertyList());
@@ -600,7 +600,7 @@ public class AlertDefinitionResourceProviderTest {
// JMX
requestProps.put("AlertDefinition/source/jmx/value",
- source.getJmxInfo().getValue());
+ source.getJmxInfo().getValue().toString());
requestProps.put("AlertDefinition/source/jmx/property_list",
source.getJmxInfo().getPropertyList());
http://git-wip-us.apache.org/repos/asf/ambari/blob/3011a482/ambari-server/src/test/java/org/apache/ambari/server/state/alert/JmxInfoTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/alert/JmxInfoTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/alert/JmxInfoTest.java
new file mode 100644
index 0000000..308ab4f
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/alert/JmxInfoTest.java
@@ -0,0 +1,68 @@
+package org.apache.ambari.server.state.alert;
+
+import static java.util.Arrays.asList;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+import org.apache.ambari.server.controller.jmx.JMXMetricHolder;
+import org.junit.Test;
+
+public class JmxInfoTest {
+ private static final String JMX_PROP_NAME1 = "Hadoop:service=NameNode,name=FSNamesystem/CapacityUsed";
+ private static final String JMX_PROP_NAME2 = "Hadoop:service=NameNode,name=FSNamesystem/CapacityRemaining";
+
+ @Test
+ public void testFindJmxMetricsAndCalculateSimpleValue() throws Exception {
+ MetricSource.JmxInfo jmxInfo = jmxInfoWith("{1}");
+ JMXMetricHolder metrics = metrics(12.5, 3.5);
+ assertThat(jmxInfo.eval(metrics), is(Optional.of(3.5)));
+ }
+
+ @Test
+ public void testFindJmxMetricsAndCalculateComplexValue() throws Exception {
+ MetricSource.JmxInfo jmxInfo = jmxInfoWith("2 * ({0} + {1})");
+ JMXMetricHolder metrics = metrics(12.5, 2.5);
+ assertThat(jmxInfo.eval(metrics), is(Optional.of(30.0)));
+ }
+
+ @Test
+ public void testReturnsEmptyWhenJmxPropertyWasNotFound() throws Exception {
+ MetricSource.JmxInfo jmxInfo = new MetricSource.JmxInfo();
+ jmxInfo.setPropertyList(asList("notfound/notfound"));
+ JMXMetricHolder metrics = metrics(1, 2);
+ assertThat(jmxInfo.eval(metrics), is(Optional.empty()));
+ }
+
+ private MetricSource.JmxInfo jmxInfoWith(String value) {
+ MetricSource.JmxInfo jmxInfo = new MetricSource.JmxInfo();
+ jmxInfo.setValue(value);
+ jmxInfo.setPropertyList(asList(JMX_PROP_NAME1, JMX_PROP_NAME2));
+ return jmxInfo;
+ }
+
+ private JMXMetricHolder metrics(final double jmxValue1, final double jmxValue2) {
+ JMXMetricHolder metrics = new JMXMetricHolder();
+ metrics.setBeans(asList(
+ new HashMap<String,Object>() {{
+ put("name", name(JMX_PROP_NAME1));
+ put(key(JMX_PROP_NAME1), jmxValue1);
+ }},
+ new HashMap<String,Object>() {{
+ put("name", name(JMX_PROP_NAME2));
+ put(key(JMX_PROP_NAME2), jmxValue2);
+ }}
+ ));
+ return metrics;
+ }
+
+ private String name(String jmxProp) {
+ return jmxProp.split("/")[0];
+ }
+
+ private String key(String jmxProp) {
+ return jmxProp.split("/")[1];
+ }
+}
\ No newline at end of file