Kaynağa Gözat

AMBARI-6915 - Alerts: Change AlertDefinition to Support a Reporting Member (jonathanhurley)

Jonathan Hurley 11 yıl önce
ebeveyn
işleme
0ac9cb3fac

+ 1 - 15
ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java

@@ -31,7 +31,6 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Scanner;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -1093,19 +1092,6 @@ public class AmbariMetaInfo {
       return null;
     }
 
-    Set<AlertDefinition> defs = new HashSet<AlertDefinition>();
-    Map<String, List<AlertDefinition>> map = alertDefinitionFactory.getAlertDefinitions(alertsFile);
-
-    for (Entry<String, List<AlertDefinition>> entry : map.entrySet()) {
-      for (AlertDefinition ad : entry.getValue()) {
-        ad.setServiceName(serviceName);
-        if (!entry.getKey().equals("service")) {
-          ad.setComponentName(entry.getKey());
-        }
-      }
-      defs.addAll(entry.getValue());
-    }
-
-    return defs;
+    return alertDefinitionFactory.getAlertDefinitions(alertsFile, serviceName);
   }
 }

+ 57 - 6
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java

@@ -61,18 +61,26 @@ import com.google.inject.Injector;
  */
 public class AlertDefinitionResourceProvider extends AbstractControllerResourceProvider {
 
+  protected static final String ALERT_DEF = "AlertDefinition";
+
   protected static final String ALERT_DEF_CLUSTER_NAME = "AlertDefinition/cluster_name";
   protected static final String ALERT_DEF_ID = "AlertDefinition/id";
   protected static final String ALERT_DEF_NAME = "AlertDefinition/name";
   protected static final String ALERT_DEF_LABEL = "AlertDefinition/label";
   protected static final String ALERT_DEF_INTERVAL = "AlertDefinition/interval";
-  protected static final String ALERT_DEF_SOURCE_TYPE = "AlertDefinition/source/type";
-  protected static final String ALERT_DEF_SOURCE = "AlertDefinition/source";
   protected static final String ALERT_DEF_SERVICE_NAME = "AlertDefinition/service_name";
   protected static final String ALERT_DEF_COMPONENT_NAME = "AlertDefinition/component_name";
   protected static final String ALERT_DEF_ENABLED = "AlertDefinition/enabled";
   protected static final String ALERT_DEF_SCOPE = "AlertDefinition/scope";
 
+  protected static final String ALERT_DEF_SOURCE = "AlertDefinition/source";
+  protected static final String ALERT_DEF_SOURCE_TYPE = "AlertDefinition/source/type";
+  protected static final String ALERT_DEF_SOURCE_REPORTING = "AlertDefinition/source/reporting";
+  protected static final String ALERT_DEF_SOURCE_REPORTING_OK = "AlertDefinition/source/reporting/ok";
+  protected static final String ALERT_DEF_SOURCE_REPORTING_WARNING = "AlertDefinition/source/reporting/warning";
+  protected static final String ALERT_DEF_SOURCE_REPORTING_CRITICAL = "AlertDefinition/source/reporting/critical";
+
+
   private static Set<String> pkPropertyIds = new HashSet<String>(
       Arrays.asList(ALERT_DEF_ID, ALERT_DEF_NAME));
 
@@ -176,21 +184,64 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
               SourceType.class)));
     }
 
-    JsonObject jsonObj = new JsonObject();
+    // !!! Alert structures contain nested objects; reconstruct a valid
+    // JSON from the flat, exploded properties so that a Source instance can
+    // be properly persisted
+    JsonObject source = new JsonObject();
+    JsonObject reporting = new JsonObject();
+    JsonObject reportingOk = new JsonObject();
+    JsonObject reportingWarning = new JsonObject();
+    JsonObject reportingCritical = new JsonObject();
 
     for (Entry<String, Object> entry : requestMap.entrySet()) {
       String propCat = PropertyHelper.getPropertyCategory(entry.getKey());
       String propName = PropertyHelper.getPropertyName(entry.getKey());
 
+      if (propCat.equals(ALERT_DEF) && "source".equals(propName)) {
+        source.addProperty(propName, entry.getValue().toString());
+      }
+
       if (propCat.equals(ALERT_DEF_SOURCE)) {
-        jsonObj.addProperty(propName, entry.getValue().toString());
+        source.addProperty(propName, entry.getValue().toString());
+      }
+
+      if (propCat.equals(ALERT_DEF_SOURCE_REPORTING)) {
+        reporting.addProperty(propName, entry.getValue().toString());
+      }
+
+      if (propCat.equals(ALERT_DEF_SOURCE_REPORTING_OK)) {
+        reportingOk.addProperty(propName, entry.getValue().toString());
+      }
+
+      if (propCat.equals(ALERT_DEF_SOURCE_REPORTING_WARNING)) {
+        reportingWarning.addProperty(propName, entry.getValue().toString());
+      }
+
+      if (propCat.equals(ALERT_DEF_SOURCE_REPORTING_CRITICAL)) {
+        reportingCritical.addProperty(propName, entry.getValue().toString());
       }
     }
 
-    if (0 == jsonObj.entrySet().size()) {
+    if (0 == source.entrySet().size()) {
       throw new IllegalArgumentException("Source must be specified");
     }
 
+    if (reportingOk.entrySet().size() > 0) {
+      reporting.add("ok", reportingOk);
+    }
+
+    if (reportingWarning.entrySet().size() > 0) {
+      reporting.add("warning", reportingWarning);
+    }
+
+    if (reportingCritical.entrySet().size() > 0) {
+      reporting.add("critical", reportingCritical);
+    }
+
+    if (reporting.entrySet().size() > 0) {
+      source.add("reporting", reporting);
+    }
+
     Cluster cluster = getManagementController().getClusters().getCluster(clusterName);
 
     AlertDefinitionEntity entity = new AlertDefinitionEntity();
@@ -207,7 +258,7 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
     entity.setScheduleInterval(interval);
     entity.setServiceName((String) requestMap.get(ALERT_DEF_SERVICE_NAME));
     entity.setSourceType((String) requestMap.get(ALERT_DEF_SOURCE_TYPE));
-    entity.setSource(jsonObj.toString());
+    entity.setSource(source.toString());
 
     Scope scope = null;
     String desiredScope = (String) requestMap.get(ALERT_DEF_SCOPE);

+ 38 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/alert/AggregateSource.java

@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.alert;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Alert when the source type is defined as {@link SourceType#AGGREGATE}.
+ * Aggregate alerts are alerts that are triggered by collecting the states of
+ * all instances of the defined alert and calculating the overall state.
+ */
+public class AggregateSource extends Source {
+
+  @SerializedName("alert_name")
+  private String m_alertName = null;
+
+  /**
+   * @return the unique name of the alert that will have its values aggregated.
+   */
+  public String getAlertName() {
+    return m_alertName;
+  }
+}

+ 17 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinition.java

@@ -31,6 +31,7 @@ public class AlertDefinition {
   private int interval = 1;
   private boolean enabled = true;
   private Source source = null;
+  private String label = null;
 
   /**
    * @return the service name
@@ -117,6 +118,22 @@ public class AlertDefinition {
     source = definitionSource;
   }
 
+  /**
+   * @return the label for the definition or {@code null} if none.
+   */
+  public String getLabel() {
+    return label;
+  }
+
+  /**
+   * Sets the label for this definition.
+   *
+   * @param definitionLabel
+   */
+  public void setLabel(String definitionLabel) {
+    label = definitionLabel;
+  }
+
   @Override
   public boolean equals(Object obj) {
     if (null == obj || !obj.getClass().equals(AlertDefinition.class)) {

+ 71 - 13
ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionFactory.java

@@ -20,8 +20,11 @@ package org.apache.ambari.server.state.alert;
 import java.io.File;
 import java.io.FileReader;
 import java.lang.reflect.Type;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
@@ -52,34 +55,58 @@ public class AlertDefinitionFactory {
   /**
    * Builder used for type adapter registration.
    */
-  private final GsonBuilder m_builder = new GsonBuilder().registerTypeAdapter(
-      Source.class, new AlertDefinitionSourceAdapter());
+  private final GsonBuilder m_builder = new GsonBuilder();
 
   /**
    * Thread safe deserializer.
    */
-  private final Gson m_gson = m_builder.create();
+  private final Gson m_gson;
 
+  /**
+   * Constructor.
+   */
+  public AlertDefinitionFactory() {
+    m_builder.registerTypeAdapter(Source.class,
+        new AlertDefinitionSourceAdapter());
+
+    m_gson = m_builder.create();
+  }
 
   /**
    * Gets a list of all of the alert definitions defined in the specified JSON
-   * {@link File}.
+   * {@link File} for the given service.
    *
    * @param alertDefinitionFile
+   * @param serviceName
    * @return
    * @throws AmbariException
    *           if there was a problem reading the file or parsing the JSON.
    */
-  public Map<String, List<AlertDefinition>> getAlertDefinitions(
-      File alertDefinitionFile) throws AmbariException {
+  public Set<AlertDefinition> getAlertDefinitions(File alertDefinitionFile,
+      String serviceName) throws AmbariException {
+    Map<String,List<AlertDefinition>> definitionMap = null;
+
     try {
       Type type = new TypeToken<Map<String, List<AlertDefinition>>>(){}.getType();
 
-      return m_gson.fromJson(new FileReader(alertDefinitionFile), type);
+      definitionMap = m_gson.fromJson(new FileReader(alertDefinitionFile), type);
     } catch (Exception e) {
       LOG.error("Could not read the alert definition file", e);
       throw new AmbariException("Could not read alert definition file", e);
     }
+
+    Set<AlertDefinition> definitions = new HashSet<AlertDefinition>();
+    for (Entry<String, List<AlertDefinition>> entry : definitionMap.entrySet()) {
+      for (AlertDefinition ad : entry.getValue()) {
+        ad.setServiceName(serviceName);
+        if (!entry.getKey().equals("service")) {
+          ad.setComponentName(entry.getKey());
+        }
+      }
+      definitions.addAll(entry.getValue());
+    }
+
+    return definitions;
   }
 
   /**
@@ -103,6 +130,7 @@ public class AlertDefinitionFactory {
     definition.setName(entity.getDefinitionName());
     definition.setScope(entity.getScope());
     definition.setServiceName(entity.getServiceName());
+    definition.setLabel(entity.getLabel());
 
     try{
       String sourceJson = entity.getSource();
@@ -117,6 +145,16 @@ public class AlertDefinitionFactory {
     return definition;
   }
 
+  /**
+   * Gets an instance of {@link Gson} that can correctly serialize and
+   * deserialize an {@link AlertDefinition}.
+   *
+   * @return a {@link Gson} instance (not {@code null}).
+   */
+  public Gson getGson() {
+    return m_gson;
+  }
+
   /**
    * Deserializes {@link Source} implementations.
    */
@@ -130,21 +168,41 @@ public class AlertDefinitionFactory {
       JsonObject jsonObj = (JsonObject) json;
 
       SourceType type = SourceType.valueOf(jsonObj.get("type").getAsString());
-      Class<? extends Source> cls = null;
+      Class<? extends Source> clazz = null;
 
       switch (type) {
-        case METRIC:
-          cls = MetricSource.class;
+        case METRIC:{
+          clazz = MetricSource.class;
+          break;
+        }
+        case PORT:{
+          clazz = PortSource.class;
           break;
+        }
+        case SCRIPT: {
+          clazz = ScriptSource.class;
+          break;
+        }
+        case AGGREGATE: {
+          clazz = AggregateSource.class;
+          break;
+        }
+        case PERCENT: {
+          clazz = PercentSource.class;
+          break;
+        }
         default:
           break;
       }
 
-      if (null != cls) {
-        return context.deserialize(json, cls);
-      } else {
+      if (null == clazz) {
+        LOG.warn(
+            "Unable to deserialize an alert definition with source type {}",
+            type);
         return null;
       }
+
+      return context.deserialize(json, clazz);
     }
   }
 }

+ 76 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/alert/PercentSource.java

@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.alert;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Alert when the source type is defined as {@link SourceType#PERCENT}
+ */
+public class PercentSource extends Source {
+
+  @SerializedName("numerator")
+  private MetricFractionPart m_numerator = null;
+
+  @SerializedName("denominator")
+  private MetricFractionPart m_denominator = null;
+
+  /**
+   * Gets the numerator for the percent calculation.
+   * 
+   * @return a metric value representing the numerator (never {@code null}).
+   */
+  public MetricFractionPart getNumerator() {
+    return m_numerator;
+  }
+
+  /**
+   * Gets the denomintor for the percent calculation.
+   *
+   * @return a metric value representing the denominator (never {@code null}).
+   */
+  public MetricFractionPart getDenominator() {
+    return m_denominator;
+  }
+
+  /**
+   * The {@link MetricFractionPart} class represents either the numerator or the
+   * denominator of a fraction.
+   */
+  public static final class MetricFractionPart {
+    @SerializedName("jmx")
+    private String m_jmxInfo = null;
+
+    @SerializedName("ganglia")
+    private String m_gangliaInfo = null;
+
+    /**
+     * @return the jmx info, if this metric is jmx-based
+     */
+    public String getJmxInfo() {
+      return m_jmxInfo;
+    }
+
+    /**
+     * @return the ganglia info, if this metric is ganglia-based
+     */
+    public String getGangliaInfo() {
+      return m_gangliaInfo;
+    }
+  }
+}

+ 46 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/alert/PortSource.java

@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.alert;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Alert when the source type is defined as {@link SourceType#PORT}
+ */
+public class PortSource extends Source {
+
+  @SerializedName("uri")
+  private String m_uri = null;
+
+  @SerializedName("port")
+  private int m_port = 0;
+
+  /**
+   * @return the URI to check for a valid port
+   */
+  public String getUri() {
+    return m_uri;
+  }
+
+  /**
+   * @return the port to check on the given URI.
+   */
+  public int getPort() {
+    return m_port;
+  }
+}

+ 92 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/alert/Reporting.java

@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.alert;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * The {@link Reporting} class represents the OK/WARNING/CRITICAL structures in
+ * an {@link AlertDefinition}.
+ */
+public class Reporting {
+
+  /**
+   *
+   */
+  @SerializedName("ok")
+  private ReportTemplate m_ok;
+
+  /**
+   *
+   */
+  @SerializedName("warning")
+  private ReportTemplate m_warning;
+
+  /**
+   *
+   */
+  @SerializedName("critical")
+  private ReportTemplate m_critical;
+
+  /**
+   * @return the WARNING structure or {@code null} if none.
+   */
+  public ReportTemplate getWarning() {
+    return m_warning;
+  }
+
+  /**
+   * @return the CRITICAL structure or {@code null} if none.
+   */
+  public ReportTemplate getCritical() {
+    return m_critical;
+  }
+
+  /**
+   * @return the OK structure or {@code null} if none.
+   */
+  public ReportTemplate getOk() {
+    return m_ok;
+  }
+
+  /**
+   * The {@link ReportTemplate} class is used to pair a label and threshhold
+   * value.
+   */
+  public static final class ReportTemplate {
+    @SerializedName("text")
+    private String m_text;
+
+    @SerializedName("value")
+    private Double m_value = null;
+
+    /**
+     * @return the parameterized text of this template or {@code null} if none.
+     */
+    public String getText() {
+      return m_text;
+    }
+
+    /**
+     * @return the threshold value for this template or {@code null} if none.
+     */
+    public Double getValue() {
+      return m_value;
+    }
+  }
+}

+ 36 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/alert/ScriptSource.java

@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.state.alert;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Alert when the source type is defined as {@link SourceType#SCRIPT}
+ */
+public class ScriptSource extends Source {
+
+  @SerializedName("path")
+  private String m_path = null;
+
+  /**
+   * @return the path to the script file.
+   */
+  public String getPath() {
+    return m_path;
+  }
+}

+ 12 - 1
ambari-server/src/main/java/org/apache/ambari/server/state/alert/Source.java

@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.state.alert;
 
+import com.google.gson.annotations.SerializedName;
+
 /**
  * Abstract class that all known alert sources should extend.
  */
@@ -24,11 +26,20 @@ public abstract class Source {
 
   private SourceType type;
 
+  @SerializedName("reporting")
+  private Reporting reporting;
+
   /**
    * @return the type
    */
   public SourceType getType() {
     return type;
   }
-  
+
+  /**
+   * @return
+   */
+  public Reporting getReporting() {
+    return reporting;
+  }
 }

+ 7 - 2
ambari-server/src/main/java/org/apache/ambari/server/state/alert/SourceType.java

@@ -18,7 +18,7 @@
 package org.apache.ambari.server.state.alert;
 
 /**
- * Source type refers to how the alert is to be collected. 
+ * Source type refers to how the alert is to be collected.
  */
 public enum SourceType {
   /**
@@ -36,5 +36,10 @@ public enum SourceType {
   /**
    * Source is an aggregate of a collection of other alert states
    */
-  AGGREGATE
+  AGGREGATE,
+
+  /**
+   * Source is a ratio of two {@link #METRIC} values.
+   */
+  PERCENT;
 }

+ 51 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java

@@ -29,6 +29,7 @@ import java.io.File;
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
@@ -54,6 +55,8 @@ import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.Stack;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.alert.AlertDefinition;
+import org.apache.ambari.server.state.alert.Reporting;
+import org.apache.ambari.server.state.alert.Source;
 import org.apache.ambari.server.state.stack.MetricDefinition;
 import org.apache.commons.io.FileUtils;
 import org.junit.Before;
@@ -1420,5 +1423,53 @@ public class AmbariMetaInfoTest {
     Assert.assertNotNull(set);
     Assert.assertTrue(set.size() > 0);
 
+    // find two different definitions and test each one
+    AlertDefinition nameNodeProcess = null;
+    AlertDefinition nameNodeCpu = null;
+
+    Iterator<AlertDefinition> iterator = set.iterator();
+    while (iterator.hasNext()) {
+      AlertDefinition definition = iterator.next();
+      if (definition.getName().equals("namenode_process")) {
+        nameNodeProcess = definition;
+      }
+
+      if (definition.getName().equals("namenode_cpu")) {
+        nameNodeCpu = definition;
+      }
+    }
+
+    assertNotNull(nameNodeProcess);
+    assertNotNull(nameNodeCpu);
+
+    assertEquals("NameNode host CPU Utilization", nameNodeCpu.getLabel());
+
+    Source source = nameNodeProcess.getSource();
+    assertNotNull(source);
+
+    // test namenode_process
+    Reporting reporting = source.getReporting();
+    assertNotNull(reporting);
+    assertNotNull(reporting.getOk());
+    assertNotNull(reporting.getOk().getText());
+    assertNull(reporting.getOk().getValue());
+    assertNotNull(reporting.getCritical());
+    assertNotNull(reporting.getCritical().getText());
+    assertNull(reporting.getCritical().getValue());
+    assertNull(reporting.getWarning());
+
+    // test namenode_cpu
+    source = nameNodeCpu.getSource();
+    reporting = source.getReporting();
+    assertNotNull(reporting);
+    assertNotNull(reporting.getOk());
+    assertNotNull(reporting.getOk().getText());
+    assertNull(reporting.getOk().getValue());
+    assertNotNull(reporting.getCritical());
+    assertNotNull(reporting.getCritical().getText());
+    assertNotNull(reporting.getCritical().getValue());
+    assertNotNull(reporting.getWarning());
+    assertNotNull(reporting.getWarning().getText());
+    assertNotNull(reporting.getWarning().getValue());
   }
 }

+ 81 - 8
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java

@@ -29,10 +29,12 @@ import static org.easymock.EasyMock.resetToStrict;
 import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertEquals;
 
+import java.io.File;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -44,18 +46,24 @@ import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.metadata.ActionMetadata;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.alert.AlertDefinition;
+import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
 import org.apache.ambari.server.state.alert.AlertDefinitionHash;
+import org.apache.ambari.server.state.alert.Source;
+import org.apache.ambari.server.state.alert.SourceType;
 import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.gson.Gson;
 import com.google.inject.Binder;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
@@ -69,6 +77,7 @@ public class AlertDefinitionResourceProviderTest {
 
   private AlertDefinitionDAO dao = null;
   private AlertDefinitionHash definitionHash = null;
+  private AlertDefinitionFactory m_factory = new AlertDefinitionFactory();
   private Injector m_injector;
 
   private static String DEFINITION_UUID = UUID.randomUUID().toString();
@@ -82,6 +91,7 @@ public class AlertDefinitionResourceProviderTest {
         new InMemoryDefaultTestModule()).with(new MockModule()));
 
     AlertDefinitionResourceProvider.init(m_injector);
+    m_injector.injectMembers(m_factory);
   }
 
   /**
@@ -149,6 +159,7 @@ public class AlertDefinitionResourceProviderTest {
         AlertDefinitionResourceProvider.ALERT_DEF_ID,
         AlertDefinitionResourceProvider.ALERT_DEF_NAME,
         AlertDefinitionResourceProvider.ALERT_DEF_LABEL,
+        AlertDefinitionResourceProvider.ALERT_DEF_SOURCE,
         AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE);
 
     AmbariManagementController amc = createMock(AmbariManagementController.class);
@@ -174,9 +185,20 @@ public class AlertDefinitionResourceProviderTest {
     Resource r = results.iterator().next();
 
     Assert.assertEquals("my_def", r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_NAME));
-    Assert.assertEquals("metric", r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE));
+
+    Assert.assertEquals(
+        SourceType.METRIC.name(),
+        r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE));
+
+    Source source = getMockSource();
+    String okJson = source.getReporting().getOk().getText();
+    Object reporting = r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_REPORTING);
+
+    Assert.assertTrue(reporting.toString().contains(okJson));
+
     Assert.assertEquals("Mock Label",
         r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_LABEL));
+
     Assert.assertNotNull(r.getPropertyValue("AlertDefinition/source/type"));
   }
 
@@ -203,14 +225,25 @@ public class AlertDefinitionResourceProviderTest {
 
     replay(amc, clusters, cluster, dao, definitionHash);
 
+    Gson gson = m_factory.getGson();
+    Source source = getMockSource();
+    String sourceJson = gson.toJson(source);
     AlertDefinitionResourceProvider provider = createProvider(amc);
 
     Map<String, Object> requestProps = new HashMap<String, Object>();
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME, "c1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_INTERVAL, "1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_NAME, "my_def");
-    requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SERVICE_NAME, "HDFS");
-    requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE, "METRIC");
+
+    requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SERVICE_NAME,
+        "HDFS");
+
+    requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE,
+        sourceJson);
+
+    requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE,
+        SourceType.METRIC.name());
+
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_LABEL,
         "Mock Label (Create)");
 
@@ -230,10 +263,23 @@ public class AlertDefinitionResourceProviderTest {
     Assert.assertEquals(Integer.valueOf(1), entity.getScheduleInterval());
     Assert.assertNull(entity.getScope());
     Assert.assertEquals("HDFS", entity.getServiceName());
-    Assert.assertNotNull(entity.getSource());
     Assert.assertEquals("METRIC", entity.getSourceType());
     Assert.assertEquals("Mock Label (Create)", entity.getLabel());
 
+    // verify Source
+    Assert.assertNotNull(entity.getSource());
+    Source actualSource = gson.fromJson(entity.getSource(), Source.class);
+    Assert.assertNotNull(actualSource);
+
+    assertEquals(source.getReporting().getOk().getText(),
+        source.getReporting().getOk().getText());
+
+    assertEquals(source.getReporting().getWarning().getText(),
+        source.getReporting().getWarning().getText());
+
+    assertEquals(source.getReporting().getCritical().getText(),
+        source.getReporting().getCritical().getText());
+
     verify(amc, clusters, cluster, dao);
 
   }
@@ -386,7 +432,10 @@ public class AlertDefinitionResourceProviderTest {
   /**
    * @return
    */
-  private List<AlertDefinitionEntity> getMockEntities() {
+  private List<AlertDefinitionEntity> getMockEntities() throws Exception {
+    Source source = getMockSource();
+    String sourceJson = new Gson().toJson(source);
+
     AlertDefinitionEntity entity = new AlertDefinitionEntity();
     entity.setClusterId(Long.valueOf(1L));
     entity.setComponentName(null);
@@ -397,12 +446,35 @@ public class AlertDefinitionResourceProviderTest {
     entity.setHash(DEFINITION_UUID);
     entity.setScheduleInterval(Integer.valueOf(2));
     entity.setServiceName(null);
-    entity.setSourceType("metric");
-    entity.setSource("{'jmx': 'beanName/attributeName', 'host': '{{aa:123445}}'}");
-
+    entity.setSourceType(SourceType.METRIC.name());
+    entity.setSource(sourceJson);
     return Arrays.asList(entity);
   }
 
+  /**
+   * @return
+   */
+  private Source getMockSource() throws Exception {
+    File alertsFile = new File(
+        "src/test/resources/stacks/HDP/2.0.5/services/HDFS/alerts.json");
+
+    Assert.assertTrue(alertsFile.exists());
+
+    Set<AlertDefinition> set = m_factory.getAlertDefinitions(alertsFile, "HDFS");
+    AlertDefinition nameNodeCpu = null;
+    Iterator<AlertDefinition> definitions = set.iterator();
+    while (definitions.hasNext()) {
+      AlertDefinition definition = definitions.next();
+
+      if (definition.getName().equals("namenode_cpu")) {
+        nameNodeCpu = definition;
+      }
+    }
+
+    Assert.assertNotNull(nameNodeCpu.getSource());
+    return nameNodeCpu.getSource();
+  }
+
   /**
   *
   */
@@ -418,6 +490,7 @@ public class AlertDefinitionResourceProviderTest {
           EasyMock.createNiceMock(Clusters.class));
       binder.bind(Cluster.class).toInstance(
           EasyMock.createNiceMock(Cluster.class));
+      binder.bind(ActionMetadata.class);
     }
   }
 }

+ 41 - 20
ambari-server/src/test/resources/stacks/HDP/2.0.5/services/HDFS/alerts.json

@@ -1,18 +1,5 @@
 {
-  "service": [
-  ],
-  "SECONDARY_NAMENODE": [
-    {
-      "name": "secondary_namenode_process",
-      "label": "Secondary NameNode process",
-      "interval": 1,
-      "scope": "service",
-      "source": {
-        "type": "PORT",
-        "config": "{{hdfs-site/dfs.namenode.secondary.http-address}}:50071"
-      }
-    }
-  ],
+  "service": [],
   "NAMENODE": [
     {
       "name": "namenode_cpu",
@@ -21,7 +8,20 @@
       "source": {
         "type": "METRIC",
         "jmx": "java.lang:type=OperatingSystem/SystemCpuLoad",
-        "host": "{{hdfs-site/dfs.namenode.secondary.http-address}}"
+        "host": "{{hdfs-site/dfs.namenode.secondary.http-address}}",
+        "reporting": {
+          "ok": {
+            "text": "System CPU Load is OK"
+          },
+          "warning": {
+            "text": "System CPU Load is Nearing Critical",
+            "value": 70
+          },          
+          "critical": {
+            "text": "System CPU Load is Critical",
+            "value": 80
+          }
+        }
       }
     },
     {
@@ -31,8 +31,17 @@
       "scope": "host",
       "source": {
         "type": "PORT",
-        "uri": "{{hdfs-site/dfs.namenode.http-address}}:50070"
-       }
+        "uri": "{{hdfs-site/dfs.namenode.http-address}}",
+        "port": 50070,
+        "reporting": {
+          "ok": {
+            "text": "TCP OK - {0:.4f} response on port {1}"
+          },
+          "critical": {
+            "text": "TCP FAIL - {0:.4f} response on port {1}"
+          }
+        }        
+      }
     },
     {
       "name": "hdfs_last_checkpoint",
@@ -46,6 +55,18 @@
       }
     }
   ],
-  "DATANODE": [
-  ]
-}
+  "SECONDARY_NAMENODE": [
+    {
+      "name": "secondary_namenode_process",
+      "label": "Secondary NameNode process",
+      "interval": 1,
+      "scope": "service",
+      "source": {
+        "type": "PORT",        
+        "uri": "{{hdfs-site/dfs.namenode.secondary.http-address}}",
+        "port": 50070
+      }
+    }
+  ],  
+  "DATANODE": []
+}