Browse Source

[AMBARI-24052] Add Kerberos-related configuration recommendations to the stack advisor

* [AMBARI-24052] Add Kerberos-related configuration recommendations to the stack advisor

* [AMBARI-24052] Add Kerberos-related configuration recommendations to the stack advisor
Robert Levas 7 years ago
parent
commit
d27b445454
14 changed files with 205 additions and 163 deletions
  1. 6 3
      ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java
  2. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
  3. 16 7
      ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java
  4. 0 121
      ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/SingleSignOnConfigurationRecommendationCommand.java
  5. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandType.java
  6. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
  7. 6 0
      ambari-server/src/main/resources/scripts/stack_advisor.py
  8. 4 0
      ambari-server/src/main/resources/stacks/HDP/2.6/services/stack_advisor.py
  9. 8 0
      ambari-server/src/main/resources/stacks/service_advisor.py
  10. 99 17
      ambari-server/src/main/resources/stacks/stack_advisor.py
  11. 43 4
      ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java
  12. 16 9
      ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequestTypeTest.java
  13. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java
  14. 2 0
      ambari-serviceadvisor/src/main/java/org/apache/ambari/serviceadvisor/ServiceAdvisorCommandType.java

+ 6 - 3
ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelper.java

@@ -29,8 +29,8 @@ import org.apache.ambari.server.api.services.stackadvisor.commands.ComponentLayo
 import org.apache.ambari.server.api.services.stackadvisor.commands.ConfigurationDependenciesRecommendationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.ConfigurationRecommendationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.ConfigurationValidationCommand;
-import org.apache.ambari.server.api.services.stackadvisor.commands.SingleSignOnConfigurationRecommendationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand;
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommandType;
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
 import org.apache.ambari.server.configuration.Configuration;
@@ -144,10 +144,13 @@ public class StackAdvisorHelper {
       command = new ComponentLayoutRecommendationCommand(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
           requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
     } else if (requestType == StackAdvisorRequestType.CONFIGURATIONS) {
-      command = new ConfigurationRecommendationCommand(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
+      command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
           requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
     } else if (requestType == StackAdvisorRequestType.SSO_CONFIGURATIONS) {
-      command = new SingleSignOnConfigurationRecommendationCommand(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
+      command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_SSO, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
+          requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+    } else if (requestType == StackAdvisorRequestType.KERBEROS_CONFIGURATIONS) {
+      command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_KERBEROS, recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,
           requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
     } else if (requestType == StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES) {
       command = new ConfigurationDependenciesRecommendationCommand(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType,

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java

@@ -229,6 +229,7 @@ public class StackAdvisorRequest {
     HOST_GROUPS("host_groups"),
     CONFIGURATIONS("configurations"),
     SSO_CONFIGURATIONS("sso-configurations"),
+    KERBEROS_CONFIGURATIONS("kerberos-configurations"),
     CONFIGURATION_DEPENDENCIES("configuration-dependencies");
 
     private String type;

+ 16 - 7
ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommand.java

@@ -34,15 +34,24 @@ import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRunner;
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
 import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.commons.collections.CollectionUtils;
 
 /**
  * {@link org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand} implementation for
  * configuration recommendation.
+ * <p>
+ * One of the following recommandataion types is indicated:
+ * <ul>
+ * <li>RECOMMEND_CONFIGURATIONS</li>
+ * <li>RECOMMEND_CONFIGURATIONS_FOR_SSO</li>
+ * <li>RECOMMEND_CONFIGURATIONS_FOR_KERBEROS</li>
+ * </ul>
  */
-public class ConfigurationRecommendationCommand extends
-    StackAdvisorCommand<RecommendationResponse> {
+public class ConfigurationRecommendationCommand extends StackAdvisorCommand<RecommendationResponse> {
 
-  public ConfigurationRecommendationCommand(File recommendationsDir,
+  private final StackAdvisorCommandType commandType;
+
+  public ConfigurationRecommendationCommand(StackAdvisorCommandType commandType, File recommendationsDir,
                                             String recommendationsArtifactsLifetime,
                                             ServiceInfo.ServiceAdvisorType serviceAdvisorType,
                                             int requestId,
@@ -50,17 +59,17 @@ public class ConfigurationRecommendationCommand extends
                                             AmbariMetaInfo metaInfo,
                                             AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
     super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
+    this.commandType = commandType;
   }
 
   @Override
-  protected StackAdvisorCommandType getCommandType() {
-    return StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS;
+  public StackAdvisorCommandType getCommandType() {
+    return commandType;
   }
 
   @Override
   protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
-    if (request.getHosts() == null || request.getHosts().isEmpty() || request.getServices() == null
-        || request.getServices().isEmpty()) {
+    if (CollectionUtils.isEmpty(request.getHosts()) || CollectionUtils.isEmpty(request.getServices())) {
       throw new StackAdvisorException("Hosts and services must not be empty");
     }
   }

+ 0 - 121
ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/SingleSignOnConfigurationRecommendationCommand.java

@@ -1,121 +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.api.services.stackadvisor.commands;
-
-import static org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.BindingHostGroup;
-import static org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse.HostGroup;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.ambari.server.api.services.AmbariMetaInfo;
-import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorException;
-import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
-import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRunner;
-import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
-import org.apache.ambari.server.controller.internal.AmbariServerConfigurationHandler;
-import org.apache.ambari.server.state.ServiceInfo;
-
-/**
- * {@link SingleSignOnConfigurationRecommendationCommand} implementation for
- * single sign-on configuration recommendation.
- */
-public class SingleSignOnConfigurationRecommendationCommand extends
-    StackAdvisorCommand<RecommendationResponse> {
-
-  public SingleSignOnConfigurationRecommendationCommand(File recommendationsDir,
-                                                        String recommendationsArtifactsLifetime,
-                                                        ServiceInfo.ServiceAdvisorType serviceAdvisorType,
-                                                        int requestId,
-                                                        StackAdvisorRunner saRunner,
-                                                        AmbariMetaInfo metaInfo, AmbariServerConfigurationHandler ambariServerConfigurationHandler) {
-    super(recommendationsDir, recommendationsArtifactsLifetime, serviceAdvisorType, requestId, saRunner, metaInfo, ambariServerConfigurationHandler);
-  }
-
-  @Override
-  protected StackAdvisorCommandType getCommandType() {
-    return StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_SSO;
-  }
-
-  @Override
-  protected void validate(StackAdvisorRequest request) throws StackAdvisorException {
-    if (request.getHosts() == null || request.getHosts().isEmpty() || request.getServices() == null
-        || request.getServices().isEmpty()) {
-      throw new StackAdvisorException("Hosts and services must not be empty");
-    }
-  }
-
-  @Override
-  protected RecommendationResponse updateResponse(StackAdvisorRequest request, RecommendationResponse response) {
-    response.getRecommendations().getBlueprint().setHostGroups(processHostGroups(request));
-    response.getRecommendations().getBlueprintClusterBinding().setHostGroups(processHostGroupBindings(request));
-    return response;
-  }
-
-  protected Set<HostGroup> processHostGroups(StackAdvisorRequest request) {
-    Set<HostGroup> resultSet = new HashSet<>();
-    for (Map.Entry<String, Set<String>> componentHost : request.getHostComponents().entrySet()) {
-      String hostGroupName = componentHost.getKey();
-      Set<String> components = componentHost.getValue();
-      if (hostGroupName != null && components != null) {
-        HostGroup hostGroup = new HostGroup();
-        Set<Map<String, String>> componentsSet = new HashSet<>();
-        for (String component : components) {
-          Map<String, String> componentMap = new HashMap<>();
-          componentMap.put("name", component);
-          componentsSet.add(componentMap);
-        }
-        hostGroup.setComponents(componentsSet);
-        hostGroup.setName(hostGroupName);
-        resultSet.add(hostGroup);
-      }
-    }
-    return resultSet;
-  }
-
-  private Set<BindingHostGroup> processHostGroupBindings(StackAdvisorRequest request) {
-    Set<BindingHostGroup> resultSet = new HashSet<>();
-    for (Map.Entry<String, Set<String>> hostBinding : request.getHostGroupBindings().entrySet()) {
-      String hostGroupName = hostBinding.getKey();
-      Set<String> hosts = hostBinding.getValue();
-      if (hostGroupName != null && hosts != null) {
-        BindingHostGroup bindingHostGroup = new BindingHostGroup();
-        Set<Map<String, String>> hostsSet = new HashSet<>();
-        for (String host : hosts) {
-          Map<String, String> hostMap = new HashMap<>();
-          hostMap.put("name", host);
-          hostsSet.add(hostMap);
-        }
-        bindingHostGroup.setHosts(hostsSet);
-        bindingHostGroup.setName(hostGroupName);
-        resultSet.add(bindingHostGroup);
-      }
-    }
-    return resultSet;
-  }
-
-  @Override
-  protected String getResultFileName() {
-    return "configurations.json";
-  }
-
-}

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommandType.java

@@ -31,6 +31,8 @@ public enum StackAdvisorCommandType {
 
   RECOMMEND_CONFIGURATIONS_FOR_SSO("recommend-configurations-for-sso"),
 
+  RECOMMEND_CONFIGURATIONS_FOR_KERBEROS("recommend-configurations-for-kerberos"),
+
   RECOMMEND_CONFIGURATION_DEPENDENCIES("recommend-configuration-dependencies"),
 
   VALIDATE_CONFIGURATIONS("validate-configurations");

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java

@@ -757,7 +757,7 @@ public class KerberosHelperImpl implements KerberosHelper {
           .forHosts(hostNames)
           .withComponentHostsMap(cluster.getServiceComponentHostMap(null, services))
           .withConfigurations(requestConfigurations)
-          .ofType(StackAdvisorRequest.StackAdvisorRequestType.CONFIGURATIONS)
+          .ofType(StackAdvisorRequest.StackAdvisorRequestType.KERBEROS_CONFIGURATIONS)
           .build();
 
         try {

+ 6 - 0
ambari-server/src/main/resources/scripts/stack_advisor.py

@@ -27,6 +27,7 @@ RECOMMEND_COMPONENT_LAYOUT_ACTION = 'recommend-component-layout'
 VALIDATE_COMPONENT_LAYOUT_ACTION = 'validate-component-layout'
 RECOMMEND_CONFIGURATIONS = 'recommend-configurations'
 RECOMMEND_CONFIGURATIONS_FOR_SSO = 'recommend-configurations-for-sso'
+RECOMMEND_CONFIGURATIONS_FOR_KERBEROS = 'recommend-configurations-for-kerberos'
 RECOMMEND_CONFIGURATION_DEPENDENCIES = 'recommend-configuration-dependencies'
 VALIDATE_CONFIGURATIONS = 'validate-configurations'
 
@@ -34,6 +35,7 @@ ALL_ACTIONS = [RECOMMEND_COMPONENT_LAYOUT_ACTION,
                VALIDATE_COMPONENT_LAYOUT_ACTION,
                RECOMMEND_CONFIGURATIONS,
                RECOMMEND_CONFIGURATIONS_FOR_SSO,
+               RECOMMEND_CONFIGURATIONS_FOR_KERBEROS,
                RECOMMEND_CONFIGURATION_DEPENDENCIES,
                VALIDATE_CONFIGURATIONS]
 USAGE = "Usage: <action> <hosts_file> <services_file>\nPossible actions are: {0}\n".format( str(ALL_ACTIONS) )
@@ -123,6 +125,10 @@ def main(argv=None):
     services[ADVISOR_CONTEXT] = {CALL_TYPE : 'recommendConfigurationsForSSO'}
     result = stackAdvisor.recommendConfigurationsForSSO(services, hosts)
     result_file = os.path.join(actionDir, "configurations.json")
+  elif action == RECOMMEND_CONFIGURATIONS_FOR_KERBEROS:
+    services[ADVISOR_CONTEXT] = {CALL_TYPE : 'recommendConfigurationsForKerberos'}
+    result = stackAdvisor.recommendConfigurationsForKerberos(services, hosts)
+    result_file = os.path.join(actionDir, "configurations.json")
   elif action == RECOMMEND_CONFIGURATION_DEPENDENCIES:
     services[ADVISOR_CONTEXT] = {CALL_TYPE : 'recommendConfigurationDependencies'}
     result = stackAdvisor.recommendConfigurationDependencies(services, hosts)

+ 4 - 0
ambari-server/src/main/resources/stacks/HDP/2.6/services/stack_advisor.py

@@ -54,6 +54,10 @@ class HDP26StackAdvisor(HDP25StackAdvisor):
         "RANGER": self.recommendRangerConfigurationsForSSO
       }
 
+  def getServiceConfigurationRecommenderForKerberosDict(self):
+    # For backwards compatibility, return the dict use for general stack advisor calls.
+    return self.getServiceConfigurationRecommenderDict()
+
   def recommendSPARK2Configurations(self, configurations, clusterData, services, hosts):
     """
     :type configurations dict

+ 8 - 0
ambari-server/src/main/resources/stacks/service_advisor.py

@@ -99,6 +99,14 @@ class ServiceAdvisor(DefaultStackAdvisor):
     """
     pass
 
+  def getServiceConfigurationRecommendationsForKerberos(self, configurations, clusterSummary, services, hosts):
+    """
+    Any Kerberos-related configuration recommendations for the service should be defined in this function.
+
+    Redirect to getServiceConfigurationRecommendations for backward compatibility
+    """
+    return self.getServiceConfigurationRecommendations(configurations, clusterSummary, services, hosts)
+
   def getServiceComponentLayoutValidations(self, services, hosts):
     """
     Returns an array of Validation objects about issues with the hostnames to which components are assigned.

+ 99 - 17
ambari-server/src/main/resources/stacks/stack_advisor.py

@@ -341,6 +341,26 @@ class StackAdvisor(object):
     """
     pass
 
+  def recommendConfigurationsForKerberos(self, services, hosts):
+    """
+    Returns recommendation of Kerberos-related service configurations based on host-specific layout
+    of components.
+
+    This function takes as input all details about services being installed, and hosts
+    they are being installed into, to recommend host-specific configurations.
+
+    For backwards compatibility, this function redirects to recommendConfigurations. Implementations
+    should override this function to recommend Kerberos-specific property changes.
+
+    :type services: dict
+    :param services: Dictionary containing all information about services and component layout selected by the user.
+    :type hosts: dict
+    :param hosts: Dictionary containing all information about hosts in this cluster
+    :rtype: dict
+    :return: Layout recommendation of service components on cluster hosts in Ambari Blueprints friendly format.
+    """
+    return self.recommendConfigurations(services, hosts)
+
   def validateConfigurations(self, services, hosts):
     """"
     Returns array of Validation issues with configurations provided by user
@@ -1124,15 +1144,12 @@ class DefaultStackAdvisor(StackAdvisor):
 
   def calculateYarnAllocationSizes(self, configurations, services, hosts):
     # initialize data
-    servicesList = [service["StackServices"]["service_name"] for service in services["services"]]
-    components = [component["StackServiceComponents"]["component_name"]
-                  for service in services["services"]
-                  for component in service["components"]]
+    servicesList, componentsList = self.get_service_and_component_lists(services["services"])
     putYarnProperty = self.putProperty(configurations, "yarn-site", services)
     putYarnPropertyAttribute = self.putPropertyAttribute(configurations, "yarn-site")
 
     # calculate memory properties and get cluster data dictionary with whole information
-    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, components, services)
+    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, componentsList, services)
 
     # executing code from stack advisor HDP 206
     nodemanagerMinRam = 1048576 # 1TB in mb
@@ -1580,12 +1597,9 @@ class DefaultStackAdvisor(StackAdvisor):
     stackName = services["Versions"]["stack_name"]
     stackVersion = services["Versions"]["stack_version"]
     hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]]
-    servicesList = [service["StackServices"]["service_name"] for service in services["services"]]
-    components = [component["StackServiceComponents"]["component_name"]
-                  for service in services["services"]
-                  for component in service["components"]]
+    servicesList, componentsList = self.get_service_and_component_lists(services["services"])
 
-    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, components, services)
+    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, componentsList, services)
 
     recommendations = {
       "Versions": {"stack_name": stackName, "stack_version": stackVersion},
@@ -1604,7 +1618,7 @@ class DefaultStackAdvisor(StackAdvisor):
 
     # If recommendation for config groups
     if "config-groups" in services:
-      self.recommendConfigGroupsConfigurations(recommendations, services, components, hosts,
+      self.recommendConfigGroupsConfigurations(recommendations, services, componentsList, hosts,
                                  servicesList)
     else:
       configurations = recommendations["recommendations"]["blueprint"]["configurations"]
@@ -1632,12 +1646,9 @@ class DefaultStackAdvisor(StackAdvisor):
     stackName = services["Versions"]["stack_name"]
     stackVersion = services["Versions"]["stack_version"]
     hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]]
-    servicesList = [service["StackServices"]["service_name"] for service in services["services"]]
-    components = [component["StackServiceComponents"]["component_name"]
-                  for service in services["services"]
-                  for component in service["components"]]
+    servicesList, componentsList = self.get_service_and_component_lists(services["services"])
 
-    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, components, services)
+    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, componentsList, services)
 
     recommendations = {
       "Versions": {"stack_name": stackName, "stack_version": stackVersion},
@@ -1656,7 +1667,7 @@ class DefaultStackAdvisor(StackAdvisor):
 
     # If recommendation for config groups
     if "config-groups" in services:
-      self.recommendConfigGroupsConfigurations(recommendations, services, components, hosts,
+      self.recommendConfigGroupsConfigurations(recommendations, services, componentsList, hosts,
                                  servicesList)
     else:
       configurations = recommendations["recommendations"]["blueprint"]["configurations"]
@@ -1679,6 +1690,60 @@ class DefaultStackAdvisor(StackAdvisor):
 
     return recommendations
 
+  def recommendConfigurationsForKerberos(self, services, hosts):
+    self.services = services
+
+    stackName = services["Versions"]["stack_name"]
+    stackVersion = services["Versions"]["stack_version"]
+    hostsList = [host["Hosts"]["host_name"] for host in hosts["items"]]
+    servicesList, componentsList = self.get_service_and_component_lists(services["services"])
+
+    clusterSummary = self.getConfigurationClusterSummary(servicesList, hosts, componentsList, services)
+
+
+    recommendations = {
+      "Versions": {
+        "stack_name": stackName,
+        "stack_version": stackVersion
+      },
+      "hosts": hostsList,
+      "services": servicesList,
+      "recommendations": {
+        "blueprint": {
+          "configurations": {},
+          "host_groups": []
+        },
+        "blueprint_cluster_binding": {
+          "host_groups": []
+        }
+      }
+    }
+
+    # If recommendation for config groups
+    if "config-groups" in services:
+      self.recommendConfigGroupsConfigurations(recommendations, services, componentsList, hosts,
+                                 servicesList)
+    else:
+      configurations = recommendations["recommendations"]["blueprint"]["configurations"]
+
+      # there can be dependencies between service recommendations which require special ordering
+      # for now, make sure custom services (that have service advisors) run after standard ones
+      serviceAdvisors = []
+      recommenderDict = self.getServiceConfigurationRecommenderForKerberosDict()
+      for service in services["services"]:
+        serviceName = service["StackServices"]["service_name"]
+        calculation = recommenderDict.get(serviceName, None)
+        if calculation is not None:
+          calculation(configurations, clusterSummary, services, hosts)
+        else:
+          serviceAdvisor = self.getServiceAdvisor(serviceName)
+          if serviceAdvisor is not None:
+            serviceAdvisors.append(serviceAdvisor)
+      for serviceAdvisor in serviceAdvisors:
+        serviceAdvisor.getServiceConfigurationRecommendationsForKerberos(configurations, clusterSummary, services, hosts)
+
+    return recommendations
+
   def getServiceConfigurationRecommender(self, service):
     return self.getServiceConfigurationRecommenderDict().get(service, None)
 
@@ -1688,6 +1753,9 @@ class DefaultStackAdvisor(StackAdvisor):
   def getServiceConfigurationRecommenderForSSODict(self):
     return {}
 
+  def getServiceConfigurationRecommenderForKerberosDict(self):
+    return {}
+
   # Recommendation helper methods
   def isComponentHostsPopulated(self, component):
     hostnames = self.getComponentAttribute(component, "hostnames")
@@ -3171,3 +3239,17 @@ class DefaultStackAdvisor(StackAdvisor):
       return int(re.sub("\D", "", s))
     except ValueError:
       return None
+
+  def get_service_and_component_lists(self, services):
+    serviceList = []
+    componentList = []
+
+    if services:
+      for service in services:
+        serviceList.append(service["StackServices"]["service_name"])
+
+        if service["components"]:
+          for component in service["components"]:
+            componentList.append(component["StackServiceComponents"]["component_name"])
+
+    return serviceList, componentList

+ 43 - 4
ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorHelperTest.java

@@ -20,6 +20,7 @@ package org.apache.ambari.server.api.services.stackadvisor;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -33,8 +34,10 @@ import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.St
 import org.apache.ambari.server.api.services.stackadvisor.commands.ComponentLayoutRecommendationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.ComponentLayoutValidationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.ConfigurationDependenciesRecommendationCommand;
+import org.apache.ambari.server.api.services.stackadvisor.commands.ConfigurationRecommendationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.ConfigurationValidationCommand;
 import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommand;
+import org.apache.ambari.server.api.services.stackadvisor.commands.StackAdvisorCommandType;
 import org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.api.services.stackadvisor.validations.ValidationResponse;
 import org.apache.ambari.server.configuration.Configuration;
@@ -94,7 +97,7 @@ public class StackAdvisorHelperTest {
     doReturn(command).when(helper).createValidationCommand("ZOOKEEPER", request);
     helper.validate(request);
 
-    assertTrue(false);
+    fail();
   }
 
   @Test
@@ -144,7 +147,7 @@ public class StackAdvisorHelperTest {
     doReturn(command).when(helper).createRecommendationCommand("ZOOKEEPER", request);
     helper.recommend(request);
 
-    assertTrue(false);
+    fail("Expected StackAdvisorException to be thrown");
   }
 
   @Test
@@ -168,6 +171,21 @@ public class StackAdvisorHelperTest {
     assertEquals(ComponentLayoutRecommendationCommand.class, command.getClass());
   }
 
+  @Test
+  public void testCreateRecommendationCommand_returnsConfigurationRecommendationCommand() throws IOException, StackAdvisorException {
+    testCreateConfigurationRecommendationCommand(StackAdvisorRequestType.CONFIGURATIONS, StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS);
+  }
+
+  @Test
+  public void testCreateRecommendationCommand_returnsSingleSignOnConfigurationRecommendationCommand() throws IOException, StackAdvisorException {
+    testCreateConfigurationRecommendationCommand(StackAdvisorRequestType.SSO_CONFIGURATIONS, StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_SSO);
+  }
+
+  @Test
+  public void testCreateRecommendationCommand_returnsKerberosConfigurationRecommendationCommand() throws IOException, StackAdvisorException {
+    testCreateConfigurationRecommendationCommand(StackAdvisorRequestType.KERBEROS_CONFIGURATIONS, StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS_FOR_KERBEROS);
+  }
+
   @Test
   public void testCreateValidationCommand_returnsComponentLayoutValidationCommand()
       throws IOException, StackAdvisorException {
@@ -210,7 +228,7 @@ public class StackAdvisorHelperTest {
 
   @Test
   public void testCreateRecommendationDependencyCommand_returnsConfigurationDependencyRecommendationCommand()
-    throws IOException, StackAdvisorException {
+      throws IOException, StackAdvisorException {
     Configuration configuration = mock(Configuration.class);
     when(configuration.getRecommendationsArtifactsRolloverMax()).thenReturn(100);
     StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
@@ -218,7 +236,7 @@ public class StackAdvisorHelperTest {
     ServiceInfo service = mock(ServiceInfo.class);
     when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
     when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
-    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo,null);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
     StackAdvisorRequestType requestType = StackAdvisorRequestType.CONFIGURATION_DEPENDENCIES;
     StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
         .ofType(requestType).build();
@@ -228,6 +246,27 @@ public class StackAdvisorHelperTest {
     assertEquals(ConfigurationDependenciesRecommendationCommand.class, command.getClass());
   }
 
+  private void testCreateConfigurationRecommendationCommand(StackAdvisorRequestType requestType, StackAdvisorCommandType expectedCommandType)
+      throws IOException, StackAdvisorException {
+    Configuration configuration = mock(Configuration.class);
+    when(configuration.getRecommendationsArtifactsRolloverMax()).thenReturn(100);
+    StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
+    AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
+    ServiceInfo service = mock(ServiceInfo.class);
+    when(metaInfo.getService(Mockito.anyString(), Mockito.anyString(), Mockito.anyString())).thenReturn(service);
+    when(service.getServiceAdvisorType()).thenReturn(ServiceInfo.ServiceAdvisorType.PYTHON);
+    StackAdvisorHelper helper = new StackAdvisorHelper(configuration, saRunner, metaInfo, null);
+
+    StackAdvisorRequest request = StackAdvisorRequestBuilder.forStack("stackName", "stackVersion")
+        .ofType(requestType).build();
+
+    StackAdvisorCommand<RecommendationResponse> command = helper.createRecommendationCommand("ZOOKEEPER", request);
+
+    assertTrue(command instanceof ConfigurationRecommendationCommand);
+    assertEquals(expectedCommandType, ((ConfigurationRecommendationCommand) command).getCommandType());
+
+  }
+
   private static StackAdvisorHelper stackAdvisorHelperSpy(Configuration configuration, StackAdvisorRunner saRunner, AmbariMetaInfo metaInfo) throws IOException {
     return spy(new StackAdvisorHelper(configuration, saRunner, metaInfo, null));
   }

+ 16 - 9
ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequestTypeTest.java

@@ -19,7 +19,7 @@
 package org.apache.ambari.server.api.services.stackadvisor;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest.StackAdvisorRequestType;
 import org.junit.Test;
@@ -31,18 +31,22 @@ public class StackAdvisorRequestTypeTest {
 
   @Test
   public void testFromString_returnsHostGroupType() throws StackAdvisorException {
-    String text = "host_groups";
-    StackAdvisorRequestType type = StackAdvisorRequestType.fromString(text);
-
-    assertEquals(type, StackAdvisorRequestType.HOST_GROUPS);
+    testFromString("host_groups", StackAdvisorRequestType.HOST_GROUPS);
   }
 
   @Test
   public void testFromString_returnsConfigurationsType() throws StackAdvisorException {
-    String text = "configurations";
-    StackAdvisorRequestType type = StackAdvisorRequestType.fromString(text);
+    testFromString("configurations", StackAdvisorRequestType.CONFIGURATIONS);
+  }
 
-    assertEquals(type, StackAdvisorRequestType.CONFIGURATIONS);
+  @Test
+  public void testFromString_returnsSingleSignOnConfigurationsType() throws StackAdvisorException {
+    testFromString("sso-configurations", StackAdvisorRequestType.SSO_CONFIGURATIONS);
+  }
+
+  @Test
+  public void testFromString_returnsKerberosConfigurationsType() throws StackAdvisorException {
+    testFromString("kerberos-configurations", StackAdvisorRequestType.KERBEROS_CONFIGURATIONS);
   }
 
   @Test(expected = StackAdvisorException.class)
@@ -50,7 +54,10 @@ public class StackAdvisorRequestTypeTest {
     String text = "unknown_type";
     StackAdvisorRequestType.fromString(text);
 
-    assertTrue(false);
+    fail("Expected StackAdvisorException");
   }
 
+  private void testFromString(String text, StackAdvisorRequestType expectedType) throws StackAdvisorException {
+    assertEquals(expectedType, StackAdvisorRequestType.fromString(text));
+  }
 }

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/api/services/stackadvisor/commands/ConfigurationRecommendationCommandTest.java

@@ -44,7 +44,7 @@ public class ConfigurationRecommendationCommandTest {
     StackAdvisorRunner saRunner = mock(StackAdvisorRunner.class);
     File file = mock(File.class);
     AmbariMetaInfo metaInfo = mock(AmbariMetaInfo.class);
-    ConfigurationRecommendationCommand command = new ConfigurationRecommendationCommand(file, "1w", ServiceInfo.ServiceAdvisorType.PYTHON, 1, saRunner, metaInfo, null);
+    ConfigurationRecommendationCommand command = new ConfigurationRecommendationCommand(StackAdvisorCommandType.RECOMMEND_CONFIGURATIONS, file, "1w", ServiceInfo.ServiceAdvisorType.PYTHON, 1, saRunner, metaInfo, null);
 
     StackAdvisorRequest request = mock(StackAdvisorRequest.class);
     Map<String, Set<String>> componentHostGroupMap = new HashMap<>();

+ 2 - 0
ambari-serviceadvisor/src/main/java/org/apache/ambari/serviceadvisor/ServiceAdvisorCommandType.java

@@ -31,6 +31,8 @@ public enum ServiceAdvisorCommandType {
 
   RECOMMEND_CONFIGURATIONS_FOR_SSO("recommend-configurations-for-sso"),
 
+  RECOMMEND_CONFIGURATIONS_FOR_KERBEROS("recommend-configurations-for-kerberos"),
+
   RECOMMEND_CONFIGURATION_DEPENDENCIES("recommend-configuration-dependencies"),
 
   VALIDATE_CONFIGURATIONS("validate-configurations");