You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2014/11/18 22:12:42 UTC

[2/2] ambari git commit: AMBARI-8362 - Alerts: Targets Should Support A Severity Level (jonathanhurley)

AMBARI-8362 - Alerts: Targets Should Support A Severity Level (jonathanhurley)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/7eee111c
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/7eee111c
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/7eee111c

Branch: refs/heads/trunk
Commit: 7eee111cdb767e403aabd00c73d21729d2b3750d
Parents: d531181
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Tue Nov 18 00:28:40 2014 -0500
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Tue Nov 18 15:37:30 2014 -0500

----------------------------------------------------------------------
 .../internal/AlertTargetResourceProvider.java   | 41 +++++++++
 .../listeners/AlertStateChangedListener.java    | 36 +++++++-
 .../server/orm/entities/AlertTargetEntity.java  | 44 ++++++++++
 .../server/upgrade/UpgradeCatalog200.java       | 19 +++--
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |  6 ++
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |  6 ++
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |  6 ++
 .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql     |  7 ++
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |  6 ++
 .../resources/Ambari-DDL-SQLServer-DROP.sql     |  2 +
 .../AlertTargetResourceProviderTest.java        | 85 +++++++++++++++++-
 .../alerts/AlertStateChangedEventTest.java      | 90 +++++++++++++++-----
 .../server/upgrade/UpgradeCatalog200Test.java   | 22 ++++-
 13 files changed, 335 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
index 3029114..1458139 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java
@@ -20,6 +20,8 @@ package org.apache.ambari.server.controller.internal;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -41,6 +43,7 @@ import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
+import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.alert.AlertTarget;
 import org.apache.commons.lang.StringUtils;
 
@@ -63,6 +66,7 @@ public class AlertTargetResourceProvider extends
   protected static final String ALERT_TARGET_NOTIFICATION_TYPE = "AlertTarget/notification_type";
   protected static final String ALERT_TARGET_PROPERTIES = "AlertTarget/properties";
   protected static final String ALERT_TARGET_GROUPS = "AlertTarget/groups";
+  protected static final String ALERT_TARGET_STATES = "AlertTarget/alert_states";
 
   private static final Set<String> PK_PROPERTY_IDS = new HashSet<String>(
       Arrays.asList(ALERT_TARGET_ID, ALERT_TARGET_NAME));
@@ -85,6 +89,7 @@ public class AlertTargetResourceProvider extends
     PROPERTY_IDS.add(ALERT_TARGET_NOTIFICATION_TYPE);
     PROPERTY_IDS.add(ALERT_TARGET_PROPERTIES);
     PROPERTY_IDS.add(ALERT_TARGET_GROUPS);
+    PROPERTY_IDS.add(ALERT_TARGET_STATES);
 
     // keys
     KEY_PROPERTY_IDS.put(Resource.Type.AlertTarget, ALERT_TARGET_ID);
@@ -218,6 +223,7 @@ public class AlertTargetResourceProvider extends
    * @param requestMaps
    * @throws AmbariException
    */
+  @SuppressWarnings("unchecked")
   private void createAlertTargets(Set<Map<String, Object>> requestMaps)
       throws AmbariException {
     List<AlertTargetEntity> entities = new ArrayList<AlertTargetEntity>();
@@ -227,6 +233,7 @@ public class AlertTargetResourceProvider extends
       String name = (String) requestMap.get(ALERT_TARGET_NAME);
       String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION);
       String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE);
+      Collection<String> alertStates = (Collection<String>) requestMap.get(ALERT_TARGET_STATES);
 
       if (StringUtils.isEmpty(name)) {
         throw new IllegalArgumentException(
@@ -244,10 +251,22 @@ public class AlertTargetResourceProvider extends
             "Alert targets must be created with their connection properties");
       }
 
+      // set the states that this alert target cares about
+      final Set<AlertState> alertStateSet;
+      if (null != alertStates) {
+        alertStateSet = new HashSet<AlertState>(alertStates.size());
+        for (String state : alertStates) {
+          alertStateSet.add(AlertState.valueOf(state));
+        }
+      } else {
+        alertStateSet = EnumSet.allOf(AlertState.class);
+      }
+
       entity.setDescription(description);
       entity.setNotificationType(notificationType);
       entity.setProperties(properties);
       entity.setTargetName(name);
+      entity.setAlertStates(alertStateSet);
 
       entities.add(entity);
     }
@@ -263,6 +282,7 @@ public class AlertTargetResourceProvider extends
    * @throws AmbariException
    *           if the entity could not be found.
    */
+  @SuppressWarnings("unchecked")
   private void updateAlertTargets(Set<Map<String, Object>> requestMaps)
       throws AmbariException {
 
@@ -286,6 +306,7 @@ public class AlertTargetResourceProvider extends
       String name = (String) requestMap.get(ALERT_TARGET_NAME);
       String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION);
       String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE);
+      Collection<String> alertStates = (Collection<String>) requestMap.get(ALERT_TARGET_STATES);
 
       if (!StringUtils.isBlank(name)) {
         entity.setTargetName(name);
@@ -304,6 +325,23 @@ public class AlertTargetResourceProvider extends
         entity.setProperties(properties);
       }
 
+      // a null alert state implies that the key was not set and no update
+      // should occur for this field, while an empty list implies all alert
+      // states should be set
+      if (null != alertStates) {
+        final Set<AlertState> alertStateSet;
+        if (alertStates.isEmpty()) {
+          alertStateSet = EnumSet.allOf(AlertState.class);
+        } else {
+          alertStateSet = new HashSet<AlertState>(alertStates.size());
+          for (String state : alertStates) {
+            alertStateSet.add(AlertState.valueOf(state));
+          }
+        }
+
+        entity.setAlertStates(alertStateSet);
+      }
+
       s_dao.merge(entity);
     }
   }
@@ -342,6 +380,9 @@ public class AlertTargetResourceProvider extends
       resource.setProperty(ALERT_TARGET_PROPERTIES, map);
     }
 
+    setResourceProperty(resource, ALERT_TARGET_STATES, entity.getAlertStates(),
+        requestedIds);
+
     return resource;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/AlertStateChangedListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/AlertStateChangedListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/AlertStateChangedListener.java
index c42851b..6924fc0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/AlertStateChangedListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/AlertStateChangedListener.java
@@ -31,6 +31,7 @@ import org.apache.ambari.server.orm.entities.AlertGroupEntity;
 import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
 import org.apache.ambari.server.orm.entities.AlertNoticeEntity;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
+import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.MaintenanceState;
 import org.apache.ambari.server.state.NotificationState;
 import org.apache.commons.logging.Log;
@@ -72,7 +73,12 @@ public class AlertStateChangedListener {
   }
 
   /**
-   * Listens for when an alert's state has changed.
+   * Listens for when an alert's state has changed and creates
+   * {@link AlertNoticeEntity} instances when appropriate to notify
+   * {@link AlertTargetEntity}.
+   * <p/>
+   * {@link AlertNoticeEntity} are only created when the target has the
+   * {@link AlertState} in its list of states.
    */
   @Subscribe
   @AllowConcurrentEvents
@@ -100,6 +106,10 @@ public class AlertStateChangedListener {
       }
 
       for (AlertTargetEntity target : targets) {
+        if (!isAlertTargetInterested(target, history)) {
+          continue;
+        }
+
         AlertNoticeEntity notice = new AlertNoticeEntity();
         notice.setUuid(UUID.randomUUID().toString());
         notice.setAlertTarget(target);
@@ -110,4 +120,28 @@ public class AlertStateChangedListener {
       }
     }
   }
+
+  /**
+   * Gets whether the {@link AlertTargetEntity} is interested in receiving a
+   * notification about the {@link AlertHistoryEntity}'s state change.
+   *
+   * @param target
+   *          the target (not {@code null}).
+   * @param history
+   *          the history entry that represents the state change (not
+   *          {@code null}).
+   * @return {@code true} if the target cares about this state change,
+   *         {@code false} otherwise.
+   */
+  private boolean isAlertTargetInterested(AlertTargetEntity target,
+      AlertHistoryEntity history) {
+    Set<AlertState> alertStates = target.getAlertStates();
+    if (null != alertStates && alertStates.size() > 0) {
+      if (!alertStates.contains(history.getAlertState())) {
+        return false;
+      }
+    }
+
+    return true;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
index 12c394d..d18ec68 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertTargetEntity.java
@@ -23,12 +23,17 @@ import java.util.Set;
 
 import javax.persistence.Basic;
 import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
 import javax.persistence.Column;
+import javax.persistence.ElementCollection;
 import javax.persistence.Entity;
 import javax.persistence.EntityManager;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.JoinColumn;
 import javax.persistence.Lob;
 import javax.persistence.ManyToMany;
 import javax.persistence.NamedQueries;
@@ -37,6 +42,8 @@ import javax.persistence.PreRemove;
 import javax.persistence.Table;
 import javax.persistence.TableGenerator;
 
+import org.apache.ambari.server.state.AlertState;
+
 /**
  * The {@link AlertTargetEntity} class represents audience that will receive
  * dispatches when an alert is triggered.
@@ -77,6 +84,18 @@ public class AlertTargetEntity {
   private Set<AlertGroupEntity> alertGroups;
 
   /**
+   * Gets the alert states that this target will be notified for. If this is
+   * either empty or {@code null}, then it is implied that all alert states are
+   * of interest to the target. A target without an alert states does not make
+   * sense which is why the absence of states implies all states.
+   */
+  @Enumerated(value = EnumType.STRING)
+  @ElementCollection(targetClass = AlertState.class)
+  @CollectionTable(name = "alert_target_states", joinColumns = @JoinColumn(name = "target_id"))
+  @Column(name = "alert_state")
+  private Set<AlertState> alertStates;
+
+  /**
    * Gets the unique ID of this alert target.
    *
    * @return the ID of the target (never {@code null}).
@@ -152,6 +171,31 @@ public class AlertTargetEntity {
   }
 
   /**
+   * Gets the alert states that will cause a triggered alert to be sent to this
+   * target. A target may be associated with a group where an alert has changed
+   * state, but if that new state is not of interest to the target, it will not
+   * be sent.
+   *
+   * @return the set of alert states or {@code null} or empty to imply all.
+   */
+  public Set<AlertState> getAlertStates() {
+    return alertStates;
+  }
+
+  /**
+   * Sets the alert states that will cause a triggered alert to be sent to this
+   * target. A target may be associated with a group where an alert has changed
+   * state, but if that new state is not of interest to the target, it will not
+   * be sent.
+   *
+   * @param alertStates
+   *          the set of alert states or {@code null} or empty to imply all.
+   */
+  public void setAlertStates(Set<AlertState> alertStates) {
+    this.alertStates = alertStates;
+  }
+
+  /**
    * Sets the name of this alert target.
    *
    * @param targetName

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
index 2cbf266..a85a5d0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog200.java
@@ -25,7 +25,6 @@ import java.util.List;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.DBAccessor.DBColumnInfo;
-import org.apache.ambari.server.orm.dao.DaoUtils;
 import org.apache.ambari.server.state.UpgradeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -39,10 +38,9 @@ import com.google.inject.Injector;
  */
 public class UpgradeCatalog200 extends AbstractUpgradeCatalog {
 
-  private static final String ALERT_TABLE_DEFINITION = "alert_definition";
-
-  @Inject
-  private DaoUtils daoUtils;
+  private static final String ALERT_DEFINITION_TABLE = "alert_definition";
+  private static final String ALERT_TARGET_TABLE = "alert_target";
+  private static final String ALERT_TARGET_STATES_TABLE = "alert_target_states";
 
   /**
    * {@inheritDoc}
@@ -88,9 +86,18 @@ public class UpgradeCatalog200 extends AbstractUpgradeCatalog {
     prepareRollingUpgradesDDL();
 
     // add ignore_host column to alert_definition
-    dbAccessor.addColumn(ALERT_TABLE_DEFINITION, new DBColumnInfo(
+    dbAccessor.addColumn(ALERT_DEFINITION_TABLE, new DBColumnInfo(
         "ignore_host", Short.class, 1, 0, false));
 
+    // create alert_target_states table
+    ArrayList<DBColumnInfo> columns = new ArrayList<DBColumnInfo>();
+    columns.add(new DBColumnInfo("target_id", Long.class, null, null, false));
+    columns.add(new DBColumnInfo("alert_state", String.class, 255, null, false));
+    dbAccessor.createTable(ALERT_TARGET_STATES_TABLE, columns, "target_id");
+    dbAccessor.addFKConstraint(ALERT_TARGET_STATES_TABLE,
+        "fk_alert_target_states_target_id", "target_id", ALERT_TARGET_TABLE,
+        "target_id", false);
+
     // Alter column : make viewinstanceproperty.value & viewinstancedata.value nullable
     dbAccessor.alterColumn("viewinstanceproperty", new DBColumnInfo("value", String.class, 2000, null, true));
     dbAccessor.alterColumn("viewinstancedata", new DBColumnInfo("value", String.class, 2000, null, true));

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 4d15914..4fa4be1 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -651,6 +651,12 @@ CREATE TABLE alert_target (
   PRIMARY KEY (target_id)
 );
 
+CREATE TABLE alert_target_states (
+  target_id BIGINT NOT NULL,
+  alert_state VARCHAR(255) NOT NULL,
+  FOREIGN KEY (target_id) REFERENCES alert_target(target_id)
+);
+
 CREATE TABLE alert_group_target (
   group_id BIGINT NOT NULL,
   target_id BIGINT NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index e3ae87a..5092332 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -641,6 +641,12 @@ CREATE TABLE alert_target (
   PRIMARY KEY (target_id)
 );
 
+CREATE TABLE alert_target_states (
+  target_id NUMBER(19) NOT NULL,
+  alert_state VARCHAR2(255) NOT NULL,
+  FOREIGN KEY (target_id) REFERENCES alert_target(target_id)
+);
+
 CREATE TABLE alert_group_target (
   group_id NUMBER(19) NOT NULL,
   target_id NUMBER(19) NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 0587232..fb666df 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -638,6 +638,12 @@ CREATE TABLE alert_target (
   PRIMARY KEY (target_id)
 );
 
+CREATE TABLE alert_target_states (
+  target_id BIGINT NOT NULL,
+  alert_state VARCHAR(255) NOT NULL,
+  FOREIGN KEY (target_id) REFERENCES alert_target(target_id)
+);
+
 CREATE TABLE alert_group_target (
   group_id BIGINT NOT NULL,
   target_id BIGINT NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
index 5605d57..bb3fa5f 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
@@ -711,6 +711,12 @@ CREATE TABLE ambari.alert_target (
   PRIMARY KEY (target_id)
 );
 
+CREATE TABLE ambari.alert_target_states (
+  target_id BIGINT NOT NULL,
+  alert_state VARCHAR(255) NOT NULL,
+  FOREIGN KEY (target_id) REFERENCES ambari.alert_target(target_id)
+);
+
 CREATE TABLE ambari.alert_group_target (
   group_id BIGINT NOT NULL,
   target_id BIGINT NOT NULL,
@@ -743,6 +749,7 @@ GRANT ALL PRIVILEGES ON TABLE ambari.alert_history TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.alert_current TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.alert_group TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.alert_target TO :username;
+GRANT ALL PRIVILEGES ON TABLE ambari.alert_target_states TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.alert_group_target TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.alert_grouping TO :username;
 GRANT ALL PRIVILEGES ON TABLE ambari.alert_notice TO :username;

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index 79caca7..2993cbf 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -221,6 +221,12 @@ CREATE TABLE alert_target (
   PRIMARY KEY (target_id)
 );
 
+CREATE TABLE alert_target_states (
+  target_id BIGINT NOT NULL,
+  alert_state VARCHAR(255) NOT NULL,
+  FOREIGN KEY (target_id) REFERENCES alert_target(target_id)
+);
+
 CREATE TABLE alert_group_target (
   group_id BIGINT NOT NULL,
   target_id BIGINT NOT NULL,

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/main/resources/Ambari-DDL-SQLServer-DROP.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-DROP.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-DROP.sql
index 7658b63..8f6b2b4 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-DROP.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-DROP.sql
@@ -166,6 +166,8 @@ IF OBJECT_ID('alert_grouping', 'U') IS NOT NULL DROP TABLE alert_grouping
 GO
 IF OBJECT_ID('alert_group_target', 'U') IS NOT NULL DROP TABLE alert_group_target
 GO
+IF OBJECT_ID('alert_target_states', 'U') IS NOT NULL DROP TABLE alert_target_states
+GO
 IF OBJECT_ID('alert_target', 'U') IS NOT NULL DROP TABLE alert_target
 GO
 IF OBJECT_ID('alert_group', 'U') IS NOT NULL DROP TABLE alert_group

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
index 7a633e7..0d081f8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProviderTest.java
@@ -18,6 +18,8 @@
 package org.apache.ambari.server.controller.internal;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
@@ -28,9 +30,13 @@ import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.resetToStrict;
 import static org.easymock.EasyMock.verify;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -45,6 +51,7 @@ import org.apache.ambari.server.metadata.ActionMetadata;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
+import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.alert.TargetType;
@@ -92,6 +99,7 @@ public class AlertTargetResourceProviderTest {
    * @throws Exception
    */
   @Test
+  @SuppressWarnings("unchecked")
   public void testGetResourcesNoPredicate() throws Exception {
     Request request = PropertyHelper.getReadRequest(
         AlertTargetResourceProvider.ALERT_TARGET_DESCRIPTION,
@@ -115,7 +123,10 @@ public class AlertTargetResourceProviderTest {
     Map<String, String> properties = (Map<String, String>) resource.getPropertyValue(
         AlertTargetResourceProvider.ALERT_TARGET_PROPERTIES);
 
-    Assert.assertNull(properties);
+    Collection<String> alertStates = (Collection<String>) resource.getPropertyValue(AlertTargetResourceProvider.ALERT_TARGET_STATES);
+
+    assertNull(properties);
+    assertNull(alertStates);
 
     verify(m_dao);
   }
@@ -130,7 +141,8 @@ public class AlertTargetResourceProviderTest {
         AlertTargetResourceProvider.ALERT_TARGET_DESCRIPTION,
         AlertTargetResourceProvider.ALERT_TARGET_ID,
         AlertTargetResourceProvider.ALERT_TARGET_NAME,
-        AlertTargetResourceProvider.ALERT_TARGET_NOTIFICATION_TYPE);
+        AlertTargetResourceProvider.ALERT_TARGET_NOTIFICATION_TYPE,
+        AlertTargetResourceProvider.ALERT_TARGET_STATES);
 
     Predicate predicate = new PredicateBuilder().property(
         AlertTargetResourceProvider.ALERT_TARGET_ID).equals(
@@ -152,11 +164,18 @@ public class AlertTargetResourceProviderTest {
     Assert.assertEquals(ALERT_TARGET_NAME,
         resource.getPropertyValue(AlertTargetResourceProvider.ALERT_TARGET_NAME));
 
+    // alert states were requested
+    Collection<String> alertStates = (Collection<String>) resource.getPropertyValue(AlertTargetResourceProvider.ALERT_TARGET_STATES);
+    Assert.assertNotNull(alertStates);
+    Assert.assertEquals(2, alertStates.size());
+    Assert.assertTrue(alertStates.contains(AlertState.CRITICAL));
+    Assert.assertTrue(alertStates.contains(AlertState.WARNING));
+
     // properties were not requested, they should not be included
     Map<String, String> properties = (Map<String, String>) resource.getPropertyValue(
         AlertTargetResourceProvider.ALERT_TARGET_PROPERTIES);
 
-    Assert.assertNull(properties);
+    assertNull(properties);
 
     // ask for all fields
     request = PropertyHelper.getReadRequest();
@@ -201,6 +220,11 @@ public class AlertTargetResourceProviderTest {
     assertEquals(ALERT_TARGET_TYPE, entity.getNotificationType());
     assertEquals(ALERT_TARGET_PROPS, entity.getProperties());
 
+    // no alert states were set explicitely in the request, so all should be set
+    // by the backend
+    assertNotNull(entity.getAlertStates());
+    assertEquals(EnumSet.allOf(AlertState.class), entity.getAlertStates());
+
     verify(m_amc, m_dao);
   }
 
@@ -208,7 +232,7 @@ public class AlertTargetResourceProviderTest {
    * @throws Exception
    */
   @Test
-  public void testCreateWithRecipientArray() throws Exception {
+  public void testCreateResourceWithRecipientArray() throws Exception {
     Capture<List<AlertTargetEntity>> listCapture = new Capture<List<AlertTargetEntity>>();
 
     m_dao.createTargets(capture(listCapture));
@@ -234,6 +258,11 @@ public class AlertTargetResourceProviderTest {
         "{\"ambari.dispatch.recipients\":\"[\\\"ambari@ambari.apache.org\\\"]\"}",
         entity.getProperties());
 
+    // no alert states were set explicitely in the request, so all should be set
+    // by the backend
+    assertNotNull(entity.getAlertStates());
+    assertEquals(EnumSet.allOf(AlertState.class), entity.getAlertStates());
+
     verify(m_amc, m_dao);
   }
 
@@ -242,6 +271,49 @@ public class AlertTargetResourceProviderTest {
    */
   @Test
   @SuppressWarnings("unchecked")
+  public void testCreateResourceWithAlertStates() throws Exception {
+    Capture<List<AlertTargetEntity>> listCapture = new Capture<List<AlertTargetEntity>>();
+
+    m_dao.createTargets(capture(listCapture));
+    expectLastCall();
+
+    replay(m_amc, m_dao);
+
+    AlertTargetResourceProvider provider = createProvider(m_amc);
+    Map<String, Object> requestProps = getCreationProperties();
+    requestProps.put(
+        AlertTargetResourceProvider.ALERT_TARGET_STATES,
+        new ArrayList(Arrays.asList(AlertState.OK.name(),
+            AlertState.UNKNOWN.name())));
+
+    Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
+
+    provider.createResources(request);
+
+    Assert.assertTrue(listCapture.hasCaptured());
+    AlertTargetEntity entity = listCapture.getValue().get(0);
+    Assert.assertNotNull(entity);
+
+    assertEquals(ALERT_TARGET_NAME, entity.getTargetName());
+    assertEquals(ALERT_TARGET_DESC, entity.getDescription());
+    assertEquals(ALERT_TARGET_TYPE, entity.getNotificationType());
+    assertEquals(ALERT_TARGET_PROPS, entity.getProperties());
+
+    Set<AlertState> alertStates = entity.getAlertStates();
+    assertNotNull(alertStates);
+    assertEquals(2, alertStates.size());
+    assertTrue(alertStates.contains(AlertState.OK));
+    assertTrue(alertStates.contains(AlertState.UNKNOWN));
+
+    verify(m_amc, m_dao);
+  }
+
+
+  /**
+   * @throws Exception
+   */
+  @Test
+  @SuppressWarnings("unchecked")
   public void testUpdateResources() throws Exception {
     Capture<AlertTargetEntity> entityCapture = new Capture<AlertTargetEntity>();
 
@@ -344,6 +416,7 @@ public class AlertTargetResourceProviderTest {
   /**
    * @return
    */
+  @SuppressWarnings({ "rawtypes", "unchecked" })
   private List<AlertTargetEntity> getMockEntities() throws Exception {
     AlertTargetEntity entity = new AlertTargetEntity();
     entity.setTargetId(ALERT_TARGET_ID);
@@ -351,6 +424,10 @@ public class AlertTargetResourceProviderTest {
     entity.setTargetName(ALERT_TARGET_NAME);
     entity.setNotificationType(ALERT_TARGET_TYPE);
     entity.setProperties(ALERT_TARGET_PROPS);
+
+    entity.setAlertStates(new HashSet(Arrays.asList(AlertState.CRITICAL,
+        AlertState.WARNING)));
+
     return Arrays.asList(entity);
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertStateChangedEventTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertStateChangedEventTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertStateChangedEventTest.java
index 18b4123..ad31fff 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertStateChangedEventTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertStateChangedEventTest.java
@@ -19,6 +19,7 @@ package org.apache.ambari.server.state.alerts;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -35,6 +36,7 @@ import org.apache.ambari.server.orm.entities.AlertGroupEntity;
 import org.apache.ambari.server.orm.entities.AlertHistoryEntity;
 import org.apache.ambari.server.orm.entities.AlertNoticeEntity;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
+import org.apache.ambari.server.state.AlertState;
 import org.easymock.EasyMock;
 import org.junit.After;
 import org.junit.Before;
@@ -101,8 +103,30 @@ public class AlertStateChangedEventTest {
    */
   @Test
   public void testAlertNoticeCreationFromEvent() throws Exception {
+    AlertTargetEntity alertTarget = EasyMock.createMock(AlertTargetEntity.class);
+    AlertGroupEntity alertGroup = EasyMock.createMock(AlertGroupEntity.class);
+    List<AlertGroupEntity> groups = new ArrayList<AlertGroupEntity>();
+    Set<AlertTargetEntity> targets = new HashSet<AlertTargetEntity>();
+
+    targets.add(alertTarget);
+    groups.add(alertGroup);
+
+    EasyMock.expect(alertGroup.getAlertTargets()).andReturn(targets).once();
+    EasyMock.expect(alertTarget.getAlertStates()).andReturn(
+        EnumSet.of(AlertState.OK, AlertState.CRITICAL)).atLeastOnce();
+
+    EasyMock.expect(
+        dispatchDao.findGroupsByDefinition(EasyMock.anyObject(AlertDefinitionEntity.class))).andReturn(
+        groups).once();
+
+    dispatchDao.create(EasyMock.anyObject(AlertNoticeEntity.class));
+    EasyMock.expectLastCall().once();
+
+    EasyMock.replay(alertTarget, alertGroup, dispatchDao);
+
     AlertHistoryEntity history = EasyMock.createNiceMock(AlertHistoryEntity.class);
     AlertStateChangeEvent event = EasyMock.createNiceMock(AlertStateChangeEvent.class);
+    EasyMock.expect(history.getAlertState()).andReturn(AlertState.CRITICAL).atLeastOnce();
     EasyMock.expect(event.getNewHistoricalEntry()).andReturn(history).atLeastOnce();
 
     EasyMock.replay(history, event);
@@ -113,35 +137,59 @@ public class AlertStateChangedEventTest {
   }
 
   /**
+   * Tests that an {@link AlertNoticeEntity} is not created for a target that
+   * does not match the {@link AlertState} of the alert.
    *
+   * @throws Exception
    */
-  private class MockModule implements Module {
-    /**
-    *
-    */
-    @Override
-    public void configure(Binder binder) {
-      AlertTargetEntity alertTarget = EasyMock.createMock(AlertTargetEntity.class);
-      AlertGroupEntity alertGroup = EasyMock.createMock(AlertGroupEntity.class);
-      List<AlertGroupEntity> groups = new ArrayList<AlertGroupEntity>();
-      Set<AlertTargetEntity> targets = new HashSet<AlertTargetEntity>();
+  @Test
+  public void testAlertNoticeSkippedForTarget() throws Exception {
+    AlertTargetEntity alertTarget = EasyMock.createMock(AlertTargetEntity.class);
+    AlertGroupEntity alertGroup = EasyMock.createMock(AlertGroupEntity.class);
+    List<AlertGroupEntity> groups = new ArrayList<AlertGroupEntity>();
+    Set<AlertTargetEntity> targets = new HashSet<AlertTargetEntity>();
 
-      targets.add(alertTarget);
-      groups.add(alertGroup);
+    targets.add(alertTarget);
+    groups.add(alertGroup);
 
-      EasyMock.expect(alertGroup.getAlertTargets()).andReturn(targets).once();
+    EasyMock.expect(alertGroup.getAlertTargets()).andReturn(targets).once();
+    EasyMock.expect(alertTarget.getAlertStates()).andReturn(
+        EnumSet.of(AlertState.OK, AlertState.CRITICAL)).atLeastOnce();
 
-      AlertDispatchDAO dispatchDao = EasyMock.createMock(AlertDispatchDAO.class);
-      EasyMock.expect(
-          dispatchDao.findGroupsByDefinition(EasyMock.anyObject(AlertDefinitionEntity.class))).andReturn(
-          groups).once();
+    EasyMock.expect(
+        dispatchDao.findGroupsByDefinition(EasyMock.anyObject(AlertDefinitionEntity.class))).andReturn(
+        groups).once();
 
-      dispatchDao.create(EasyMock.anyObject(AlertNoticeEntity.class));
-      EasyMock.expectLastCall().once();
+    // dispatchDao should be strict enough to throw an exception on verify
+    // that the create alert notice method was not called
+    EasyMock.replay(alertTarget, alertGroup, dispatchDao);
 
-      binder.bind(AlertDispatchDAO.class).toInstance(dispatchDao);
+    AlertHistoryEntity history = EasyMock.createNiceMock(AlertHistoryEntity.class);
+    AlertStateChangeEvent event = EasyMock.createNiceMock(AlertStateChangeEvent.class);
+
+    // use WARNING to ensure that the target (which only cares about OK/CRIT)
+    // does not receive the alert notice
+    EasyMock.expect(history.getAlertState()).andReturn(AlertState.WARNING).atLeastOnce();
+    EasyMock.expect(event.getNewHistoricalEntry()).andReturn(history).atLeastOnce();
+
+    EasyMock.replay(history, event);
 
-      EasyMock.replay(alertTarget, alertGroup, dispatchDao);
+    // async publishing
+    eventPublisher.publish(event);
+    EasyMock.verify(dispatchDao, history, event);
+  }
+
+  /**
+   *
+   */
+  private class MockModule implements Module {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void configure(Binder binder) {
+      AlertDispatchDAO dispatchDao = EasyMock.createMock(AlertDispatchDAO.class);
+      binder.bind(AlertDispatchDAO.class).toInstance(dispatchDao);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/7eee111c/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
index be97222..64bdbca 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog200Test.java
@@ -18,9 +18,6 @@
 
 package org.apache.ambari.server.upgrade;
 
-import java.sql.SQLException;
-import java.util.List;
-
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
@@ -36,6 +33,8 @@ import static org.easymock.EasyMock.verify;
 
 import java.lang.reflect.Field;
 import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
 
 import javax.persistence.EntityManager;
 
@@ -94,6 +93,7 @@ public class UpgradeCatalog200Test {
     Capture<List<DBAccessor.DBColumnInfo>> hostVersionCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
     Capture<DBAccessor.DBColumnInfo> valueColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
     Capture<DBAccessor.DBColumnInfo> dataValueColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
+    Capture<List<DBAccessor.DBColumnInfo>> alertTargetStatesCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
 
     Capture<List<DBAccessor.DBColumnInfo>> upgradeCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
     Capture<List<DBAccessor.DBColumnInfo>> upgradeItemCapture = new Capture<List<DBAccessor.DBColumnInfo>>();
@@ -102,6 +102,9 @@ public class UpgradeCatalog200Test {
     dbAccessor.addColumn(eq("alert_definition"),
         capture(alertDefinitionIgnoreColumnCapture));
 
+    dbAccessor.createTable(eq("alert_target_states"),
+        capture(alertTargetStatesCapture), eq("target_id"));
+
     // Host Component State
     dbAccessor.addColumn(eq("hostcomponentstate"),
         capture(hostComponentStateColumnCapture));
@@ -137,6 +140,9 @@ public class UpgradeCatalog200Test {
     // verify ignore column for alert_definition
     verifyAlertDefinitionIgnoreColumn(alertDefinitionIgnoreColumnCapture);
 
+    // verify new table for alert target states
+    verifyAlertTargetStatesTable(alertTargetStatesCapture);
+
     // Verify added column in hostcomponentstate table
     DBAccessor.DBColumnInfo upgradeStateColumn = hostComponentStateColumnCapture.getValue();
     assertEquals("upgrade_state", upgradeStateColumn.getName());
@@ -190,6 +196,16 @@ public class UpgradeCatalog200Test {
     Assert.assertEquals("ignore_host", column.getName());
   }
 
+  /**
+   * Verifies alert_target_states table.
+   *
+   * @param alertTargetStatesColumnCapture
+   */
+  private void verifyAlertTargetStatesTable(
+      Capture<List<DBAccessor.DBColumnInfo>> alertTargetStatesCapture) {
+    Assert.assertEquals(2, alertTargetStatesCapture.getValue().size());
+  }
+
   @Test
   public void testGetSourceVersion() {
     final DBAccessor dbAccessor = createNiceMock(DBAccessor.class);