Przeglądaj źródła

AMBARI-8978 - Alerts: Allow Ability To Test An AlertTarget Before Creating It (Yurii Shylov via jonathanhurley)

Jonathan Hurley 10 lat temu
rodzic
commit
275e8986b1

+ 10 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/AlertTargetResourceDefinition.java

@@ -18,6 +18,7 @@
 package org.apache.ambari.server.api.resources;
 
 import org.apache.ambari.server.controller.spi.Resource;
+import java.util.Collection;
 
 /**
  * The {@link AlertTargetResourceDefinition} class is used to register alert
@@ -25,6 +26,8 @@ import org.apache.ambari.server.controller.spi.Resource;
  */
 public class AlertTargetResourceDefinition extends BaseResourceDefinition {
 
+  public static final String VALIDATE_CONFIG_DIRECTIVE = "validate_config";
+
   /**
    * Constructor.
    */
@@ -47,4 +50,11 @@ public class AlertTargetResourceDefinition extends BaseResourceDefinition {
   public String getSingularName() {
     return "alert_target";
   }
+
+  @Override
+  public Collection<String> getCreateDirectives() {
+    Collection<String> directives = super.getCreateDirectives();
+    directives.add(VALIDATE_CONFIG_DIRECTIVE);
+    return directives;
+  }
 }

+ 41 - 11
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java

@@ -31,6 +31,7 @@ import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StaticallyInject;
+import org.apache.ambari.server.api.resources.AlertTargetResourceDefinition;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -41,6 +42,8 @@ import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.notifications.DispatchFactory;
+import org.apache.ambari.server.notifications.NotificationDispatcher;
 import org.apache.ambari.server.orm.dao.AlertDispatchDAO;
 import org.apache.ambari.server.orm.entities.AlertGroupEntity;
 import org.apache.ambari.server.orm.entities.AlertTargetEntity;
@@ -105,8 +108,11 @@ public class AlertTargetResourceProvider extends
   @Inject
   private static AlertDispatchDAO s_dao;
 
+  @Inject
+  private static DispatchFactory dispatchFactory;
+
   /**
-   * Used for serializationa and deserialization of some fields.
+   * Used for serialization and deserialization of some fields.
    */
   private static final Gson s_gson = new Gson();
 
@@ -126,7 +132,7 @@ public class AlertTargetResourceProvider extends
     createResources(new Command<Void>() {
       @Override
       public Void invoke() throws AmbariException {
-        createAlertTargets(request.getProperties());
+        createAlertTargets(request.getProperties(), request.getRequestInfoProperties());
         return null;
       }
     });
@@ -225,10 +231,11 @@ public class AlertTargetResourceProvider extends
    * Create and persist {@link AlertTargetEntity} from the map of properties.
    *
    * @param requestMaps
+   * @param requestInfoProps
    * @throws AmbariException
    */
   @SuppressWarnings("unchecked")
-  private void createAlertTargets(Set<Map<String, Object>> requestMaps)
+  private void createAlertTargets(Set<Map<String, Object>> requestMaps, Map<String, String> requestInfoProps)
       throws AmbariException {
     List<AlertTargetEntity> entities = new ArrayList<AlertTargetEntity>();
     for (Map<String, Object> requestMap : requestMaps) {
@@ -250,12 +257,19 @@ public class AlertTargetResourceProvider extends
             "The type of the alert target is required.");
       }
 
-      String properties = extractProperties(requestMap);
-      if (StringUtils.isEmpty(properties)) {
+      Map<String, String> properties = extractProperties(requestMap);
+
+      String propertiesJson = s_gson.toJson(properties);
+      if (StringUtils.isEmpty(propertiesJson)) {
         throw new IllegalArgumentException(
             "Alert targets must be created with their connection properties");
       }
 
+      String validationProperty =  requestInfoProps.get(AlertTargetResourceDefinition.VALIDATE_CONFIG_DIRECTIVE);
+      if (validationProperty != null && validationProperty.equalsIgnoreCase("true")) {
+        validateTargetConfig(notificationType, properties);
+      }
+
       // global not required
       boolean isGlobal = false;
       if (null != globalProperty) {
@@ -286,7 +300,7 @@ public class AlertTargetResourceProvider extends
 
       entity.setDescription(description);
       entity.setNotificationType(notificationType);
-      entity.setProperties(properties);
+      entity.setProperties(propertiesJson);
       entity.setTargetName(name);
       entity.setAlertStates(alertStateSet);
       entity.setGlobal(isGlobal);
@@ -344,7 +358,7 @@ public class AlertTargetResourceProvider extends
         entity.setNotificationType(notificationType);
       }
 
-      String properties = extractProperties(requestMap);
+      String properties = s_gson.toJson(extractProperties(requestMap));
       if (!StringUtils.isEmpty(properties)) {
         entity.setProperties(properties);
       }
@@ -448,8 +462,8 @@ public class AlertTargetResourceProvider extends
    * @return the JSON representing the key/value pairs of all properties, or
    *         {@code null} if none.
    */
-  private String extractProperties( Map<String, Object> requestMap ){
-    Map<String, Object> normalizedMap = new HashMap<String, Object>(
+  private Map<String, String> extractProperties(Map<String, Object> requestMap) {
+    Map<String, String> normalizedMap = new HashMap<String, String>(
         requestMap.size());
 
     for (Entry<String, Object> entry : requestMap.entrySet()) {
@@ -458,10 +472,26 @@ public class AlertTargetResourceProvider extends
 
       if (propCat.equals(ALERT_TARGET_PROPERTIES)) {
         String propKey = PropertyHelper.getPropertyName(key);
-        normalizedMap.put(propKey, entry.getValue());
+        normalizedMap.put(propKey, entry.getValue().toString());
       }
     }
 
-    return s_gson.toJson(normalizedMap);
+    return normalizedMap;
+  }
+
+  /**
+   * Finds dispatcher for given notification type and validates on it given alert target configuration properties.
+   * @param notificationType type of dispatcher
+   * @param properties alert target configuration properties
+   */
+  private void validateTargetConfig(String notificationType, Map<String, String> properties) {
+    NotificationDispatcher dispatcher = dispatchFactory.getDispatcher(notificationType);
+    if (dispatcher == null) {
+      throw new IllegalArgumentException("Dispatcher for given notification type doesn't exist");
+    }
+    NotificationDispatcher.ConfigValidationResult validationResult = dispatcher.validateTargetConfig(properties);
+    if (validationResult.getStatus() == NotificationDispatcher.ConfigValidationResult.Status.INVALID) {
+      throw new IllegalArgumentException(validationResult.getMessage());
+    }
   }
 }

+ 39 - 0
ambari-server/src/main/java/org/apache/ambari/server/notifications/NotificationDispatcher.java

@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.notifications;
 
+import java.util.Map;
 
 /**
  * The {@link NotificationDispatcher} interface represents a mechanism for dispatching a
@@ -52,4 +53,42 @@ public interface NotificationDispatcher {
    * @return {@code true} if digest is supported, {@code false} otherwise.
    */
   public boolean isDigestSupported();
+
+  /**
+   * Validates alert target configuration. Checks if it can dispatch notifications with given properties set.
+   * @param properties alert target properties
+   * @return ConfigValidationResult with validation status and message
+   */
+  public ConfigValidationResult validateTargetConfig(Map<String, String> properties);
+
+  public static class ConfigValidationResult {
+
+    public enum Status {
+      VALID, INVALID
+    }
+
+    private String message;
+    private Status status;
+
+    private ConfigValidationResult(Status status, String message) {
+      this.message = message;
+      this.status = status;
+    }
+
+    public String getMessage() {
+      return message;
+    }
+
+    public Status getStatus() {
+      return status;
+    }
+
+    public static ConfigValidationResult valid() {
+      return new ConfigValidationResult(Status.VALID, "Configuration is valid");
+    }
+
+    public static ConfigValidationResult invalid(String message) {
+      return new ConfigValidationResult(Status.INVALID, message);
+    }
+  }
 }

+ 44 - 3
ambari-server/src/main/java/org/apache/ambari/server/notifications/dispatchers/EmailDispatcher.java

@@ -17,13 +17,16 @@
  */
 package org.apache.ambari.server.notifications.dispatchers;
 
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Timer;
 
+import javax.mail.AuthenticationFailedException;
 import javax.mail.Authenticator;
 import javax.mail.Message.RecipientType;
 import javax.mail.MessagingException;
+import javax.mail.NoSuchProviderException;
 import javax.mail.PasswordAuthentication;
 import javax.mail.Session;
 import javax.mail.Transport;
@@ -35,6 +38,7 @@ import org.apache.ambari.server.notifications.Notification;
 import org.apache.ambari.server.notifications.NotificationDispatcher;
 import org.apache.ambari.server.notifications.Recipient;
 import org.apache.ambari.server.state.alert.TargetType;
+import org.apache.ambari.server.state.services.AlertNoticeDispatchService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -158,11 +162,45 @@ public class EmailDispatcher implements NotificationDispatcher {
     return true;
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ConfigValidationResult validateTargetConfig(Map<String, String> properties) {
+    try {
+      Transport transport = getMailTransport(properties);
+      transport.connect();
+      transport.close();
+    } catch(AuthenticationFailedException e) {
+      LOG.debug("Invalid credentials. Authentication failure.", e);
+      return ConfigValidationResult.invalid("Invalid credentials. Authentication failure: " + e.getMessage());
+    } catch(MessagingException e) {
+      LOG.debug("Invalid config.", e);
+      return ConfigValidationResult.invalid("Invalid config: " + e.getMessage());
+    }
+    return ConfigValidationResult.valid();
+  }
+
+  protected Transport getMailTransport(Map<String, String> properties) throws NoSuchProviderException {
+    DispatchCredentials credentials = null;
+    if (properties.containsKey(AlertNoticeDispatchService.AMBARI_DISPATCH_CREDENTIAL_USERNAME)) {
+      credentials = new DispatchCredentials();
+      credentials.UserName = properties.get(AlertNoticeDispatchService.AMBARI_DISPATCH_CREDENTIAL_USERNAME);
+      credentials.Password = properties.get(AlertNoticeDispatchService.AMBARI_DISPATCH_CREDENTIAL_PASSWORD);
+    }
+    Properties props = new Properties();
+    for (Entry<String, String> entry : properties.entrySet()) {
+      props.put(entry.getKey(), entry.getValue());
+    }
+    Session session = Session.getInstance(props, new EmailAuthenticator(credentials));
+    return session.getTransport();
+  }
+
   /**
    * The {@link EmailAuthenticator} class is used to provide a username and
    * password combination to an SMTP server.
    */
-  private static final class EmailAuthenticator extends Authenticator{
+  private static final class EmailAuthenticator extends Authenticator {
 
     private final DispatchCredentials m_credentials;
 
@@ -180,8 +218,11 @@ public class EmailDispatcher implements NotificationDispatcher {
      */
     @Override
     protected PasswordAuthentication getPasswordAuthentication() {
-      return new PasswordAuthentication(m_credentials.UserName,
-          m_credentials.Password);
+      if (m_credentials != null) {
+        return new PasswordAuthentication(m_credentials.UserName,
+            m_credentials.Password);
+      }
+      return null;
     }
   }
 }

+ 55 - 21
ambari-server/src/main/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcher.java

@@ -49,6 +49,8 @@ import org.snmp4j.smi.VariableBinding;
 import org.snmp4j.transport.DefaultUdpTransportMapping;
 import org.snmp4j.util.DefaultPDUFactory;
 
+import java.util.Map;
+
 import com.google.inject.Singleton;
 
 /**
@@ -105,7 +107,7 @@ public class SNMPDispatcher implements NotificationDispatcher {
     LOG.info("Sending SNMP trap: {}", notification.Subject);
     try {
       snmp = new Snmp(new DefaultUdpTransportMapping());
-      SnmpVersion snmpVersion = getSnmpVersion(notification);
+      SnmpVersion snmpVersion = getSnmpVersion(notification.DispatchProperties);
       sendTraps(notification, snmpVersion);
       successCallback(notification);
     } catch (InvalidSnmpConfigurationException ex) {
@@ -117,6 +119,38 @@ public class SNMPDispatcher implements NotificationDispatcher {
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public ConfigValidationResult validateTargetConfig(Map<String, String> properties) {
+    try {
+      getDispatchProperty(properties, BODY_OID_PROPERTY);
+      getDispatchProperty(properties, SUBJECT_OID_PROPERTY);
+      getDispatchProperty(properties, TRAP_OID_PROPERTY);
+      getDispatchProperty(properties, PORT_PROPERTY);
+      SnmpVersion snmpVersion = getSnmpVersion(properties);
+      switch (snmpVersion) {
+        case SNMPv3:
+          getDispatchProperty(properties, SECURITY_USERNAME_PROPERTY);
+          TrapSecurity securityLevel = getSecurityLevel(properties);
+          switch (securityLevel) {
+            case AUTH_PRIV:
+              getDispatchProperty(properties, SECURITY_PRIV_PASSPHRASE_PROPERTY);
+            case AUTH_NOPRIV:
+              getDispatchProperty(properties, SECURITY_AUTH_PASSPHRASE_PROPERTY);
+          }
+          break;
+        case SNMPv2c:
+        case SNMPv1:
+          getDispatchProperty(properties, COMMUNITY_PROPERTY);
+      }
+    } catch (InvalidSnmpConfigurationException ex) {
+      return ConfigValidationResult.invalid(ex.getMessage());
+    }
+    return ConfigValidationResult.valid();
+  }
+
   /**
    * Creates protocol data unit (PDU) with corresponding SNMP version for alert notification.
    * @param notification alert notification to dispatch
@@ -128,10 +162,10 @@ public class SNMPDispatcher implements NotificationDispatcher {
     PDU pdu = DefaultPDUFactory.createPDU(snmpVersion.getTargetVersion());
     pdu.setType(snmpVersion.getTrapType());
     // Set trap oid for PDU
-    pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OID(getDispatchProperty(notification, TRAP_OID_PROPERTY))));
+    pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID, new OID(getDispatchProperty(notification.DispatchProperties, TRAP_OID_PROPERTY))));
     // Set notification body and subject for PDU objects with identifiers specified in dispatch properties.
-    pdu.add(new VariableBinding(new OID(getDispatchProperty(notification, BODY_OID_PROPERTY)), new OctetString(notification.Body)));
-    pdu.add(new VariableBinding(new OID(getDispatchProperty(notification, SUBJECT_OID_PROPERTY)), new OctetString(notification.Subject)));
+    pdu.add(new VariableBinding(new OID(getDispatchProperty(notification.DispatchProperties, BODY_OID_PROPERTY)), new OctetString(notification.Body)));
+    pdu.add(new VariableBinding(new OID(getDispatchProperty(notification.DispatchProperties, SUBJECT_OID_PROPERTY)), new OctetString(notification.Subject)));
     return pdu;
   }
 
@@ -144,7 +178,7 @@ public class SNMPDispatcher implements NotificationDispatcher {
    */
   protected void sendTraps(Notification notification, SnmpVersion snmpVersion) throws InvalidSnmpConfigurationException, IOException {
     PDU trap = prepareTrap(notification, snmpVersion);
-    String udpPort = getDispatchProperty(notification, PORT_PROPERTY);
+    String udpPort = getDispatchProperty(notification.DispatchProperties, PORT_PROPERTY);
     for (Recipient recipient : getNotificationRecipients(notification)) {
       String address = recipient.Identifier;
       Target target = createTrapTarget(notification, snmpVersion);
@@ -162,13 +196,13 @@ public class SNMPDispatcher implements NotificationDispatcher {
    */
   protected Target createTrapTarget(Notification notification, SnmpVersion snmpVersion) throws InvalidSnmpConfigurationException {
     if (snmpVersion.isCommunityTargetRequired()) {
-      OctetString community = new OctetString(getDispatchProperty(notification, COMMUNITY_PROPERTY));
+      OctetString community = new OctetString(getDispatchProperty(notification.DispatchProperties, COMMUNITY_PROPERTY));
       CommunityTarget communityTarget = new CommunityTarget();
       communityTarget.setCommunity(community);
       communityTarget.setVersion(snmpVersion.getTargetVersion());
       return communityTarget;
     } else {
-      OctetString userName = new OctetString(getDispatchProperty(notification, SECURITY_USERNAME_PROPERTY));
+      OctetString userName = new OctetString(getDispatchProperty(notification.DispatchProperties, SECURITY_USERNAME_PROPERTY));
       if (snmp.getUSM() == null) {
         // provide User-based Security Model (USM) with user specified
         USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
@@ -183,7 +217,7 @@ public class SNMPDispatcher implements NotificationDispatcher {
       }
       UserTarget userTarget = new UserTarget();
       userTarget.setSecurityName(userName);
-      userTarget.setSecurityLevel(getSecurityLevel(notification).getSecurityLevel());
+      userTarget.setSecurityLevel(getSecurityLevel(notification.DispatchProperties).getSecurityLevel());
       userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM);
       userTarget.setVersion(snmpVersion.getTargetVersion());
       return userTarget;
@@ -283,27 +317,27 @@ public class SNMPDispatcher implements NotificationDispatcher {
   }
 
   /**
-   * Get dispatch property with specific key from notification.
-   * @param notification alerts notification
+   * Get dispatch property with specific key from dispatch properties.
+   * @param dispatchProperties dispatch properties
    * @param key property key
    * @return property value
    * @throws InvalidSnmpConfigurationException if property with such key does not exist
    */
-  private static String getDispatchProperty(Notification notification, String key) throws InvalidSnmpConfigurationException {
-    if (notification.DispatchProperties == null || !notification.DispatchProperties.containsKey(key)) {
+  private static String getDispatchProperty(Map<String, String> dispatchProperties, String key) throws InvalidSnmpConfigurationException {
+    if (dispatchProperties == null || !dispatchProperties.containsKey(key)) {
       throw new InvalidSnmpConfigurationException(String.format("Property \"%s\" should be set.", key));
     }
-    return notification.DispatchProperties.get(key);
+    return dispatchProperties.get(key);
   }
 
   /**
-   * Returns {@link SnmpVersion} instance corresponding to dispatch property <code>ambari.dispatch.snmp.version</code> from notification.
-   * @param notification alerts notification
+   * Returns {@link SnmpVersion} instance corresponding to dispatch property <code>ambari.dispatch.snmp.version</code> from dispatch properties.
+   * @param dispatchProperties dispatch properties
    * @return corresponding SnmpVersion instance
    * @throws InvalidSnmpConfigurationException if dispatch properties doesn't contain required property
    */
-  private SnmpVersion getSnmpVersion(Notification notification) throws InvalidSnmpConfigurationException {
-    String snmpVersion = getDispatchProperty(notification, SNMP_VERSION_PROPERTY);
+  private SnmpVersion getSnmpVersion(Map<String, String> dispatchProperties) throws InvalidSnmpConfigurationException {
+    String snmpVersion = getDispatchProperty(dispatchProperties, SNMP_VERSION_PROPERTY);
     try {
       return SnmpVersion.valueOf(snmpVersion);
     } catch (IllegalArgumentException ex) {
@@ -314,13 +348,13 @@ public class SNMPDispatcher implements NotificationDispatcher {
   }
 
   /**
-   * Returns {@link TrapSecurity} instance corresponding to dispatch property <code>ambari.dispatch.snmp.security.level</code> from notification.
-   * @param notification alerts notification
+   * Returns {@link TrapSecurity} instance corresponding to dispatch property <code>ambari.dispatch.snmp.security.level</code> from dispatch properties.
+   * @param dispatchProperties dispatch properties
    * @return corresponding TrapSecurity instance
    * @throws InvalidSnmpConfigurationException if dispatch properties doesn't contain required property
    */
-  private TrapSecurity getSecurityLevel(Notification notification) throws InvalidSnmpConfigurationException {
-    String securityLevel = getDispatchProperty(notification, SECURITY_LEVEL_PROPERTY);
+  private TrapSecurity getSecurityLevel(Map<String, String> dispatchProperties) throws InvalidSnmpConfigurationException {
+    String securityLevel = getDispatchProperty(dispatchProperties, SECURITY_LEVEL_PROPERTY);
     try {
       return TrapSecurity.valueOf(securityLevel);
     } catch (IllegalArgumentException ex) {

+ 2 - 2
ambari-server/src/main/java/org/apache/ambari/server/state/services/AlertNoticeDispatchService.java

@@ -117,12 +117,12 @@ public class AlertNoticeDispatchService extends AbstractScheduledService {
   /**
    * The property containing the dispatch authentication username.
    */
-  private static final String AMBARI_DISPATCH_CREDENTIAL_USERNAME = "ambari.dispatch.credential.username";
+  public static final String AMBARI_DISPATCH_CREDENTIAL_USERNAME = "ambari.dispatch.credential.username";
 
   /**
    * The property containing the dispatch authentication password.
    */
-  private static final String AMBARI_DISPATCH_CREDENTIAL_PASSWORD = "ambari.dispatch.credential.password";
+  public static final String AMBARI_DISPATCH_CREDENTIAL_PASSWORD = "ambari.dispatch.credential.password";
 
   /**
    * The property containing the dispatch recipients

+ 7 - 0
ambari-server/src/test/java/org/apache/ambari/server/notifications/MockDispatcher.java

@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.notifications;
 
+import java.util.Map;
+
 /**
  *
  */
@@ -51,4 +53,9 @@ public class MockDispatcher implements NotificationDispatcher {
   @Override
   public void dispatch(Notification notification) {
   }
+
+  @Override
+  public ConfigValidationResult validateTargetConfig(Map<String, String> properties) {
+    return null;
+  }
 }

+ 62 - 2
ambari-server/src/test/java/org/apache/ambari/server/notifications/EmailDispatcherTest.java → ambari-server/src/test/java/org/apache/ambari/server/notifications/dispatchers/EmailDispatcherTest.java

@@ -15,15 +15,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ambari.server.notifications;
+package org.apache.ambari.server.notifications.dispatchers;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.UUID;
 
+import org.apache.ambari.server.notifications.*;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.alert.TargetType;
 import org.easymock.EasyMock;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -33,6 +37,10 @@ import com.google.inject.Injector;
 import com.google.inject.Module;
 import com.google.inject.util.Modules;
 
+import javax.mail.AuthenticationFailedException;
+import javax.mail.MessagingException;
+import javax.mail.Transport;
+
 /**
  *
  */
@@ -77,7 +85,7 @@ public class EmailDispatcherTest {
    * Tests that an email without properties causes a callback error.
    */
   @Test
-  public void testNoEmailProperties() {
+  public void testNoEmailPropeties() {
     Notification notification = new Notification();
     DispatchCallback callback = EasyMock.createMock(DispatchCallback.class);
     notification.Callback = callback;
@@ -103,6 +111,58 @@ public class EmailDispatcherTest {
     EasyMock.verify(callback);
   }
 
+  @Test
+  public void testValidateTargetConfig_invalidOnAuthenticationException() throws Exception {
+
+    Map<String, String> properties = new HashMap<String, String>();
+    Transport mockedTransport = EasyMock.createNiceMock(Transport.class);
+    EmailDispatcher dispatcher = EasyMock.createMockBuilder(EmailDispatcher.class).
+        addMockedMethods("getMailTransport").createNiceMock();
+
+    EasyMock.expect(dispatcher.getMailTransport(properties)).andReturn(mockedTransport);
+    mockedTransport.connect();
+    EasyMock.expectLastCall().andThrow(new AuthenticationFailedException());
+
+    EasyMock.replay(dispatcher, mockedTransport);
+
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    Assert.assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateTargetConfig_invalidOnMessagingException() throws Exception {
+
+    Map<String, String> properties = new HashMap<String, String>();
+    Transport mockedTransport = EasyMock.createNiceMock(Transport.class);
+    EmailDispatcher dispatcher = EasyMock.createMockBuilder(EmailDispatcher.class).
+        addMockedMethods("getMailTransport").createNiceMock();
+
+    EasyMock.expect(dispatcher.getMailTransport(properties)).andReturn(mockedTransport);
+    mockedTransport.connect();
+    EasyMock.expectLastCall().andThrow(new MessagingException());
+
+    EasyMock.replay(dispatcher, mockedTransport);
+
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    Assert.assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateTargetConfig_validIfNoErrors() throws Exception {
+
+    Map<String, String> properties = new HashMap<String, String>();
+    Transport mockedTransport = EasyMock.createNiceMock(Transport.class);
+    EmailDispatcher dispatcher = EasyMock.createMockBuilder(EmailDispatcher.class).
+        addMockedMethods("getMailTransport").createNiceMock();
+
+    EasyMock.expect(dispatcher.getMailTransport(properties)).andReturn(mockedTransport);
+
+    EasyMock.replay(dispatcher, mockedTransport);
+
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    Assert.assertEquals(NotificationDispatcher.ConfigValidationResult.Status.VALID, configValidationResult.getStatus());
+  }
+
   /**
    *
    */

+ 180 - 0
ambari-server/src/test/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcherTest.java

@@ -19,6 +19,7 @@ package org.apache.ambari.server.notifications.dispatchers;
 
 import org.apache.ambari.server.notifications.DispatchCallback;
 import org.apache.ambari.server.notifications.Notification;
+import org.apache.ambari.server.notifications.NotificationDispatcher;
 import org.apache.ambari.server.notifications.Recipient;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -367,4 +368,183 @@ public class SNMPDispatcherTest {
     doReturn(trap).when(dispatcher).prepareTrap(notification, snmpVersion);
     dispatcher.sendTraps(notification, snmpVersion);
   }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv1() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv1");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "public");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.VALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_incorrectSNMPversion() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv4");
+    properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "public");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv1_invalid() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv1");
+    properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "public");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv2c() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv2c");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "public");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.VALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv2c_invalid() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv2c");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_incorrectSecurityLevel() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_AUTH_PASSPHRASE_PROPERTY, "PASSPHRASE1");
+    properties.put(SNMPDispatcher.SECURITY_PRIV_PASSPHRASE_PROPERTY, "PASSPHRASE2");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "INCORRECT");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_noAuthNoPriv() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "NOAUTH_NOPRIV");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.VALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_AuthNoPriv_valid() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_AUTH_PASSPHRASE_PROPERTY, "PASSPHRASE1");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "AUTH_NOPRIV");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.VALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_AuthNoPriv_invalid() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "AUTH_NOPRIV");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_AuthPriv_valid() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_AUTH_PASSPHRASE_PROPERTY, "PASSPHRASE1");
+    properties.put(SNMPDispatcher.SECURITY_PRIV_PASSPHRASE_PROPERTY, "PASSPHRASE2");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "AUTH_PRIV");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.VALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_AuthPriv_noPassphrases() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "AUTH_PRIV");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
+
+  @Test
+  public void testValidateAlertValidation_SNMPv3_AuthPriv_onlyAuthPassphrase() throws Exception {
+    Map<String, String> properties = new HashMap<String, String>();
+    properties.put(SNMPDispatcher.SUBJECT_OID_PROPERTY, "1");
+    properties.put(SNMPDispatcher.BODY_OID_PROPERTY, "2");
+    properties.put(SNMPDispatcher.PORT_PROPERTY, "162");
+    properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv3");
+    properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4");
+    properties.put(SNMPDispatcher.SECURITY_USERNAME_PROPERTY, "USER");
+    properties.put(SNMPDispatcher.SECURITY_AUTH_PASSPHRASE_PROPERTY, "PASSPHRASE1");
+    properties.put(SNMPDispatcher.SECURITY_LEVEL_PROPERTY, "AUTH_PRIV");
+    NotificationDispatcher dispatcher = new SNMPDispatcher();
+    NotificationDispatcher.ConfigValidationResult configValidationResult = dispatcher.validateTargetConfig(properties);
+    assertEquals(NotificationDispatcher.ConfigValidationResult.Status.INVALID, configValidationResult.getStatus());
+  }
 }

+ 12 - 0
ambari-server/src/test/java/org/apache/ambari/server/state/services/AlertNoticeDispatchServiceTest.java

@@ -28,6 +28,7 @@ import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.Map;
 import java.util.TimeZone;
 import java.util.UUID;
 import java.util.concurrent.Executor;
@@ -419,6 +420,11 @@ public class AlertNoticeDispatchServiceTest extends AlertNoticeDispatchService {
       m_notificaiton = notification;
     }
 
+    @Override
+    public ConfigValidationResult validateTargetConfig(Map<String, String> properties) {
+      return null;
+    }
+
     public Notification getNotification() {
       return m_notificaiton;
     }
@@ -459,6 +465,12 @@ public class AlertNoticeDispatchServiceTest extends AlertNoticeDispatchService {
     public List<Notification> getNotifications() {
       return m_notifications;
     }
+
+    @Override
+    public ConfigValidationResult validateTargetConfig(
+        Map<String, String> properties) {
+      return null;
+    }
   }
 
   /**