Repository: ambari Updated Branches: refs/heads/trunk 8486be6aa -> 211a48d56
AMBARI-15303 - New Alerts Do Not Honor Existing Maintenance Mode Setting (jonathanhurley) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/211a48d5 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/211a48d5 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/211a48d5 Branch: refs/heads/trunk Commit: 211a48d56eb6975d002b9c8c12aaab76049d6ee0 Parents: 8486be6 Author: Jonathan Hurley <[email protected]> Authored: Fri Mar 4 12:43:42 2016 -0500 Committer: Jonathan Hurley <[email protected]> Committed: Fri Mar 4 14:56:49 2016 -0500 ---------------------------------------------------------------------- .../controller/MaintenanceStateHelper.java | 60 ++++++++ .../listeners/alerts/AlertReceivedListener.java | 23 ++- .../controller/MaintenanceStateHelperTest.java | 145 +++++++++++++++++++ .../state/alerts/AlertReceivedListenerTest.java | 47 ++++++ 4 files changed, 274 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/211a48d5/ambari-server/src/main/java/org/apache/ambari/server/controller/MaintenanceStateHelper.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/MaintenanceStateHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/MaintenanceStateHelper.java index cd49e76..dd1652c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/MaintenanceStateHelper.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/MaintenanceStateHelper.java @@ -24,9 +24,11 @@ import java.util.Set; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.HostNotFoundException; +import org.apache.ambari.server.controller.RootServiceResponseFactory.Services; import org.apache.ambari.server.controller.internal.RequestOperationLevel; import org.apache.ambari.server.controller.internal.RequestResourceFilter; import org.apache.ambari.server.controller.spi.Resource; +import org.apache.ambari.server.state.Alert; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.Host; @@ -34,6 +36,7 @@ import org.apache.ambari.server.state.MaintenanceState; import org.apache.ambari.server.state.Service; import org.apache.ambari.server.state.ServiceComponent; import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -230,6 +233,63 @@ public class MaintenanceStateHelper { } /** + * Gets the effective maintenance state for a given alert. This will determine + * whether the alert needs to report that it is in maintenance mode. + * + * @param clusterId + * the ID of the cluster for the alert. + * @param alert + * the alert (not {@code null}). + * @return {@link MaintenanceState#OFF} if the host/service/component all + * report they are not in maintenance mode; + * {@link MaintenanceState#ON} otherwise. + * @throws AmbariException + */ + public MaintenanceState getEffectiveState(long clusterId, Alert alert) throws AmbariException { + String serviceName = alert.getService(); + String componentName = alert.getComponent(); + String hostName = alert.getHostName(); + + if (null == serviceName && null == hostName) { + LOG.warn("Unable to determine maintenance state for an alert without a service or host"); + return MaintenanceState.OFF; + } + + // always start with the host first; we always have a host unless this is an + // aggregate alert (service-level alert) + if (StringUtils.isNotBlank(hostName)) { + Host host = clusters.getHost(hostName); + if (host.getMaintenanceState(clusterId) != MaintenanceState.OFF) { + return MaintenanceState.ON; + } + } + + // the AMBARI service is not a real service; it's never in MM + if( StringUtils.equals(Services.AMBARI.name(), serviceName)){ + return MaintenanceState.OFF; + } + + // the host is not in MM, move onto the service + Cluster cluster = clusters.getClusterById(clusterId); + Service service = cluster.getService(serviceName); + if( service.getMaintenanceState() != MaintenanceState.OFF ){ + return MaintenanceState.ON; + } + + if( StringUtils.isNotBlank(componentName)){ + ServiceComponent serviceComponent = service.getServiceComponent(componentName); + ServiceComponentHost serviceComponentHost = serviceComponent.getServiceComponentHost(hostName); + + if (serviceComponentHost.getMaintenanceState() != MaintenanceState.OFF) { + return MaintenanceState.ON; + } + } + + // MM is off at the host, service, and component level; return OFF + return MaintenanceState.OFF; + } + + /** * @param clusters * the collection of clusters * @param cluster http://git-wip-us.apache.org/repos/asf/ambari/blob/211a48d5/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java index 9bbfe37..8dc8e1e 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/alerts/AlertReceivedListener.java @@ -24,6 +24,7 @@ import java.util.Map; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.EagerSingleton; import org.apache.ambari.server.configuration.Configuration; +import org.apache.ambari.server.controller.MaintenanceStateHelper; import org.apache.ambari.server.controller.RootServiceResponseFactory.Services; import org.apache.ambari.server.events.AlertEvent; import org.apache.ambari.server.events.AlertReceivedEvent; @@ -81,6 +82,16 @@ public class AlertReceivedListener { Provider<Clusters> m_clusters; /** + * Used to calculate the maintenance state of new alerts being created. + * Consider the case where you have disabled alerts for a component in MM. + * This means that there are no current alerts in the system since disabling + * them removes all current instances. New alerts being created for the + * component in MM must reflect the correct MM. + */ + @Inject + private Provider<MaintenanceStateHelper> m_maintenanceStateHelper; + + /** * Receives and publishes {@link AlertEvent} instances. */ private AlertEventPublisher m_alertEventPublisher; @@ -164,8 +175,18 @@ public class AlertReceivedListener { if (null == current) { AlertHistoryEntity history = createHistory(clusterId, definition, alert); + // this new alert must reflect the correct MM state for the + // service/component/host + MaintenanceState maintenanceState = MaintenanceState.OFF; + try { + maintenanceState = m_maintenanceStateHelper.get().getEffectiveState(clusterId, alert); + } catch (Exception exception) { + LOG.error("Unable to determine the maintenance mode state for {}, defaulting to OFF", + alert, exception); + } + current = new AlertCurrentEntity(); - current.setMaintenanceState(MaintenanceState.OFF); + current.setMaintenanceState(maintenanceState); current.setAlertHistory(history); current.setLatestTimestamp(alert.getTimestamp()); current.setOriginalTimestamp(alert.getTimestamp()); http://git-wip-us.apache.org/repos/asf/ambari/blob/211a48d5/ambari-server/src/test/java/org/apache/ambari/server/controller/MaintenanceStateHelperTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/MaintenanceStateHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/MaintenanceStateHelperTest.java index d9c5039..8cdaa10 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/MaintenanceStateHelperTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/MaintenanceStateHelperTest.java @@ -39,11 +39,14 @@ import org.apache.ambari.server.controller.internal.RequestOperationLevel; import org.apache.ambari.server.controller.internal.RequestResourceFilter; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.controller.spi.Resource.Type; +import org.apache.ambari.server.state.Alert; +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.Host; import org.apache.ambari.server.state.MaintenanceState; import org.apache.ambari.server.state.Service; +import org.apache.ambari.server.state.ServiceComponent; import org.apache.ambari.server.state.ServiceComponentHost; import org.easymock.EasyMock; import org.junit.Assert; @@ -154,6 +157,148 @@ public class MaintenanceStateHelperTest { verify(maintenanceStateHelper, clusters, cluster, sch, host, service); } + /** + * Tests that the host MM state is calculated correctly for an alert. + * + * @throws Exception + */ + @Test + public void testGetEffectiveStateForHostAlert() throws Exception { + Injector injector = createStrictMock(Injector.class); + MaintenanceStateHelper maintenanceStateHelper = createMockBuilder( + MaintenanceStateHelper.class).withConstructor(injector).createNiceMock(); + + Clusters clusters = createMock(Clusters.class); + final Host host = createNiceMock(Host.class); + + long clusterId = 1L; + String hostName = "c6401.ambari.apache.org"; + + Alert alert = new Alert("foo-alert", null, "HDFS", "DATANODE", hostName, + AlertState.CRITICAL); + + expect(host.getMaintenanceState(clusterId)).andReturn(MaintenanceState.ON).once(); + expect(clusters.getHost(hostName)).andReturn(host).once(); + + injectField(maintenanceStateHelper, clusters); + replay(maintenanceStateHelper, clusters, host); + + MaintenanceState state = maintenanceStateHelper.getEffectiveState(clusterId, alert); + Assert.assertEquals(MaintenanceState.ON, state); + + verify(maintenanceStateHelper, clusters, host); + } + + /** + * Tests that the service MM state is calculated correctly for an alert. + * + * @throws Exception + */ + @Test + public void testGetEffectiveStateForServiceAlert() throws Exception { + Injector injector = createStrictMock(Injector.class); + MaintenanceStateHelper maintenanceStateHelper = createMockBuilder( + MaintenanceStateHelper.class).withConstructor(injector).createNiceMock(); + + Clusters clusters = createMock(Clusters.class); + Cluster cluster = createMock(Cluster.class); + Service service = createNiceMock(Service.class); + final Host host = createNiceMock(Host.class); + + long clusterId = 1L; + String hostName = "c6401.ambari.apache.org"; + + Alert alert = new Alert("foo-alert", null, "HDFS", null, hostName, AlertState.CRITICAL); + + expect(host.getMaintenanceState(clusterId)).andReturn(MaintenanceState.OFF).once(); + expect(clusters.getHost(hostName)).andReturn(host).once(); + expect(clusters.getClusterById(clusterId)).andReturn(cluster).once(); + expect(cluster.getService("HDFS")).andReturn(service).once(); + expect(service.getMaintenanceState()).andReturn(MaintenanceState.ON); + + injectField(maintenanceStateHelper, clusters); + replay(maintenanceStateHelper, clusters, host, cluster, service); + + MaintenanceState state = maintenanceStateHelper.getEffectiveState(clusterId, alert); + Assert.assertEquals(MaintenanceState.ON, state); + + verify(maintenanceStateHelper, clusters, host, cluster, service); + } + + /** + * Tests that the service MM state is calculated correctly for an alert which + * is only for a service (such as an AGGREGATE alert). + * + * @throws Exception + */ + @Test + public void testGetEffectiveStateForServiceOnlyAlert() throws Exception { + Injector injector = createStrictMock(Injector.class); + MaintenanceStateHelper maintenanceStateHelper = createMockBuilder( + MaintenanceStateHelper.class).withConstructor(injector).createNiceMock(); + + Clusters clusters = createMock(Clusters.class); + Cluster cluster = createMock(Cluster.class); + Service service = createNiceMock(Service.class); + + long clusterId = 1L; + + Alert alert = new Alert("foo-alert", null, "HDFS", null, null, AlertState.CRITICAL); + + expect(clusters.getClusterById(clusterId)).andReturn(cluster).once(); + expect(cluster.getService("HDFS")).andReturn(service).once(); + expect(service.getMaintenanceState()).andReturn(MaintenanceState.ON); + + injectField(maintenanceStateHelper, clusters); + replay(maintenanceStateHelper, clusters, cluster, service); + + MaintenanceState state = maintenanceStateHelper.getEffectiveState(clusterId, alert); + Assert.assertEquals(MaintenanceState.ON, state); + + verify(maintenanceStateHelper, clusters, cluster, service); + } + + /** + * Tests that the service MM state is calculated correctly for an alert. + * + * @throws Exception + */ + @Test + public void testGetEffectiveStateForComponentAlert() throws Exception { + Injector injector = createStrictMock(Injector.class); + MaintenanceStateHelper maintenanceStateHelper = createMockBuilder( + MaintenanceStateHelper.class).withConstructor(injector).createNiceMock(); + + Clusters clusters = createMock(Clusters.class); + Cluster cluster = createMock(Cluster.class); + Service service = createNiceMock(Service.class); + ServiceComponent serviceComponent = createMock(ServiceComponent.class); + ServiceComponentHost sch = createMock(ServiceComponentHost.class); + final Host host = createNiceMock(Host.class); + + long clusterId = 1L; + String hostName = "c6401.ambari.apache.org"; + + Alert alert = new Alert("foo-alert", null, "HDFS", "DATANODE", hostName, AlertState.CRITICAL); + + expect(host.getMaintenanceState(clusterId)).andReturn(MaintenanceState.OFF).once(); + expect(clusters.getHost(hostName)).andReturn(host).once(); + expect(clusters.getClusterById(clusterId)).andReturn(cluster).once(); + expect(cluster.getService("HDFS")).andReturn(service).once(); + expect(service.getMaintenanceState()).andReturn(MaintenanceState.OFF); + expect(service.getServiceComponent("DATANODE")).andReturn(serviceComponent).once(); + expect(serviceComponent.getServiceComponentHost(hostName)).andReturn(sch).once(); + expect(sch.getMaintenanceState()).andReturn(MaintenanceState.ON).once(); + + injectField(maintenanceStateHelper, clusters); + replay(maintenanceStateHelper, clusters, host, cluster, service, serviceComponent, sch); + + MaintenanceState state = maintenanceStateHelper.getEffectiveState(clusterId, alert); + Assert.assertEquals(MaintenanceState.ON, state); + + verify(maintenanceStateHelper, clusters, host, cluster, service, serviceComponent, sch); + } + @Test public void testServiceOperationsAllowance() throws Exception { Injector injector = createStrictMock(Injector.class); http://git-wip-us.apache.org/repos/asf/ambari/blob/211a48d5/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java index 6e58876..136a756 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertReceivedListenerTest.java @@ -35,6 +35,7 @@ import org.apache.ambari.server.state.Alert; 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.MaintenanceState; import org.apache.ambari.server.state.ServiceComponentFactory; import org.apache.ambari.server.state.ServiceComponentHostFactory; import org.apache.ambari.server.state.ServiceFactory; @@ -259,4 +260,50 @@ public class AlertReceivedListenerTest { allCurrent = m_dao.findCurrent(); assertEquals(0, allCurrent.size()); } + + /** + * Tests that a disabled definition doesn't record alert events. + */ + @Test + public void testMaintenanceModeSet() throws Exception { + String definitionName = ALERT_DEFINITION + "1"; + String componentName = "DATANODE"; + + Alert alert1 = new Alert(definitionName, null, "HDFS", componentName, HOST1, + AlertState.CRITICAL); + + alert1.setCluster(m_cluster.getClusterName()); + alert1.setLabel(ALERT_LABEL); + alert1.setText("HDFS " + componentName + " is OK"); + alert1.setTimestamp(1L); + + // verify that the listener works with a regular alert + AlertReceivedListener listener = m_injector.getInstance(AlertReceivedListener.class); + AlertReceivedEvent event = new AlertReceivedEvent(m_cluster.getClusterId(), alert1); + listener.onAlertEvent(event); + + List<AlertCurrentEntity> allCurrent = m_dao.findCurrent(); + assertEquals(1, allCurrent.size()); + + AlertCurrentEntity current = allCurrent.get(0); + assertEquals(MaintenanceState.OFF, current.getMaintenanceState()); + + // remove it + m_dao.removeCurrentByService(m_cluster.getClusterId(), "HDFS"); + allCurrent = m_dao.findCurrent(); + assertEquals(0, allCurrent.size()); + + // set maintenance mode on the service + m_cluster.getService("HDFS").setMaintenanceState(MaintenanceState.ON); + + // verify that the listener handles the event and creates the current alert + // with the correct MM + listener.onAlertEvent(event); + + allCurrent = m_dao.findCurrent(); + assertEquals(1, allCurrent.size()); + + current = allCurrent.get(0); + assertEquals(MaintenanceState.ON, current.getMaintenanceState()); + } }
