Browse Source

[AMBARI-22931] Pre-Upgrade Checks Should Have Structured Output (#292)

Jonathan Hurley 7 years ago
parent
commit
c3a05fe887
28 changed files with 441 additions and 149 deletions
  1. 8 3
      ambari-project/pom.xml
  2. 4 0
      ambari-server/pom.xml
  3. 164 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java
  4. 0 58
      ambari-server/src/main/java/org/apache/ambari/server/checks/AtlasPresenceCheck.java
  5. 43 3
      ambari-server/src/main/java/org/apache/ambari/server/checks/ClientRetryPropertyCheck.java
  6. 23 11
      ambari-server/src/main/java/org/apache/ambari/server/checks/ComponentsExistInRepoCheck.java
  7. 22 16
      ambari-server/src/main/java/org/apache/ambari/server/checks/ComponentsInstallationCheck.java
  8. 6 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/HealthCheck.java
  9. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/HostMaintenanceModeCheck.java
  10. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/HostsHeartbeatCheck.java
  11. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/HostsMasterMaintenanceCheck.java
  12. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/HostsRepositoryVersionCheck.java
  13. 13 4
      ambari-server/src/main/java/org/apache/ambari/server/checks/InstallPackagesCheck.java
  14. 8 0
      ambari-server/src/main/java/org/apache/ambari/server/checks/RequiredServicesInRepositoryCheck.java
  15. 76 4
      ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceCheckValidityCheck.java
  16. 11 5
      ambari-server/src/main/java/org/apache/ambari/server/checks/ServicesUpCheck.java
  17. 17 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostComponentSummary.java
  18. 1 1
      ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
  19. 0 42
      ambari-server/src/test/java/org/apache/ambari/server/checks/AtlasPresenceCheckTest.java
  20. 6 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/ComponentExistsInRepoCheckTest.java
  21. 5 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/ComponentsInstallationCheckTest.java
  22. 2 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/HealthCheckTest.java
  23. 2 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/HostMaintenanceModeCheckTest.java
  24. 3 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/HostsHeartbeatCheckTest.java
  25. 2 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/InstallPackagesCheckTest.java
  26. 2 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/RequiredServicesInRepositoryCheckTest.java
  27. 9 0
      ambari-server/src/test/java/org/apache/ambari/server/checks/ServicesUpCheckTest.java
  28. 2 2
      contrib/ambari-scom/ambari-scom-server/src/main/resources/META-INF/spring-security.xml

+ 8 - 3
ambari-project/pom.xml

@@ -128,23 +128,28 @@
       <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-core</artifactId>
-        <version>3.1.2.RELEASE</version>
+        <version>3.2.10.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-config</artifactId>
-        <version>3.1.2.RELEASE</version>
+        <version>3.2.10.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-web</artifactId>
-        <version>3.1.2.RELEASE</version>
+        <version>3.2.10.RELEASE</version>
       </dependency>
       <dependency>
         <groupId>org.springframework.security.kerberos</groupId>
         <artifactId>spring-security-kerberos-web</artifactId>
         <version>1.0.1.RELEASE</version>
       </dependency>
+      <dependency>
+        <groupId>org.springframework</groupId>
+        <artifactId>spring-jdbc</artifactId>
+        <version>3.2.10.RELEASE</version>
+      </dependency>
       <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-mock</artifactId>

+ 4 - 0
ambari-server/pom.xml

@@ -1206,6 +1206,10 @@
       <groupId>org.springframework.security.kerberos</groupId>
       <artifactId>spring-security-kerberos-web</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-jdbc</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-mock</artifactId>

+ 164 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/AbstractCheckDescriptor.java

@@ -18,9 +18,11 @@
 package org.apache.ambari.server.checks;
 
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import org.apache.ambari.server.AmbariException;
@@ -47,6 +49,7 @@ import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
+import org.codehaus.jackson.annotate.JsonProperty;
 
 import com.google.common.collect.Lists;
 import com.google.gson.Gson;
@@ -468,4 +471,165 @@ public abstract class AbstractCheckDescriptor {
       return false;
     }
   }
+
+  /**
+   * Used to represent information about a service. This class is safe to use in
+   * sorted & unique collections.
+   */
+  static class ServiceDetail implements Comparable<ServiceDetail> {
+    @JsonProperty("service_name")
+    final String serviceName;
+
+    ServiceDetail(String serviceName) {
+      this.serviceName = serviceName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+      return Objects.hash(serviceName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+
+      if (obj == null) {
+        return false;
+      }
+
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+
+      ServiceDetail other = (ServiceDetail) obj;
+      return Objects.equals(serviceName, other.serviceName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int compareTo(ServiceDetail other) {
+      return serviceName.compareTo(other.serviceName);
+    }
+  }
+
+  /**
+   * Used to represent information about a service component. This class is safe
+   * to use in sorted & unique collections.
+   */
+  static class ServiceComponentDetail implements Comparable<ServiceComponentDetail> {
+    @JsonProperty("service_name")
+    final String serviceName;
+
+    @JsonProperty("component_name")
+    final String componentName;
+
+    ServiceComponentDetail(String serviceName, String componentName) {
+      this.serviceName = serviceName;
+      this.componentName = componentName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+      return Objects.hash(serviceName, componentName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+
+      if (obj == null) {
+        return false;
+      }
+
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+
+      ServiceComponentDetail other = (ServiceComponentDetail) obj;
+      return Objects.equals(serviceName, other.serviceName)
+          && Objects.equals(componentName, other.componentName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int compareTo(ServiceComponentDetail other) {
+      return Comparator.comparing(
+          (ServiceComponentDetail detail) -> detail.serviceName).thenComparing(
+              detail -> detail.componentName).compare(this, other);
+    }
+  }
+
+  /**
+   * Used to represent information about a host. This class is safe to use in
+   * sorted & unique collections.
+   */
+  static class HostDetail implements Comparable<HostDetail> {
+    @JsonProperty("host_id")
+    final Long hostId;
+
+    @JsonProperty("host_name")
+    final String hostName;
+
+    HostDetail(Long hostId, String hostName) {
+      this.hostId = hostId;
+      this.hostName = hostName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+      return Objects.hash(hostId, hostName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+
+      if (obj == null) {
+        return false;
+      }
+
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+
+      HostDetail other = (HostDetail) obj;
+      return Objects.equals(hostId, other.hostId) && Objects.equals(hostName, other.hostName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int compareTo(HostDetail other) {
+      return hostName.compareTo(other.hostName);
+    }
+  }
 }

+ 0 - 58
ambari-server/src/main/java/org/apache/ambari/server/checks/AtlasPresenceCheck.java

@@ -1,58 +0,0 @@
-/*
- * 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.checks;
-
-import java.util.Set;
-
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.controller.PrereqCheckRequest;
-import org.apache.ambari.server.state.stack.PrereqCheckStatus;
-import org.apache.ambari.server.state.stack.PrerequisiteCheck;
-
-import com.google.common.collect.Sets;
-import com.google.inject.Singleton;
-
-/**
- * Checks if Atlas service is present. Upgrade to stack HDP 2.5 from previous stack
- * must first delete Atlas from the cluster.
- */
-@Singleton
-@UpgradeCheck(group = UpgradeCheckGroup.DEFAULT)
-public class AtlasPresenceCheck extends AbstractCheckDescriptor{
-
-  private static final String serviceName = "ATLAS";
-
-  public AtlasPresenceCheck(){
-    super(CheckDescription.ATLAS_SERVICE_PRESENCE_CHECK);
-  }
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public Set<String> getApplicableServices() {
-    return Sets.newHashSet(serviceName);
-  }
-
-  @Override
-  public void perform(PrerequisiteCheck prerequisiteCheck, PrereqCheckRequest request) throws AmbariException {
-    prerequisiteCheck.getFailedOn().add(serviceName);
-    prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
-    prerequisiteCheck.setFailReason(getFailReason(prerequisiteCheck, request));
-  }
-}

+ 43 - 3
ambari-server/src/main/java/org/apache/ambari/server/checks/ClientRetryPropertyCheck.java

@@ -30,6 +30,7 @@ import org.apache.ambari.server.state.stack.PrereqCheckStatus;
 import org.apache.ambari.server.state.stack.PrerequisiteCheck;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 import org.apache.commons.lang.StringUtils;
+import org.codehaus.jackson.annotate.JsonProperty;
 
 import com.google.common.collect.Sets;
 import com.google.inject.Singleton;
@@ -48,6 +49,10 @@ public class ClientRetryPropertyCheck extends AbstractCheckDescriptor {
   static final String HIVE_CLIENT_RETRY_MISSING_KEY = "hive.client.retry.missing.key";
   static final String OOZIE_CLIENT_RETRY_MISSING_KEY = "oozie.client.retry.missing.key";
 
+  private static final String HDFS_CLIENT_RETRY_PROPERTY = "dfs.client.retry.policy.enabled";
+  private static final String HIVE_CLIENT_RETRY_PROPERTY = "hive.metastore.failure.retries";
+  private static final String OOZIE_CLIENT_RETRY_PROPERTY = "-Doozie.connection.retry.count";
+
   /**
    * Constructor.
    */
@@ -76,8 +81,13 @@ public class ClientRetryPropertyCheck extends AbstractCheckDescriptor {
 
     // HDFS needs to actually prevent client retry since that causes them to try too long and not failover quickly.
     if (services.containsKey("HDFS")) {
-      String clientRetryPolicyEnabled = getProperty(request, "hdfs-site", "dfs.client.retry.policy.enabled");
+      String clientRetryPolicyEnabled = getProperty(request, "hdfs-site", HDFS_CLIENT_RETRY_PROPERTY);
       if (null != clientRetryPolicyEnabled && Boolean.parseBoolean(clientRetryPolicyEnabled)) {
+        MissingClientRetryProperty missingProperty = new MissingClientRetryProperty("HDFS",
+            "hdfs-site", HDFS_CLIENT_RETRY_PROPERTY);
+
+        prerequisiteCheck.getFailedDetail().add(missingProperty);
+
         errorMessages.add(getFailReason(HDFS_CLIENT_RETRY_DISABLED_KEY, prerequisiteCheck, request));
         prerequisiteCheck.getFailedOn().add("HDFS");
       }
@@ -85,8 +95,13 @@ public class ClientRetryPropertyCheck extends AbstractCheckDescriptor {
 
     // check hive client properties
     if (services.containsKey("HIVE")) {
-      String hiveClientRetryCount = getProperty(request, "hive-site", "hive.metastore.failure.retries");
+      String hiveClientRetryCount = getProperty(request, "hive-site", HIVE_CLIENT_RETRY_PROPERTY);
       if (null != hiveClientRetryCount && Integer.parseInt(hiveClientRetryCount) <= 0) {
+        MissingClientRetryProperty missingProperty = new MissingClientRetryProperty("HIVE",
+            "hive-site", HIVE_CLIENT_RETRY_PROPERTY);
+
+        prerequisiteCheck.getFailedDetail().add(missingProperty);
+
         errorMessages.add(getFailReason(HIVE_CLIENT_RETRY_MISSING_KEY, prerequisiteCheck, request));
         prerequisiteCheck.getFailedOn().add("HIVE");
       }
@@ -94,7 +109,12 @@ public class ClientRetryPropertyCheck extends AbstractCheckDescriptor {
 
     if (services.containsKey("OOZIE")) {
       String oozieClientRetry = getProperty(request, "oozie-env", "content");
-      if (null == oozieClientRetry || !oozieClientRetry.contains("-Doozie.connection.retry.count")) {
+      if (null == oozieClientRetry || !oozieClientRetry.contains(OOZIE_CLIENT_RETRY_PROPERTY)) {
+        MissingClientRetryProperty missingProperty = new MissingClientRetryProperty("OOZIE",
+            "oozie-env", OOZIE_CLIENT_RETRY_PROPERTY);
+
+        prerequisiteCheck.getFailedDetail().add(missingProperty);
+
         errorMessages.add(getFailReason(OOZIE_CLIENT_RETRY_MISSING_KEY, prerequisiteCheck, request));
         prerequisiteCheck.getFailedOn().add("OOZIE");
       }
@@ -105,4 +125,24 @@ public class ClientRetryPropertyCheck extends AbstractCheckDescriptor {
       prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
     }
   }
+
+  /**
+   * Used to represent a missing retry property.
+   */
+  private static class MissingClientRetryProperty {
+    @JsonProperty("service_name")
+    public String serviceName;
+
+    @JsonProperty("type")
+    public String propertyType;
+
+    @JsonProperty("property_name")
+    public String propertyName;
+
+    MissingClientRetryProperty(String serviceName, String propertyType, String propertyName) {
+      this.serviceName = serviceName;
+      this.propertyType = propertyType;
+      this.propertyName = propertyName;
+    }
+  }
 }

+ 23 - 11
ambari-server/src/main/java/org/apache/ambari/server/checks/ComponentsExistInRepoCheck.java

@@ -22,6 +22,7 @@ import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StackAccessException;
@@ -68,8 +69,8 @@ public class ComponentsExistInRepoCheck extends AbstractCheckDescriptor {
     StackId sourceStack = request.getSourceStackId();
     StackId targetStack = repositoryVersion.getStackId();
 
-    Set<String> failedServices = new TreeSet<>();
-    Set<String> failedComponents = new TreeSet<>();
+    Set<ServiceDetail> failedServices = new TreeSet<>();
+    Set<ServiceComponentDetail> failedComponents = new TreeSet<>();
 
     Set<String> servicesInUpgrade = getServicesInUpgrade(request);
     for (String serviceName : servicesInUpgrade) {
@@ -78,7 +79,7 @@ public class ComponentsExistInRepoCheck extends AbstractCheckDescriptor {
             targetStack.getStackVersion(), serviceName);
 
         if (serviceInfo.isDeleted() || !serviceInfo.isValid()) {
-          failedServices.add(serviceName);
+          failedServices.add(new ServiceDetail(serviceName));
           continue;
         }
 
@@ -96,30 +97,41 @@ public class ComponentsExistInRepoCheck extends AbstractCheckDescriptor {
             }
 
             if (componentInfo.isDeleted()) {
-              failedComponents.add(componentName);
+              failedComponents.add(new ServiceComponentDetail(serviceName, componentName));
             }
 
           } catch (StackAccessException stackAccessException) {
-            failedComponents.add(componentName);
+            failedComponents.add(new ServiceComponentDetail(serviceName, componentName));
           }
         }
       } catch (StackAccessException stackAccessException) {
-        failedServices.add(serviceName);
+        failedServices.add(new ServiceDetail(serviceName));
       }
     }
 
-    if( failedServices.isEmpty() && failedComponents.isEmpty() ){
+    if (failedServices.isEmpty() && failedComponents.isEmpty()) {
       prerequisiteCheck.setStatus(PrereqCheckStatus.PASS);
       return;
     }
 
-    LinkedHashSet<String> failedOn = new LinkedHashSet<>();
-    failedOn.addAll(failedServices);
-    failedOn.addAll(failedComponents);
+    Set<String> failedServiceNames = failedServices.stream().map(
+        failureDetail -> failureDetail.serviceName).collect(
+            Collectors.toCollection(LinkedHashSet::new));
 
-    prerequisiteCheck.setFailedOn(failedOn);
+    Set<String> failedComponentNames = failedComponents.stream().map(
+        failureDetail -> failureDetail.componentName).collect(
+            Collectors.toCollection(LinkedHashSet::new));
+
+    LinkedHashSet<String> failures = new LinkedHashSet<>();
+    failures.addAll(failedServiceNames);
+    failures.addAll(failedComponentNames);
+
+    prerequisiteCheck.setFailedOn(failures);
     prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
 
+    prerequisiteCheck.getFailedDetail().addAll(failedServices);
+    prerequisiteCheck.getFailedDetail().addAll(failedComponents);
+
     String message = "The following {0} exist in {1} but are not included in {2}. They must be removed before upgrading.";
     String messageFragment = "";
     if (!failedServices.isEmpty()) {

+ 22 - 16
ambari-server/src/main/java/org/apache/ambari/server/checks/ComponentsInstallationCheck.java

@@ -70,23 +70,28 @@ public class ComponentsInstallationCheck extends AbstractCheckDescriptor {
     for (String serviceName : servicesInUpgrade) {
       final Service service = cluster.getService(serviceName);
       // Skip service if it is in maintenance mode
-      if (service.getMaintenanceState() != MaintenanceState.ON) {
-        Map<String, ServiceComponent> serviceComponents = service.getServiceComponents();
-        for (Map.Entry<String, ServiceComponent> component : serviceComponents.entrySet()) {
-          ServiceComponent serviceComponent = component.getValue();
-          if (serviceComponent.isVersionAdvertised()) {
-            List<HostComponentSummary> hostComponentSummaries = HostComponentSummary.getHostComponentSummaries(
-                service.getName(), serviceComponent.getName());
+      if (service.getMaintenanceState() == MaintenanceState.ON) {
+        continue;
+      }
+
+      Map<String, ServiceComponent> serviceComponents = service.getServiceComponents();
+      for (Map.Entry<String, ServiceComponent> component : serviceComponents.entrySet()) {
+        ServiceComponent serviceComponent = component.getValue();
+        if (serviceComponent.isVersionAdvertised()) {
+          List<HostComponentSummary> hostComponentSummaries = HostComponentSummary.getHostComponentSummaries(
+              service.getName(), serviceComponent.getName());
+
+          for (HostComponentSummary hcs : hostComponentSummaries) {
+            // Skip host if it is in maintenance mode
+            Host host = clustersProvider.get().getHost(hcs.getHostName());
+            if (host.getMaintenanceState(cluster.getClusterId()) != MaintenanceState.ON) {
+              if (hcs.getCurrentState() == State.INSTALL_FAILED) {
 
-            for (HostComponentSummary hcs : hostComponentSummaries) {
-              // Skip host if it is in maintenance mode
-              Host host = clustersProvider.get().getHost(hcs.getHostName());
-              if (host.getMaintenanceState(cluster.getClusterId()) != MaintenanceState.ON) {
-                if (hcs.getCurrentState() == State.INSTALL_FAILED) {
-                  failedServiceNames.add(service.getName());
-                  installFailedHostComponents.add(MessageFormat.format(
-                      "[{0}:{1} on {2}]", service.getName(), serviceComponent.getName(), hcs.getHostName()));
-                }
+                prerequisiteCheck.getFailedDetail().add(hcs);
+
+                failedServiceNames.add(service.getName());
+                installFailedHostComponents.add(MessageFormat.format("[{0}:{1} on {2}]",
+                    service.getName(), serviceComponent.getName(), hcs.getHostName()));
               }
             }
           }
@@ -97,6 +102,7 @@ public class ComponentsInstallationCheck extends AbstractCheckDescriptor {
     if(!installFailedHostComponents.isEmpty()) {
       String message = MessageFormat.format("Service components in INSTALL_FAILED state: {0}.",
           StringUtils.join(installFailedHostComponents, ", "));
+
       prerequisiteCheck.setFailedOn(new LinkedHashSet<>(failedServiceNames));
       prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
       prerequisiteCheck.setFailReason(

+ 6 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/HealthCheck.java

@@ -37,6 +37,7 @@ import org.apache.ambari.server.state.stack.PrereqCheckStatus;
 import org.apache.ambari.server.state.stack.PrerequisiteCheck;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 import org.apache.commons.lang.StringUtils;
+import org.codehaus.jackson.annotate.JsonProperty;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -108,8 +109,13 @@ public class HealthCheck extends AbstractCheckDescriptor {
    * Used to represent specific detail about alert.
    */
   private static class AlertDetail {
+    @JsonProperty("state")
     public String state;
+
+    @JsonProperty("label")
     public String label;
+
+    @JsonProperty("host_name")
     public String hostName;
 
     AlertDetail(String state, String label, String hostName) {

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/HostMaintenanceModeCheck.java

@@ -70,6 +70,9 @@ public class HostMaintenanceModeCheck extends AbstractCheckDescriptor {
       MaintenanceState maintenanceState = host.getMaintenanceState(cluster.getClusterId());
       if (maintenanceState != MaintenanceState.OFF) {
         prerequisiteCheck.getFailedOn().add(host.getHostName());
+
+        prerequisiteCheck.getFailedDetail().add(
+            new HostDetail(host.getHostId(), host.getHostName()));
       }
     }
 

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/HostsHeartbeatCheck.java

@@ -73,6 +73,9 @@ public class HostsHeartbeatCheck extends AbstractCheckDescriptor {
         case UNKNOWN:
           if (maintenanceState == MaintenanceState.OFF) {
             prerequisiteCheck.getFailedOn().add(host.getHostName());
+
+            prerequisiteCheck.getFailedDetail().add(
+                new HostDetail(host.getHostId(), host.getHostName()));
           }
           break;
         default:

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/HostsMasterMaintenanceCheck.java

@@ -104,6 +104,9 @@ public class HostsMasterMaintenanceCheck extends AbstractCheckDescriptor {
       final Host host = hostEntry.getValue();
       if (host.getMaintenanceState(cluster.getClusterId()) == MaintenanceState.ON && hostsWithMasterComponent.contains(host.getHostName())) {
         prerequisiteCheck.getFailedOn().add(host.getHostName());
+
+        prerequisiteCheck.getFailedDetail().add(
+            new HostDetail(host.getHostId(), host.getHostName()));
       }
     }
 

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/HostsRepositoryVersionCheck.java

@@ -82,6 +82,9 @@ public class HostsRepositoryVersionCheck extends AbstractCheckDescriptor {
 
       if (hostVersion == null || !okStates.contains(hostVersion.getState())) {
         prerequisiteCheck.getFailedOn().add(host.getHostName());
+
+        prerequisiteCheck.getFailedDetail().add(
+            new HostDetail(host.getHostId(), host.getHostName()));
       }
     }
 

+ 13 - 4
ambari-server/src/main/java/org/apache/ambari/server/checks/InstallPackagesCheck.java

@@ -18,9 +18,10 @@
 package org.apache.ambari.server.checks;
 
 import java.text.MessageFormat;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.PrereqCheckRequest;
@@ -73,14 +74,15 @@ public class InstallPackagesCheck extends AbstractCheckDescriptor {
       return;
     }
 
-    final Set<String> failedHosts = new HashSet<>();
+    final Set<HostDetail> failedHosts = new TreeSet<>();
 
     for (Host host : cluster.getHosts()) {
       if (host.getMaintenanceState(cluster.getClusterId()) != MaintenanceState.ON) {
         for (HostVersionEntity hve : hostVersionDaoProvider.get().findByHost(host.getHostName())) {
           if (StringUtils.equals(hve.getRepositoryVersion().getVersion(), repositoryVersion.getVersion())
               && hve.getState() == RepositoryVersionState.INSTALL_FAILED) {
-            failedHosts.add(host.getHostName());
+
+            failedHosts.add(new HostDetail(host.getHostId(), host.getHostName()));
           }
         }
       }
@@ -92,9 +94,16 @@ public class InstallPackagesCheck extends AbstractCheckDescriptor {
               "in Maintenance mode: {4}", cluster.getClusterName(), targetStackId.getStackName(),
           targetStackId.getStackVersion(), repositoryVersion.getVersion(),
           StringUtils.join(failedHosts, ", "));
-      prerequisiteCheck.setFailedOn(new LinkedHashSet<>(failedHosts));
+
+      LinkedHashSet<String> failedHostNames = failedHosts.stream().map(
+          failedHost -> failedHost.hostName).collect(
+              Collectors.toCollection(LinkedHashSet::new));
+
+      prerequisiteCheck.setFailedOn(failedHostNames);
       prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
       prerequisiteCheck.setFailReason(message);
+      prerequisiteCheck.getFailedDetail().addAll(failedHosts);
+
       return;
     }
 

+ 8 - 0
ambari-server/src/main/java/org/apache/ambari/server/checks/RequiredServicesInRepositoryCheck.java

@@ -19,6 +19,8 @@ package org.apache.ambari.server.checks;
 
 import java.util.LinkedHashSet;
 import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.PrereqCheckRequest;
@@ -77,6 +79,12 @@ public class RequiredServicesInRepositoryCheck extends AbstractCheckDescriptor {
       prerequisiteCheck.setFailedOn(new LinkedHashSet<>(missingDependencies));
       prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
       prerequisiteCheck.setFailReason(String.format(failReasonTemplate, message));
+
+      Set<ServiceDetail> missingServiceDetails = missingDependencies.stream().map(
+          missingService -> new ServiceDetail(missingService)).collect(
+              Collectors.toCollection(TreeSet::new));
+
+      prerequisiteCheck.getFailedDetail().addAll(missingServiceDetails);
       return;
     }
 

+ 76 - 4
ambari-server/src/main/java/org/apache/ambari/server/checks/ServiceCheckValidityCheck.java

@@ -25,6 +25,10 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.PrereqCheckRequest;
@@ -42,6 +46,7 @@ import org.apache.ambari.server.state.stack.PrereqCheckStatus;
 import org.apache.ambari.server.state.stack.PrerequisiteCheck;
 import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
 import org.apache.commons.lang.StringUtils;
+import org.codehaus.jackson.annotate.JsonProperty;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -118,7 +123,7 @@ public class ServiceCheckValidityCheck extends AbstractCheckDescriptor {
       lastServiceChecksByRole.put(lastServiceCheck.role, lastServiceCheck.endTime);
     }
 
-    LinkedHashSet<String> failedServiceNames = new LinkedHashSet<>();
+    LinkedHashSet<ServiceCheckConfigDetail> failures = new LinkedHashSet<>();
 
     // for every service, see if there was a service check executed and then
     for( Entry<String, Long> entry : lastServiceConfigUpdates.entrySet() ) {
@@ -128,13 +133,15 @@ public class ServiceCheckValidityCheck extends AbstractCheckDescriptor {
 
       if(!lastServiceChecksByRole.containsKey(role) ) {
         LOG.info("There was no service check found for service {} matching role {}", serviceName, role);
-        failedServiceNames.add(serviceName);
+        failures.add(new ServiceCheckConfigDetail(serviceName, null, null));
         continue;
       }
 
       long lastServiceCheckTime = lastServiceChecksByRole.get(role);
       if (lastServiceCheckTime < configCreationTime) {
-        failedServiceNames.add(serviceName);
+        failures.add(
+            new ServiceCheckConfigDetail(serviceName, lastServiceCheckTime, configCreationTime));
+
         LOG.info(
             "The {} service (role {}) had its configurations updated on {}, but the last service check was {}",
             serviceName, role, DATE_FORMAT.format(new Date(configCreationTime)),
@@ -142,7 +149,12 @@ public class ServiceCheckValidityCheck extends AbstractCheckDescriptor {
       }
     }
 
-    if (!failedServiceNames.isEmpty()) {
+    if (!failures.isEmpty()) {
+      prerequisiteCheck.getFailedDetail().addAll(failures);
+
+      LinkedHashSet<String> failedServiceNames = failures.stream().map(
+          failure -> failure.serviceName).collect(Collectors.toCollection(LinkedHashSet::new));
+
       prerequisiteCheck.setFailedOn(failedServiceNames);
       prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
       String failReason = getFailReason(prerequisiteCheck, request);
@@ -160,4 +172,64 @@ public class ServiceCheckValidityCheck extends AbstractCheckDescriptor {
     return false;
   }
 
+  /**
+   * Used to represent information about a service component. This class is safe
+   * to use in sorted & unique collections.
+   */
+  static class ServiceCheckConfigDetail implements Comparable<ServiceCheckConfigDetail> {
+    @JsonProperty("service_name")
+    final String serviceName;
+
+    @JsonProperty("service_check_date")
+    final Long serviceCheckDate;
+
+    @JsonProperty("configuration_date")
+    final Long configurationDate;
+
+    ServiceCheckConfigDetail(String serviceName, @Nullable Long serviceCheckDate,
+        @Nullable Long configurationDate) {
+      this.serviceName = serviceName;
+      this.serviceCheckDate = serviceCheckDate;
+      this.configurationDate = configurationDate;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+      return Objects.hash(serviceName, serviceCheckDate, configurationDate);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+
+      if (obj == null) {
+        return false;
+      }
+
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+
+      ServiceCheckConfigDetail other = (ServiceCheckConfigDetail) obj;
+      return Objects.equals(serviceName, other.serviceName)
+          && Objects.equals(serviceCheckDate, other.serviceCheckDate)
+          && Objects.equals(configurationDate, other.configurationDate);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int compareTo(ServiceCheckConfigDetail other) {
+      return serviceName.compareTo(other.serviceName);
+    }
+  }
 }

+ 11 - 5
ambari-server/src/main/java/org/apache/ambari/server/checks/ServicesUpCheck.java

@@ -19,11 +19,11 @@ package org.apache.ambari.server.checks;
 
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.controller.PrereqCheckRequest;
@@ -89,7 +89,7 @@ public class ServicesUpCheck extends AbstractCheckDescriptor {
     final String clusterName = request.getClusterName();
     final Cluster cluster = clustersProvider.get().getCluster(clusterName);
     List<String> errorMessages = new ArrayList<>();
-    Set<String> failedServiceNames = new HashSet<>();
+    LinkedHashSet<ServiceDetail> failedServices = new LinkedHashSet<>();
 
     Set<String> servicesInUpgrade = getServicesInUpgrade(request);
     for (String serviceName : servicesInUpgrade) {
@@ -160,7 +160,8 @@ public class ServicesUpCheck extends AbstractCheckDescriptor {
           }
 
           if ((float) down / total > SLAVE_THRESHOLD) { // arbitrary
-            failedServiceNames.add(service.getName());
+            failedServices.add(new ServiceDetail(serviceName));
+
             String message = MessageFormat.format(
                 "{0}: {1} out of {2} {3} are started; there should be {4,number,percent} started before upgrading.",
                 service.getName(), up, total, serviceComponent.getName(), SLAVE_THRESHOLD);
@@ -169,7 +170,8 @@ public class ServicesUpCheck extends AbstractCheckDescriptor {
         } else {
           for (HostComponentSummary summary : hostComponentSummaries) {
             if (isConsideredDown(cluster, serviceComponent, summary)) {
-              failedServiceNames.add(service.getName());
+              failedServices.add(new ServiceDetail(serviceName));
+
               String message = MessageFormat.format("{0}: {1} (in {2} on host {3})",
                   service.getName(), serviceComponent.getName(), summary.getCurrentState(),
                   summary.getHostName());
@@ -182,7 +184,11 @@ public class ServicesUpCheck extends AbstractCheckDescriptor {
     }
 
     if (!errorMessages.isEmpty()) {
-      prerequisiteCheck.setFailedOn(new LinkedHashSet<>(failedServiceNames));
+      prerequisiteCheck.setFailedOn(
+          failedServices.stream().map(failedService -> failedService.serviceName).collect(
+              Collectors.toCollection(LinkedHashSet::new)));
+
+      prerequisiteCheck.getFailedDetail().addAll(failedServices);
       prerequisiteCheck.setStatus(PrereqCheckStatus.FAIL);
       prerequisiteCheck.setFailReason(
           "The following Service Components should be in a started state.  Please invoke a service Stop and full Start and try again. "

+ 17 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/models/HostComponentSummary.java

@@ -29,16 +29,33 @@ import org.apache.ambari.server.orm.entities.HostComponentDesiredStateEntity;
 import org.apache.ambari.server.orm.entities.HostComponentStateEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.state.State;
+import org.codehaus.jackson.annotate.JsonProperty;
 
 import com.google.inject.Inject;
 
+/**
+ * The {@link HostComponentSum1mary} class provides a concise representation of
+ * the state of a component on a given host. Some of its fields are serializable
+ * to JSON.
+ */
 @StaticallyInject
 public class HostComponentSummary {
+  @JsonProperty("service_name")
   private String serviceName;
+
+  @JsonProperty("component_name")
   private String componentName;
+
+  @JsonProperty("host_id")
   private Long hostId;
+
+  @JsonProperty("host_name")
   private String hostName;
+
+  @JsonProperty("desired_state")
   private State desiredState;
+
+  @JsonProperty("current_state")
   private State currentState;
 
   @Inject

+ 1 - 1
ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml

@@ -20,7 +20,7 @@
              xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                     http://www.springframework.org/schema/security
-                    http://www.springframework.org/schema/security/spring-security-3.1.xsd">
+                    http://www.springframework.org/schema/security/spring-security-3.2.xsd">
 
   <http use-expressions="true"
         disable-url-rewriting="true" entry-point-ref="ambariEntryPoint">

+ 0 - 42
ambari-server/src/test/java/org/apache/ambari/server/checks/AtlasPresenceCheckTest.java

@@ -1,42 +0,0 @@
-/*
- * 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.checks;
-
-import static org.junit.Assert.assertEquals;
-
-import org.apache.ambari.server.controller.PrereqCheckRequest;
-import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
-import org.apache.ambari.server.state.stack.PrereqCheckStatus;
-import org.apache.ambari.server.state.stack.PrerequisiteCheck;
-import org.junit.Test;
-import org.mockito.Mockito;
-
-
-public class AtlasPresenceCheckTest {
-  private final AtlasPresenceCheck m_check = new AtlasPresenceCheck();
-
-  @Test
-  public void perform() throws Exception {
-    PrerequisiteCheck check = new PrerequisiteCheck(null, null);
-    PrereqCheckRequest request = new PrereqCheckRequest("cluster");
-    request.setTargetRepositoryVersion(Mockito.mock(RepositoryVersionEntity.class));
-    m_check.perform(check, request);
-
-    assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
-  }
-}

+ 6 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/ComponentExistsInRepoCheckTest.java

@@ -183,6 +183,7 @@ public class ComponentExistsInRepoCheckTest extends EasyMockSupport {
     m_check.perform(check, request);
 
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
     Assert.assertTrue(StringUtils.isBlank(check.getFailReason()));
   }
 
@@ -220,6 +221,7 @@ public class ComponentExistsInRepoCheckTest extends EasyMockSupport {
     m_check.perform(check, request);
 
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
     Assert.assertTrue(StringUtils.isBlank(check.getFailReason()));
   }
 
@@ -246,6 +248,7 @@ public class ComponentExistsInRepoCheckTest extends EasyMockSupport {
     m_check.perform(check, request);
 
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertEquals(1, check.getFailedDetail().size());
     Assert.assertTrue(check.getFailedOn().contains("ZOOKEEPER"));
   }
 
@@ -274,6 +277,7 @@ public class ComponentExistsInRepoCheckTest extends EasyMockSupport {
     m_check.perform(check, request);
 
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertEquals(1, check.getFailedDetail().size());
     Assert.assertTrue(check.getFailedOn().contains("ZOOKEEPER_SERVER"));
   }
 
@@ -308,6 +312,7 @@ public class ComponentExistsInRepoCheckTest extends EasyMockSupport {
     m_check.perform(check, request);
 
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertEquals(1, check.getFailedDetail().size());
     Assert.assertTrue(check.getFailedOn().contains("FOO_SERVICE"));
   }
 
@@ -348,6 +353,7 @@ public class ComponentExistsInRepoCheckTest extends EasyMockSupport {
     m_check.perform(check, request);
 
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertEquals(1, check.getFailedDetail().size());
     Assert.assertTrue(check.getFailedOn().contains("FOO_COMPONENT"));
   }
 

+ 5 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/ComponentsInstallationCheckTest.java

@@ -293,6 +293,7 @@ public class ComponentsInstallationCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, null);
     componentsInstallationCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 2. Ensure that AMS is ignored even if their current state is not INSTALLED
     Mockito.when(hcsMetricsCollector.getCurrentState()).thenReturn(State.INSTALL_FAILED);
@@ -300,6 +301,7 @@ public class ComponentsInstallationCheckTest {
     check = new PrerequisiteCheck(null, null);
     componentsInstallationCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 3: Change TEZ client state to INSTALL_FAILED, should fail
     Mockito.when(hcsTezClient.getCurrentState()).thenReturn(State.INSTALL_FAILED);
@@ -307,6 +309,7 @@ public class ComponentsInstallationCheckTest {
     componentsInstallationCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
     Assert.assertTrue(check.getFailReason().indexOf("Service components in INSTALL_FAILED state") > -1);
+    Assert.assertEquals(1, check.getFailedDetail().size());
 
     // Case 4: Change TEZ client state to INSTALL_FAILED and place TEZ in Maintenance mode, should succeed
     Mockito.when(tezService.getMaintenanceState()).thenReturn(MaintenanceState.ON);
@@ -314,6 +317,7 @@ public class ComponentsInstallationCheckTest {
     check = new PrerequisiteCheck(null, null);
     componentsInstallationCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 5: Change TEZ client state to INSTALL_FAILED and place host2 in Maintenance mode, should succeed
     Mockito.when(tezService.getMaintenanceState()).thenReturn(MaintenanceState.OFF);
@@ -322,5 +326,6 @@ public class ComponentsInstallationCheckTest {
     check = new PrerequisiteCheck(null, null);
     componentsInstallationCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
   }
 }

+ 2 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/HealthCheckTest.java

@@ -82,6 +82,7 @@ public class HealthCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
     healthCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
   }
 
   @Test
@@ -112,5 +113,6 @@ public class HealthCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
     healthCheck.perform(check, new PrereqCheckRequest(CLUSTER_NAME));
     Assert.assertEquals(PrereqCheckStatus.WARNING, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
   }
 }

+ 2 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/HostMaintenanceModeCheckTest.java

@@ -123,12 +123,14 @@ public class HostMaintenanceModeCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, null);
     hostMaintenanceModeCheck.perform(check, new PrereqCheckRequest("cluster"));
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // put a host into MM in order to trigger the warning
     check = new PrerequisiteCheck(null, null);
     Mockito.when(host3.getMaintenanceState(1L)).thenReturn(MaintenanceState.ON);
     hostMaintenanceModeCheck.perform(check, new PrereqCheckRequest("cluster", UpgradeType.HOST_ORDERED));
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
     Assert.assertEquals("The following hosts cannot be in Maintenance Mode: h3.", check.getFailReason());
   }
 }

+ 3 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/HostsHeartbeatCheckTest.java

@@ -120,12 +120,14 @@ public class HostsHeartbeatCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, null);
     hostHeartbeatCheck.perform(check, new PrereqCheckRequest("cluster"));
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
 
     // put the unhealthy host into MM which will allow this check to pass
     check = new PrerequisiteCheck(null, null);
     Mockito.when(host3.getMaintenanceState(1L)).thenReturn(MaintenanceState.ON);
     hostHeartbeatCheck.perform(check, new PrereqCheckRequest("cluster"));
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // make the host healthy and take it out of MM to produce a PASS result
     Mockito.when(status3.getHealthStatus()).thenReturn(HealthStatus.HEALTHY);
@@ -133,5 +135,6 @@ public class HostsHeartbeatCheckTest {
     Mockito.when(host3.getMaintenanceState(1L)).thenReturn(MaintenanceState.OFF);
     hostHeartbeatCheck.perform(check, new PrereqCheckRequest("cluster"));
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
   }
 }

+ 2 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/InstallPackagesCheckTest.java

@@ -170,6 +170,7 @@ public class InstallPackagesCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, null);
     installPackagesCheck.perform(check, checkRequest);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 2: Install Packages failed on host1
     Mockito.when(hostVersionEntities.get(0).getState()).thenReturn(RepositoryVersionState.INSTALL_FAILED);
@@ -178,5 +179,6 @@ public class InstallPackagesCheckTest {
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
     Assert.assertNotNull(check.getFailedOn());
     Assert.assertTrue(check.getFailedOn().contains("host1"));
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
   }
 }

+ 2 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/RequiredServicesInRepositoryCheckTest.java

@@ -92,6 +92,7 @@ public class RequiredServicesInRepositoryCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
     m_requiredServicesCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
   }
 
   /**
@@ -109,5 +110,6 @@ public class RequiredServicesInRepositoryCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, CLUSTER_NAME);
     m_requiredServicesCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
   }
 }

+ 9 - 0
ambari-server/src/test/java/org/apache/ambari/server/checks/ServicesUpCheckTest.java

@@ -296,6 +296,7 @@ public class ServicesUpCheckTest {
     PrerequisiteCheck check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 2. Change some desired states to STARTED, should still pass
     Mockito.when(hcsNameNode.getDesiredState()).thenReturn(State.STARTED);
@@ -304,6 +305,7 @@ public class ServicesUpCheckTest {
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 3. Ensure that ZKFC and AMS are ignored even if their current state is not STARTED
     Mockito.when(hcsZKFC.getCurrentState()).thenReturn(State.INSTALLED);
@@ -313,6 +315,7 @@ public class ServicesUpCheckTest {
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 4. Change HDFS current states to INSTALLED, should fail.
     Mockito.when(hcsNameNode.getCurrentState()).thenReturn(State.INSTALLED);
@@ -321,6 +324,7 @@ public class ServicesUpCheckTest {
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
 
     // Case 5. Change HDFS master to STARTED, but one slave to INSTALLED, should pass (2/3 are up).
     Mockito.when(hcsNameNode.getCurrentState()).thenReturn(State.STARTED);
@@ -328,6 +332,7 @@ public class ServicesUpCheckTest {
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // Case 6. Change HDFS master to STARTED, but 2 slaves to INSTALLED, should fail (2/3 are down)
     Mockito.when(hcsNameNode.getCurrentState()).thenReturn(State.STARTED);
@@ -337,6 +342,7 @@ public class ServicesUpCheckTest {
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
     Assert.assertTrue(check.getFailReason().indexOf("50%") > -1);
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
 
     // place the DN slaves into MM which will allow them to be skipped
     Mockito.when(host1.getMaintenanceState(Mockito.anyLong())).thenReturn(MaintenanceState.ON);
@@ -344,6 +350,7 @@ public class ServicesUpCheckTest {
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.PASS, check.getStatus());
+    Assert.assertTrue(check.getFailedDetail().isEmpty());
 
     // put everything back to normal, then fail NN
     Mockito.when(hcsNameNode.getCurrentState()).thenReturn(State.INSTALLED);
@@ -355,11 +362,13 @@ public class ServicesUpCheckTest {
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
 
     // put NN into MM; should still fail since it's a master
     Mockito.when(host1.getMaintenanceState(Mockito.anyLong())).thenReturn(MaintenanceState.ON);
     check = new PrerequisiteCheck(null, null);
     servicesUpCheck.perform(check, request);
     Assert.assertEquals(PrereqCheckStatus.FAIL, check.getStatus());
+    Assert.assertFalse(check.getFailedDetail().isEmpty());
   }
 }

+ 2 - 2
contrib/ambari-scom/ambari-scom-server/src/main/resources/META-INF/spring-security.xml

@@ -20,7 +20,7 @@
              xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                     http://www.springframework.org/schema/security
-                    http://www.springframework.org/schema/security/spring-security-3.1.xsd">
+                    http://www.springframework.org/schema/security/spring-security-3.2.xsd">
 
     <http use-expressions="true"
           disable-url-rewriting="true" entry-point-ref="ambariEntryPoint">
@@ -43,4 +43,4 @@
 
     <beans:bean id="ambariEntryPoint" class="org.apache.ambari.server.security.AmbariEntryPoint">
     </beans:bean>
-</beans:beans>
+</beans:beans>