Repository: ambari Updated Branches: refs/heads/trunk c5cb5a16b -> 3603ce63f
AMBARI-8251 - Alerts: SNMP Target for Notifications (Yurii Shylov via jonathanhurley) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/3603ce63 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/3603ce63 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/3603ce63 Branch: refs/heads/trunk Commit: 3603ce63fcf3c8c3e31b893790dd029803d7cf26 Parents: c5cb5a1 Author: Jonathan Hurley <[email protected]> Authored: Tue Dec 23 13:32:24 2014 -0500 Committer: Jonathan Hurley <[email protected]> Committed: Tue Dec 23 13:32:24 2014 -0500 ---------------------------------------------------------------------- ambari-server/pom.xml | 5 + .../server/notifications/DispatchFactory.java | 4 +- .../dispatchers/SNMPDispatcher.java | 335 +++++++++++++++++ .../dispatchers/SNMPDispatcherTest.java | 370 +++++++++++++++++++ 4 files changed, 713 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/3603ce63/ambari-server/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml index 56de3dd..f2cbdad 100644 --- a/ambari-server/pom.xml +++ b/ambari-server/pom.xml @@ -1739,6 +1739,11 @@ <artifactId>smtp</artifactId> <version>1.5.2</version> </dependency> + <dependency> + <groupId>org.snmp4j</groupId> + <artifactId>snmp4j</artifactId> + <version>1.10.1</version> + </dependency> </dependencies> <pluginRepositories> http://git-wip-us.apache.org/repos/asf/ambari/blob/3603ce63/ambari-server/src/main/java/org/apache/ambari/server/notifications/DispatchFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/notifications/DispatchFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/notifications/DispatchFactory.java index e690e76..2675533 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/notifications/DispatchFactory.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/notifications/DispatchFactory.java @@ -25,6 +25,7 @@ import org.apache.ambari.server.notifications.dispatchers.EmailDispatcher; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Singleton; +import org.apache.ambari.server.notifications.dispatchers.SNMPDispatcher; /** * The {@link DispatchFactory} is used to provide singleton instances of @@ -45,8 +46,9 @@ public class DispatchFactory { @Inject public DispatchFactory(Injector injector) { EmailDispatcher emailDispatcher = injector.getInstance(EmailDispatcher.class); - + SNMPDispatcher snmpDispatcher = injector.getInstance(SNMPDispatcher.class); m_dispatchers.put(emailDispatcher.getType(), emailDispatcher); + m_dispatchers.put(snmpDispatcher.getType(), snmpDispatcher); } /** http://git-wip-us.apache.org/repos/asf/ambari/blob/3603ce63/ambari-server/src/main/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcher.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcher.java b/ambari-server/src/main/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcher.java new file mode 100644 index 0000000..0e75801 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcher.java @@ -0,0 +1,335 @@ +/** + * 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.notifications.dispatchers; + +import com.google.inject.Singleton; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.snmp4j.CommunityTarget; +import org.snmp4j.PDU; +import org.snmp4j.Snmp; +import org.snmp4j.Target; +import org.snmp4j.UserTarget; +import org.snmp4j.mp.MPv3; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.security.AuthMD5; +import org.snmp4j.security.PrivDES; +import org.snmp4j.security.SecurityLevel; +import org.snmp4j.security.SecurityModel; +import org.snmp4j.security.SecurityModels; +import org.snmp4j.security.SecurityProtocols; +import org.snmp4j.security.USM; +import org.snmp4j.security.UsmUser; +import org.snmp4j.smi.OID; +import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.UdpAddress; +import org.snmp4j.smi.VariableBinding; +import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.snmp4j.util.DefaultPDUFactory; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * The {@link SNMPDispatcher} class is used to dispatch {@link Notification} via SNMP. + */ +@Singleton +public class SNMPDispatcher implements NotificationDispatcher { + + private static final Logger LOG = LoggerFactory.getLogger(SNMPDispatcher.class); + + // Trap's object identifiers + public static final String BODY_OID_PROPERTY = "ambari.dispatch.snmp.oids.body"; + public static final String SUBJECT_OID_PROPERTY = "ambari.dispatch.snmp.oids.subject"; + public static final String TRAP_OID_PROPERTY = "ambari.dispatch.snmp.oids.trap"; + + // SNMP Server port + public static final String PORT_PROPERTY = "ambari.dispatch.snmp.port"; + + // SNMP version + public static final String SNMP_VERSION_PROPERTY = "ambari.dispatch.snmp.version"; + + // Properties for community-based security model configuration + public static final String COMMUNITY_PROPERTY = "ambari.dispatch.snmp.community"; + + // Properties for user-based security model configuration + public static final String SECURITY_USERNAME_PROPERTY = "ambari.dispatch.snmp.security.username"; + public static final String SECURITY_AUTH_PASSPHRASE_PROPERTY = "ambari.dispatch.snmp.security.auth.passphrase"; + public static final String SECURITY_PRIV_PASSPHRASE_PROPERTY = "ambari.dispatch.snmp.security.priv.passphrase"; + public static final String SECURITY_LEVEL_PROPERTY = "ambari.dispatch.snmp.security.level"; + + private Snmp snmp; + + public SNMPDispatcher(Snmp snmp) { + this.snmp = snmp; + } + + public SNMPDispatcher() throws IOException { + this(new Snmp(new DefaultUdpTransportMapping())); + } + + /** + * {@inheritDoc} + */ + @Override + public String getType() { + return TargetType.SNMP.name(); + } + + /** + * {@inheritDoc} + */ + @Override + public void dispatch(Notification notification) { + LOG.info("Sending SNMP trap: {}", notification.Subject); + try { + snmp = new Snmp(new DefaultUdpTransportMapping()); + SnmpVersion snmpVersion = getSnmpVersion(notification); + sendTraps(notification, snmpVersion); + successCallback(notification); + } catch (InvalidSnmpConfigurationException ex) { + LOG.error("Unable to dispatch SNMP trap with invalid configuration. " + ex.getMessage()); + failureCallback(notification); + } catch (Exception ex) { + LOG.error("Error occurred during SNMP trap dispatching.", ex); + failureCallback(notification); + } + } + + /** + * Creates protocol data unit (PDU) with corresponding SNMP version for alert notification. + * @param notification alert notification to dispatch + * @param snmpVersion SNMP version + * @return PDU containing notification info + * @throws InvalidSnmpConfigurationException if notification's dispatch properties don't contain any of required properties. + */ + protected PDU prepareTrap(Notification notification, SnmpVersion snmpVersion) throws InvalidSnmpConfigurationException { + 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)))); + // 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))); + return pdu; + } + + /** + * Creates trap based on alerts notification and sends it to hosts specified in recipients list. + * @param notification alert notification to dispatch + * @param snmpVersion SNMP version + * @throws InvalidSnmpConfigurationException if notification's dispatch properties don't contain any of required properties or recipient list is empty. + * @throws IOException if the SNMP trap could not be sent + */ + protected void sendTraps(Notification notification, SnmpVersion snmpVersion) throws InvalidSnmpConfigurationException, IOException { + PDU trap = prepareTrap(notification, snmpVersion); + String udpPort = getDispatchProperty(notification, PORT_PROPERTY); + for (Recipient recipient : getNotificationRecipients(notification)) { + String address = recipient.Identifier; + Target target = createTrapTarget(notification, snmpVersion); + target.setAddress(new UdpAddress(address + "/" + udpPort)); + snmp.send(trap, target); + } + } + + /** + * Creates snmp target with security model corresponding to snmp version. + * @param notification alerts notification + * @param snmpVersion SNMP version + * @return target with corresponding security model + * @throws InvalidSnmpConfigurationException if notification's dispatch properties don't contain any of required properties + */ + protected Target createTrapTarget(Notification notification, SnmpVersion snmpVersion) throws InvalidSnmpConfigurationException { + if (snmpVersion.isCommunityTargetRequired()) { + OctetString community = new OctetString(getDispatchProperty(notification, 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)); + if (snmp.getUSM() == null) { + // provide User-based Security Model (USM) with user specified + USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); + // authPassphraseProperty and privPassphraseProperty can be null for NoAuthNoPriv security level + String authPassphraseProperty = notification.DispatchProperties.get(SECURITY_AUTH_PASSPHRASE_PROPERTY); + String privPassphraseProperty = notification.DispatchProperties.get(SECURITY_PRIV_PASSPHRASE_PROPERTY); + OctetString authPassphrase = authPassphraseProperty != null ? new OctetString(authPassphraseProperty) : null; + OctetString privPassphrase = privPassphraseProperty != null ? new OctetString(privPassphraseProperty) : null; + UsmUser usmUser = new UsmUser(userName, AuthMD5.ID, authPassphrase, PrivDES.ID, privPassphrase); + usm.addUser(userName, usmUser); + SecurityModels.getInstance().addSecurityModel(usm); + } + UserTarget userTarget = new UserTarget(); + userTarget.setSecurityName(userName); + userTarget.setSecurityLevel(getSecurityLevel(notification).getSecurityLevel()); + userTarget.setSecurityModel(SecurityModel.SECURITY_MODEL_USM); + userTarget.setVersion(snmpVersion.getTargetVersion()); + return userTarget; + } + } + + /** + * Possible SNMP security levels + */ + protected enum TrapSecurity { + + /** + * No password authentication and the communications between the agent and the server are not encrypted. + */ + NOAUTH_NOPRIV(SecurityLevel.NOAUTH_NOPRIV), + /** + * Password authentication is hash based and no encryption is used for communications between the hosts. + */ + AUTH_NOPRIV(SecurityLevel.AUTH_NOPRIV), + /** + * Password authentication is hash based and the communications between the agent and the server are also encrypted. + */ + AUTH_PRIV(SecurityLevel.AUTH_PRIV); + + int securityLevel; + + TrapSecurity(int securityLevel) { + this.securityLevel = securityLevel; + } + + public int getSecurityLevel() { + return securityLevel; + } + } + + /** + * Supported versions of SNMP + */ + protected enum SnmpVersion { + + SNMPv1(PDU.V1TRAP, SnmpConstants.version1, true), + SNMPv2c(PDU.TRAP, SnmpConstants.version2c, true), + SNMPv3(PDU.TRAP, SnmpConstants.version3, false); + + private int trapType; + private int targetVersion; + private boolean communityTargetRequired; + + SnmpVersion(int trapType, int targetVersion, boolean communityTargetRequired) { + this.trapType = trapType; + this.targetVersion = targetVersion; + this.communityTargetRequired = communityTargetRequired; + } + + public int getTrapType() { + return trapType; + } + + public int getTargetVersion() { + return targetVersion; + } + + public boolean isCommunityTargetRequired() { + return communityTargetRequired; + } + } + + /** + * Exception thrown when Notification configuration doesn't contain required properties. + */ + protected static class InvalidSnmpConfigurationException extends Exception { + + public InvalidSnmpConfigurationException(String message) { + super(message); + } + } + + /** + * Get list of recipients for notification + * @param notification alerts notification + * @return list of recipients + * @throws InvalidSnmpConfigurationException if recipients is <code>null</code> or empty + */ + private List<Recipient> getNotificationRecipients(Notification notification) throws InvalidSnmpConfigurationException { + if (notification.Recipients == null || notification.Recipients.isEmpty()) { + throw new InvalidSnmpConfigurationException("Destination addresses should be set."); + } + return notification.Recipients; + } + + /** + * Get dispatch property with specific key from notification. + * @param notification alerts notification + * @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)) { + throw new InvalidSnmpConfigurationException(String.format("Property \"%s\" should be set.", key)); + } + return notification.DispatchProperties.get(key); + } + + /** + * Returns {@link SnmpVersion} instance corresponding to dispatch property <code>ambari.dispatch.snmp.version</code> from notification. + * @param notification alerts notification + * @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); + try { + return SnmpVersion.valueOf(snmpVersion); + } catch (IllegalArgumentException ex) { + String errorMessage = String.format("Incorrect SNMP version - \"%s\". Possible values for \"%s\": %s", + snmpVersion, SNMP_VERSION_PROPERTY, Arrays.toString(SnmpVersion.values())); + throw new InvalidSnmpConfigurationException(errorMessage); + } + } + + /** + * Returns {@link TrapSecurity} instance corresponding to dispatch property <code>ambari.dispatch.snmp.security.level</code> from notification. + * @param notification alerts notification + * @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); + try { + return TrapSecurity.valueOf(securityLevel); + } catch (IllegalArgumentException ex) { + String errorMessage = String.format("Incorrect security level for trap - \"%s\". Possible values for \"%s\": %s", + securityLevel, SECURITY_LEVEL_PROPERTY, Arrays.toString(TrapSecurity.values())); + throw new InvalidSnmpConfigurationException(errorMessage); + } + } + + private void failureCallback(Notification notification) { + if (notification.Callback != null) { + notification.Callback.onFailure(notification.CallbackIds); + } + } + + private void successCallback(Notification notification) { + if (notification.Callback != null) { + notification.Callback.onSuccess(notification.CallbackIds); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/3603ce63/ambari-server/src/test/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcherTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcherTest.java b/ambari-server/src/test/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcherTest.java new file mode 100644 index 0000000..db4af1c --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/notifications/dispatchers/SNMPDispatcherTest.java @@ -0,0 +1,370 @@ +/** + * 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.notifications.dispatchers; + +import org.apache.ambari.server.notifications.DispatchCallback; +import org.apache.ambari.server.notifications.Notification; +import org.apache.ambari.server.notifications.Recipient; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.snmp4j.PDU; +import org.snmp4j.Snmp; +import org.snmp4j.Target; +import org.snmp4j.mp.SnmpConstants; +import org.snmp4j.smi.VariableBinding; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class SNMPDispatcherTest { + + @Test + public void testDispatch_nullProperties() throws Exception { + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + dispatcher.dispatch(notification); + verify(notification.Callback).onFailure(notification.CallbackIds); + verify(notification.Callback, never()).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_notDefinedProperties() throws Exception { + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + notification.DispatchProperties = new HashMap<String, String>(); + dispatcher.dispatch(notification); + verify(notification.Callback).onFailure(notification.CallbackIds); + verify(notification.Callback, never()).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_nullRecipients() throws Exception { + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + 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, "3"); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "4"); + properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv1"); + properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + dispatcher.dispatch(notification); + verify(notification.Callback).onFailure(notification.CallbackIds); + verify(notification.Callback, never()).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_noRecipients() throws Exception { + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + 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, "3"); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "4"); + properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv1"); + properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + notification.Recipients = new ArrayList<Recipient>(); + dispatcher.dispatch(notification); + verify(notification.Callback).onFailure(notification.CallbackIds); + verify(notification.Callback, never()).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_sendTrapError() throws Exception { + SNMPDispatcher dispatcher = spy(new SNMPDispatcher()); + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + 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, "3"); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "4"); + properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv1"); + properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + notification.Recipients = Arrays.asList(new Recipient()); + doThrow(new RuntimeException()).when(dispatcher).sendTraps(eq(notification), any(SNMPDispatcher.SnmpVersion.class)); + dispatcher.dispatch(notification); + verify(notification.Callback).onFailure(notification.CallbackIds); + verify(notification.Callback, never()).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_incorrectSnmpVersion() throws Exception { + SNMPDispatcher dispatcher = spy(new SNMPDispatcher()); + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + 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, "3"); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "4"); + properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv11"); + properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + notification.Recipients = Arrays.asList(new Recipient()); + dispatcher.dispatch(notification); + verify(notification.Callback).onFailure(notification.CallbackIds); + verify(notification.Callback, never()).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_successful_v1() throws Exception { + SNMPDispatcher dispatcher = spy(new SNMPDispatcher()); + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv1; + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + 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, "3"); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "4"); + properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv1"); + properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + notification.Recipients = Arrays.asList(new Recipient()); + doNothing().when(dispatcher).sendTraps(notification, snmpVersion); + dispatcher.dispatch(notification); + verify(notification.Callback, never()).onFailure(notification.CallbackIds); + verify(notification.Callback).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_successful_v2() throws Exception { + SNMPDispatcher dispatcher = spy(new SNMPDispatcher()); + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv2c; + Notification notification = mock(Notification.class); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + 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, "3"); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "4"); + properties.put(SNMPDispatcher.SNMP_VERSION_PROPERTY, "SNMPv2c"); + properties.put(SNMPDispatcher.TRAP_OID_PROPERTY, "1.3.6.1.6.3.1.1.5.4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + notification.Recipients = Arrays.asList(new Recipient()); + doNothing().when(dispatcher).sendTraps(notification, snmpVersion); + dispatcher.dispatch(notification); + verify(notification.Callback, never()).onFailure(notification.CallbackIds); + verify(notification.Callback).onSuccess(notification.CallbackIds); + } + + @Test + public void testDispatch_successful_v3() throws Exception { + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = new Notification(); + notification.Callback = mock(DispatchCallback.class); + notification.CallbackIds = mock(List.class); + notification.Body = "body"; + notification.Subject = "subject"; + 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.COMMUNITY_PROPERTY, "public"); + 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_NOPRIV"); + notification.DispatchProperties = properties; + Recipient recipient = new Recipient(); + recipient.Identifier = "192.168.0.2"; + notification.Recipients = Arrays.asList(recipient); + dispatcher.dispatch(notification); + verify(notification.Callback, never()).onFailure(notification.CallbackIds); + verify(notification.Callback).onSuccess(notification.CallbackIds); + } + + @Test + public void testPrepareTrap_v1() throws Exception { + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv1; + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = new Notification(); + 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.TRAP_OID_PROPERTY, "3"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + PDU pdu = dispatcher.prepareTrap(notification, snmpVersion); + assertEquals(PDU.V1TRAP, pdu.getType()); + Map<String, VariableBinding> variableBindings = new HashMap<String, VariableBinding>(); + for (VariableBinding variableBinding : pdu.toArray()) { + variableBindings.put(variableBinding.getOid().toString(), variableBinding); + } + assertEquals(3, variableBindings.size()); + assertEquals("subject", variableBindings.get("1").toValueString()); + assertEquals("body", variableBindings.get("2").toValueString()); + assertEquals("3", variableBindings.get(SnmpConstants.snmpTrapOID.toString()).toValueString()); + } + + @Test + public void testPrepareTrap_v2c() throws Exception { + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv2c; + SNMPDispatcher dispatcher = new SNMPDispatcher(); + Notification notification = new Notification(); + 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.TRAP_OID_PROPERTY, "4"); + notification.DispatchProperties = properties; + notification.Body = "body"; + notification.Subject = "subject"; + PDU pdu = dispatcher.prepareTrap(notification, snmpVersion); + assertEquals(PDU.TRAP, pdu.getType()); + Map<String, VariableBinding> variableBindings = new HashMap<String, VariableBinding>(); + for (VariableBinding variableBinding : pdu.toArray()) { + variableBindings.put(variableBinding.getOid().toString(), variableBinding); + } + assertEquals(3, variableBindings.size()); + assertEquals("subject", variableBindings.get("1").toValueString()); + assertEquals("body", variableBindings.get("2").toValueString()); + assertEquals("4", variableBindings.get(SnmpConstants.snmpTrapOID.toString()).toValueString()); + } + + @Test + public void testSendTraps_v1() throws Exception { + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv1; + Snmp snmp = mock(Snmp.class); + SNMPDispatcher dispatcher = spy(new SNMPDispatcher(snmp)); + PDU trap = mock(PDU.class); + Notification notification = new Notification(); + Map<String, String> properties = new HashMap<String, String>(); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "public"); + properties.put(SNMPDispatcher.PORT_PROPERTY, "162"); + notification.DispatchProperties = properties; + Recipient rec1 = new Recipient(); + rec1.Identifier = "192.168.0.2"; + notification.Recipients = Arrays.asList(rec1); + doReturn(trap).when(dispatcher).prepareTrap(notification, snmpVersion); + dispatcher.sendTraps(notification, snmpVersion); + ArgumentCaptor<Target> argument = ArgumentCaptor.forClass(Target.class); + verify(snmp, times(1)).send(eq(trap), argument.capture()); + assertEquals("192.168.0.2/162", argument.getValue().getAddress().toString()); + assertEquals(SnmpConstants.version1, argument.getValue().getVersion()); + } + + @Test + public void testSendTraps_v2() throws Exception { + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv2c; + Snmp snmp = mock(Snmp.class); + SNMPDispatcher dispatcher = spy(new SNMPDispatcher(snmp)); + PDU trap = mock(PDU.class); + Notification notification = new Notification(); + Map<String, String> properties = new HashMap<String, String>(); + properties.put(SNMPDispatcher.COMMUNITY_PROPERTY, "public"); + properties.put(SNMPDispatcher.PORT_PROPERTY, "162"); + notification.DispatchProperties = properties; + Recipient rec1 = new Recipient(); + rec1.Identifier = "192.168.0.2"; + notification.Recipients = Arrays.asList(rec1); + doReturn(trap).when(dispatcher).prepareTrap(notification, snmpVersion); + dispatcher.sendTraps(notification, snmpVersion); + ArgumentCaptor<Target> argument = ArgumentCaptor.forClass(Target.class); + verify(snmp, times(1)).send(eq(trap), argument.capture()); + assertEquals("192.168.0.2/162", argument.getValue().getAddress().toString()); + assertEquals(SnmpConstants.version2c, argument.getValue().getVersion()); + } + + @Test + public void testSendTraps_v3() throws Exception { + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv3; + Snmp snmp = mock(Snmp.class); + SNMPDispatcher dispatcher = spy(new SNMPDispatcher(snmp)); + PDU trap = mock(PDU.class); + Notification notification = new Notification(); + Map<String, String> properties = new HashMap<String, String>(); + 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_NOPRIV"); + notification.DispatchProperties = properties; + Recipient rec1 = new Recipient(); + rec1.Identifier = "192.168.0.2"; + notification.Recipients = Arrays.asList(rec1); + doReturn(trap).when(dispatcher).prepareTrap(notification, snmpVersion); + dispatcher.sendTraps(notification, snmpVersion); + ArgumentCaptor<Target> argument = ArgumentCaptor.forClass(Target.class); + verify(snmp, times(1)).send(eq(trap), argument.capture()); + assertEquals("192.168.0.2/162", argument.getValue().getAddress().toString()); + assertEquals(SnmpConstants.version3, argument.getValue().getVersion()); + } + + @Test(expected = SNMPDispatcher.InvalidSnmpConfigurationException.class) + public void testSendTraps_v3_incorrectSecurityLevelVersion() throws Exception { + SNMPDispatcher.SnmpVersion snmpVersion = SNMPDispatcher.SnmpVersion.SNMPv3; + Snmp snmp = mock(Snmp.class); + SNMPDispatcher dispatcher = spy(new SNMPDispatcher(snmp)); + PDU trap = mock(PDU.class); + Notification notification = new Notification(); + Map<String, String> properties = new HashMap<String, String>(); + 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"); + notification.DispatchProperties = properties; + Recipient rec1 = new Recipient(); + rec1.Identifier = "192.168.0.2"; + notification.Recipients = Arrays.asList(rec1); + doReturn(trap).when(dispatcher).prepareTrap(notification, snmpVersion); + dispatcher.sendTraps(notification, snmpVersion); + } +}
