AMBARI-21602. Pre-configure services when Kerberos is enabled to reduce number 
of core service restarts when services are added (rlevas)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e6641558
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e6641558
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e6641558

Branch: refs/heads/branch-2.6
Commit: e6641558cfff0b06a8ad0c0a41610433f96f1335
Parents: e27a214
Author: Robert Levas <[email protected]>
Authored: Thu Aug 24 16:06:56 2017 -0400
Committer: Robert Levas <[email protected]>
Committed: Thu Aug 24 16:06:56 2017 -0400

----------------------------------------------------------------------
 .../server/api/services/AmbariMetaInfo.java     | 118 +--
 .../stackadvisor/StackAdvisorRequest.java       |  10 +-
 .../server/controller/KerberosHelper.java       | 116 ++-
 .../server/controller/KerberosHelperImpl.java   | 761 ++++++++++++++-----
 ...usterKerberosDescriptorResourceProvider.java |   3 +-
 .../internal/StackVersionResourceProvider.java  |  67 +-
 .../customactions/ActionDefinitionManager.java  |   4 +-
 .../AbstractPrepareKerberosServerAction.java    |   2 +-
 .../kerberos/KerberosServerAction.java          |   6 +
 .../kerberos/PreconfigureServiceType.java       |  46 ++
 .../PrepareDisableKerberosServerAction.java     |   2 +-
 .../PrepareEnableKerberosServerAction.java      |  40 +-
 .../PrepareKerberosIdentitiesServerAction.java  |  52 +-
 .../upgrades/FixAuthToLocalMappingAction.java   |   2 +-
 .../upgrades/UpgradeUserKerberosDescriptor.java |   4 +-
 .../server/stack/ConfigurationDirectory.java    |   5 +-
 .../ambari/server/stack/ExtensionDirectory.java |   3 +-
 .../ambari/server/stack/ServiceDirectory.java   |  16 +-
 .../ambari/server/stack/ServiceModule.java      |   6 +-
 .../ambari/server/stack/StackDirectory.java     |  75 +-
 .../ambari/server/stack/StackManager.java       |  13 +-
 .../apache/ambari/server/stack/StackModule.java |   9 +-
 .../ambari/server/state/ConfigHelper.java       |   3 +-
 .../apache/ambari/server/state/ServiceInfo.java |  13 +-
 .../apache/ambari/server/state/StackInfo.java   |  22 +-
 .../kerberos/AbstractKerberosDescriptor.java    |  34 +
 .../state/kerberos/KerberosDescriptor.java      |  46 +-
 .../kerberos/KerberosServiceDescriptor.java     |  86 ++-
 .../server/upgrade/AbstractUpgradeCatalog.java  |   2 +-
 .../1.10.3-10/configuration/kerberos-env.xml    |  27 +
 .../stacks/HDP/2.6/kerberos_preconfigure.json   |  24 +
 .../server/api/services/AmbariMetaInfoTest.java |  55 +-
 .../server/controller/KerberosHelperTest.java   | 717 ++++++++++++-----
 ...rKerberosDescriptorResourceProviderTest.java |  14 +-
 .../FixAuthToLocalMappingActionTest.java        |   2 +-
 .../UpgradeUserKerberosDescriptorTest.java      |   4 +-
 .../ambari/server/stack/ServiceModuleTest.java  |   3 +-
 .../KerberosDescriptorUpdateHelperTest.java     |   4 +-
 .../kerberos/KerberosServiceDescriptorTest.java |   2 +
 .../stacks/HDP/2.0.8/kerberos_preconfigure.json |  23 +
 40 files changed, 1759 insertions(+), 682 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e6641558/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
index 9afba8c..d03081f 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/AmbariMetaInfo.java
@@ -24,7 +24,6 @@ import static 
org.apache.ambari.server.controller.utilities.PropertyHelper.AGGRE
 
 import java.io.File;
 import java.io.FileReader;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
@@ -57,7 +56,6 @@ import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.dao.MetainfoDAO;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.orm.entities.MetainfoEntity;
-import org.apache.ambari.server.stack.StackDirectory;
 import org.apache.ambari.server.stack.StackManager;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.state.Cluster;
@@ -102,30 +100,6 @@ import com.google.inject.Singleton;
  */
 @Singleton
 public class AmbariMetaInfo {
-  public static final String SERVICE_CONFIG_FOLDER_NAME = "configuration";
-  public static final String SERVICE_PROPERTIES_FOLDER_NAME = "properties";
-  public static final String SERVICE_THEMES_FOLDER_NAME = "themes";
-  public static final String SERVICE_QUICKLINKS_CONFIGURATIONS_FOLDER_NAME = 
"quicklinks";
-  public static final String SERVICE_CONFIG_FILE_NAME_POSTFIX = ".xml";
-  public static final String RCO_FILE_NAME = "role_command_order.json";
-  public static final String SERVICE_METRIC_FILE_NAME = "metrics.json";
-  public static final String SERVICE_ALERT_FILE_NAME = "alerts.json";
-  public static final String SERVICE_ADVISOR_FILE_NAME = "service_advisor.py";
-
-  /**
-   * The filename for a Kerberos descriptor file at either the stack or 
service level
-   */
-  public static final String KERBEROS_DESCRIPTOR_FILE_NAME = "kerberos.json";
-
-  /**
-   * The filename for a Widgets descriptor file at either the stack or service 
level
-   */
-  public static final String WIDGETS_DESCRIPTOR_FILE_NAME = "widgets.json";
-
-  /**
-   * Filename for theme file at service layer
-   */
-  public static final String SERVICE_THEME_FILE_NAME = "theme.json";
 
   /**
    * This string is used in placeholder in places that are common for
@@ -136,13 +110,6 @@ public class AmbariMetaInfo {
    * Version of XML files with support of custom services and custom commands
    */
   public static final String SCHEMA_VERSION_2 = "2.0";
-  public static final FilenameFilter FILENAME_FILTER = new FilenameFilter() {
-    @Override
-    public boolean accept(File dir, String s) {
-      return !(s.equals(".svn") || s.equals(".git") ||
-          s.equals(StackDirectory.HOOKS_FOLDER_NAME));
-    }
-  };
   private final static Logger LOG = 
LoggerFactory.getLogger(AmbariMetaInfo.class);
 
   /**
@@ -1346,40 +1313,54 @@ public class AmbariMetaInfo {
    * All of the kerberos.json files from the specified stack (and version) are 
read, parsed and
    * complied into a complete Kerberos descriptor hierarchy.
    *
-   * @param stackName    a String declaring the stack name
-   * @param stackVersion a String declaring the stack version
+   * @param stackName               a String declaring the stack name
+   * @param stackVersion            a String declaring the stack version
+   * @param includePreconfigureData a Boolean value indicating whether to 
include the pre-configuration
+   *                                data (<code>true</code>), or not 
(<code>false</code>)
    * @return a new complete KerberosDescriptor, or null if no Kerberos 
descriptor information is available
    * @throws AmbariException if an error occurs reading or parsing the stack's 
kerberos.json files
    */
-  public KerberosDescriptor getKerberosDescriptor(String stackName, String 
stackVersion) throws AmbariException {
+  public KerberosDescriptor getKerberosDescriptor(String stackName, String 
stackVersion, boolean includePreconfigureData) throws AmbariException {
     StackInfo stackInfo = getStack(stackName, stackVersion);
 
-    String kerberosDescriptorFileLocation = 
stackInfo.getKerberosDescriptorFileLocation();
-
     KerberosDescriptor kerberosDescriptor = null;
 
-    // Read in the stack-level Kerberos descriptor
-    if (kerberosDescriptorFileLocation != null) {
-      File file = new File(kerberosDescriptorFileLocation);
+    // Read in the stack-level Kerberos descriptor pre-configuration data
+    if (includePreconfigureData) {
+      kerberosDescriptor = 
readKerberosDescriptorFromFile(stackInfo.getKerberosDescriptorPreConfigurationFileLocation());
 
-      if (file.canRead()) {
-        try {
-          kerberosDescriptor = kerberosDescriptorFactory.createInstance(file);
-        } catch (IOException e) {
-          throw new AmbariException(String.format("Failed to parse kerberos 
descriptor file %s",
-              file.getAbsolutePath()), e);
+      if(kerberosDescriptor != null) {
+        // Ensure the all services to be pre-configured are flagged 
appropriately.
+        Map<String, KerberosServiceDescriptor> serviceDescriptors = 
kerberosDescriptor.getServices();
+        if (serviceDescriptors != null) {
+          for (KerberosServiceDescriptor serviceDescriptor : 
serviceDescriptors.values()) {
+            serviceDescriptor.setPreconfigure(true);
+          }
         }
-      } else {
-        throw new AmbariException(String.format("Unable to read kerberos 
descriptor file %s",
-            file.getAbsolutePath()));
       }
     }
 
-    if (kerberosDescriptor == null) {
-      kerberosDescriptor = new KerberosDescriptor();
+    // Read in the base stack-level Kerberos descriptor.
+    KerberosDescriptor stackKerberosDescriptor = 
readKerberosDescriptorFromFile(stackInfo.getKerberosDescriptorFileLocation());
+    if(stackKerberosDescriptor == null) {
+      // If kerberosDescriptor is null and stackKerberosDescriptor is null, 
then ensure
+      // kerberosDescriptor is an empty KerberosDescriptor.
+      if(kerberosDescriptor == null) {
+        kerberosDescriptor = new KerberosDescriptor();
+      }
+    }
+    else {
+      if(kerberosDescriptor == null) {
+        // If kerberosDescriptor is null; then set it to 
stackKerberosDescriptor.
+        kerberosDescriptor = stackKerberosDescriptor;
+      }
+      else {
+        // If kerberosDescriptor is not null; then update it using 
stackKerberosDescriptor.
+        kerberosDescriptor.update(stackKerberosDescriptor);
+      }
     }
 
-    // Read in the service-level Kerberos descriptors
+    // Read in the individual service-level Kerberos descriptors
     Map<String, ServiceInfo> services = getServices(stackName, stackVersion);
 
     if (services != null) {
@@ -1388,6 +1369,9 @@ public class AmbariMetaInfo {
 
         if (serviceDescriptors != null) {
           for (KerberosServiceDescriptor serviceDescriptor : 
serviceDescriptors) {
+            // Add the service-level Kerberos descriptor to 
kerberosDescriptor. If the service was
+            // previously added (possible from the pre-configure data), update 
the existing service-level
+            // Kerberos descriptor with this one.
             kerberosDescriptor.putService(serviceDescriptor);
           }
         }
@@ -1495,5 +1479,33 @@ public class AmbariMetaInfo {
     return versionDefinitions;
   }
 
+  /**
+   * Reads a Kerberos descriptor from the specified file path.
+   *
+   * @param fileLocation the path to the file
+   * @return a KerberosDescriptor or <code>null</code>, if no path is specified
+   * @throws AmbariException if an error occurs reading or parsing the 
Kerberos descriptor file
+   */
+  KerberosDescriptor readKerberosDescriptorFromFile(String fileLocation) 
throws AmbariException {
+    if (!StringUtils.isEmpty(fileLocation)) {
+      File file = new File(fileLocation);
 
+      if (file.canRead()) {
+        try {
+          return kerberosDescriptorFactory.createInstance(file);
+        } catch (IOException e) {
+          throw new AmbariException(String.format("Failed to parse Kerberos 
descriptor file %s",
+              file.getAbsolutePath()), e);
+        }
+      } else {
+        throw new AmbariException(String.format("Unable to read Kerberos 
descriptor file %s",
+            file.getAbsolutePath()));
+      }
+    }
+    else {
+      LOG.debug("Missing path to Kerberos descriptor, returning null");
+    }
+
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e6641558/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
index c655816..64180e5 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/StackAdvisorRequest.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -20,6 +20,7 @@ package org.apache.ambari.server.api.services.stackadvisor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -28,7 +29,6 @@ import java.util.Set;
 
 import 
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
 import org.apache.ambari.server.state.ChangedConfigInfo;
-import org.apache.ambari.server.state.PropertyDependencyInfo;
 import org.apache.commons.lang.StringUtils;
 
 /**
@@ -40,7 +40,7 @@ public class StackAdvisorRequest {
   private String stackVersion;
   private StackAdvisorRequestType requestType;
   private List<String> hosts = new ArrayList<String>();
-  private List<String> services = new ArrayList<String>();
+  private Collection<String> services = new ArrayList<String>();
   private Map<String, Set<String>> componentHostsMap = new HashMap<String, 
Set<String>>();
   private Map<String, Set<String>> hostComponents = new HashMap<String, 
Set<String>>();
   private Map<String, Set<String>> hostGroupBindings = new HashMap<String, 
Set<String>>();
@@ -65,7 +65,7 @@ public class StackAdvisorRequest {
     return hosts;
   }
 
-  public List<String> getServices() {
+  public Collection<String> getServices() {
     return services;
   }
 
@@ -143,7 +143,7 @@ public class StackAdvisorRequest {
       return this;
     }
 
-    public StackAdvisorRequestBuilder forServices(List<String> services) {
+    public StackAdvisorRequestBuilder forServices(Collection<String> services) 
{
       this.instance.services = services;
       return this;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e6641558/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
index d000571..4066418 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
@@ -75,6 +75,10 @@ public interface KerberosHelper {
    */
   String AMBARI_SERVER_KERBEROS_IDENTITY_NAME = "ambari-server";
   /**
+   * The name of the Kerberos environment configuration type
+   */
+  String KERBEROS_ENV = "kerberos-env";
+  /**
    * The kerberos-env property name declaring whether Ambari should manage its 
own required
    * identities or not
    */
@@ -84,6 +88,32 @@ public interface KerberosHelper {
    * identities or not
    */
   String MANAGE_IDENTITIES = "manage_identities";
+  /**
+   * The kerberos-env property name declaring the default realm
+   */
+  String DEFAULT_REALM = "realm";
+  /**
+   * The kerberos-env property name declaring the kdc_type
+   *
+   * @see org.apache.ambari.server.serveraction.kerberos.KDCType
+   */
+  String KDC_TYPE = "kdc_type";
+  /**
+   * The kerberos-env property name declaring whether to manage auth-to-local 
rules or not
+   */
+  String MANAGE_AUTH_TO_LOCAL_RULES = "manage_auth_to_local";
+  /**
+   * The kerberos-env property name declaring whether auth-to-local rules 
should be case-insensitive or not
+   */
+  String CASE_INSENSITIVE_USERNAME_RULES = "case_insensitive_username_rules";
+  /**
+   * The kerberos-env property name declaring how to preprocess services.
+   * <p>
+   * Expected values are <code>"ALL"</code>, <code>"DEFAULT"</code>, 
<code>"NONE"</code>
+   *
+   * @see 
org.apache.ambari.server.serveraction.kerberos.PreconfigureServiceType
+   */
+  String PRECONFIGURE_SERVICES = "preconfigure_services";
 
   /**
    * Toggles Kerberos security to enable it or remove it depending on the 
state of the cluster.
@@ -368,18 +398,23 @@ public interface KerberosHelper {
    * Sets the relevant auth-to-local rule configuration properties using the 
services installed on
    * the cluster and their relevant Kerberos descriptors to determine the 
rules to be created.
    *
-   * @param kerberosDescriptor     the current Kerberos descriptor
-   * @param realm                  the default realm
-   * @param installedServices      the map of services and relevant components 
to process
-   * @param existingConfigurations a map of the current configurations
-   * @param kerberosConfigurations a map of the configurations to update, this 
where the generated
-   *                               auth-to-local values will be stored
+   * @param cluster                 cluster instance
+   * @param kerberosDescriptor      the current Kerberos descriptor
+   * @param realm                   the default realm
+   * @param installedServices       the map of services and relevant 
components to process
+   * @param existingConfigurations  a map of the current configurations
+   * @param kerberosConfigurations  a map of the configurations to update, 
this where the generated
+   *                                auth-to-local values will be stored
+   * @param includePreconfigureData <code>true</code> to include the 
preconfigure data; <code>false</code> otherwise
    * @throws AmbariException
    */
-  void setAuthToLocalRules(KerberosDescriptor kerberosDescriptor, String realm,
+  void setAuthToLocalRules(Cluster cluster,
+                           KerberosDescriptor kerberosDescriptor,
+                           String realm,
                            Map<String, Set<String>> installedServices,
                            Map<String, Map<String, String>> 
existingConfigurations,
-                           Map<String, Map<String, String>> 
kerberosConfigurations)
+                           Map<String, Map<String, String>> 
kerberosConfigurations,
+                           boolean includePreconfigureData)
       throws AmbariException;
 
   /**
@@ -426,12 +461,13 @@ public interface KerberosHelper {
    * descriptor and the composite is returned.  If not, the default cluster 
descriptor is returned
    * as-is.
    *
-   * @param cluster cluster instance
+   * @param cluster                 cluster instance
+   * @param includePreconfigureData <code>true</code> to include the 
preconfigure data; <code>false</code> otherwise
    * @return the kerberos descriptor associated with the specified cluster
    * @throws AmbariException if unable to obtain the descriptor
-   * @see #getKerberosDescriptor(KerberosDescriptorType, Cluster, boolean, 
Collection)
+   * @see #getKerberosDescriptor(KerberosDescriptorType, Cluster, boolean, 
Collection, boolean)
    */
-  KerberosDescriptor getKerberosDescriptor(Cluster cluster) throws 
AmbariException;
+  KerberosDescriptor getKerberosDescriptor(Cluster cluster, boolean 
includePreconfigureData) throws AmbariException;
 
   /**
    * Gets the requested Kerberos descriptor.
@@ -445,39 +481,63 @@ public interface KerberosHelper {
    * <dd>A Kerberos descriptor built using user-specified data stored as an 
artifact of the cluster, only</dd>
    * <dt>{@link KerberosDescriptorType#COMPOSITE}</dt>
    * <dd>A Kerberos descriptor built using data from the current stack 
definition with user-specified data stored as an artifact of the cluster applied
-   * - see {@link #getKerberosDescriptor(Cluster)}</dd>
+   * - see {@link #getKerberosDescriptor(Cluster, boolean)}</dd>
    * </dl>
    *
-   * @param kerberosDescriptorType the type of Kerberos descriptor to retrieve 
- see {@link KerberosDescriptorType}
-   * @param cluster                the relevant Cluster
-   * @param evaluateWhenClauses    true to evaluate Kerberos identity 
<code>when</code> clauses and
-   *                               prune if necessary; false otherwise.
-   * @param additionalServices     an optional collection of service names 
declaring additional
-   *                               services to add to the set of currently 
installed services to use
-   *                               while evaluating <code>when</code> clauses
+   * @param kerberosDescriptorType  the type of Kerberos descriptor to 
retrieve - see {@link KerberosDescriptorType}
+   * @param cluster                 the relevant Cluster
+   * @param evaluateWhenClauses     true to evaluate Kerberos identity 
<code>when</code> clauses and
+   *                                prune if necessary; false otherwise.
+   * @param additionalServices      an optional collection of service names 
declaring additional
+   *                                services to add to the set of currently 
installed services to use
+   *                                while evaluating <code>when</code> clauses
+   * @param includePreconfigureData <code>true</code> to include the 
preconfigure data; <code>false</code> otherwise
    * @return a Kerberos descriptor
    * @throws AmbariException
    */
   KerberosDescriptor getKerberosDescriptor(KerberosDescriptorType 
kerberosDescriptorType, Cluster cluster,
-                                           boolean evaluateWhenClauses, 
Collection<String> additionalServices)
+                                           boolean evaluateWhenClauses, 
Collection<String> additionalServices,
+                                           boolean includePreconfigureData)
       throws AmbariException;
 
   /**
-   * Merges configuration from a Map of configuration updates into a main 
configurations Map.  Each
-   * property in the updates Map is processed to replace variables using the 
replacement Map.
+   * Merges configurations from a Map of configuration updates into a main 
configurations Map.
+   * <p>
+   * Each property in the updates Map is processed to replace variables using 
the replacement Map,
+   * unless it has been filtered out using an optionally supplied 
configuration type filter
+   * (configurationTypeFilter).
    * <p/>
    * See {@link 
org.apache.ambari.server.state.kerberos.VariableReplacementHelper#replaceVariables(String,
 java.util.Map)}
    * for information on variable replacement.
    *
-   * @param configurations a Map of configurations
-   * @param updates        a Map of configuration updates
-   * @param replacements   a Map of (grouped) replacement values
-   * @return the merged Map
-   * @throws AmbariException
+   * @param configurations          a Map of existing configurations (updated 
in-place)
+   * @param updates                 a Map of configuration updates
+   * @param replacements            a Map of (grouped) replacement values
+   * @param configurationTypeFilter a Set of config types to filter from the 
map of updates;
+   *                                <code>null</code> indicate no filter
+   * @return the updated configurations map
+   * @throws AmbariException if an issue occurs
    */
   Map<String, Map<String, String>> mergeConfigurations(Map<String, Map<String, 
String>> configurations,
                                                        Map<String, 
KerberosConfigurationDescriptor> updates,
-                                                       Map<String, Map<String, 
String>> replacements)
+                                                       Map<String, Map<String, 
String>> replacements,
+                                                       Set<String> 
configurationTypeFilter)
+      throws AmbariException;
+
+  /**
+   * Determines which services, not currently installed, should be 
preconfigured to aid in reducing
+   * the number of service restarts when new services are added to a cluster 
where Kerberos is enabled.
+   *
+   * @param configurations     a Map of existing configurations (updated 
in-place)
+   * @param replacements       a Map of (grouped) replacement values
+   * @param cluster            the cluster
+   * @param kerberosDescriptor the Kerberos Descriptor
+   * @return the updated configurations map
+   * @throws AmbariException if an issue occurs
+   */
+  Map<String, Map<String, String>> 
processPreconfiguredServiceConfigurations(Map<String, Map<String, String>> 
configurations,
+                                                                             
Map<String, Map<String, String>> replacements, Cluster cluster,
+                                                                             
KerberosDescriptor kerberosDescriptor)
       throws AmbariException;
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/e6641558/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index 86ddc5f..73e9ec2 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -24,6 +24,7 @@ import java.io.File;
 import java.io.IOException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -35,6 +36,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.regex.Matcher;
 
 import org.apache.ambari.server.AmbariException;
@@ -46,6 +48,7 @@ import org.apache.ambari.server.actionmanager.RequestFactory;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.StageFactory;
 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.StackAdvisorHelper;
 import org.apache.ambari.server.api.services.stackadvisor.StackAdvisorRequest;
 import 
org.apache.ambari.server.api.services.stackadvisor.recommendations.RecommendationResponse;
@@ -89,15 +92,18 @@ import org.apache.ambari.server.stageplanner.RoleGraph;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostState;
+import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.SecurityState;
 import org.apache.ambari.server.state.SecurityType;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.State;
 import org.apache.ambari.server.state.ValueAttributesInfo;
@@ -297,45 +303,43 @@ public class KerberosHelperImpl implements KerberosHelper 
{
   @Override
   public void configureServices(Cluster cluster, Map<String, 
Collection<String>> serviceFilter)
       throws AmbariException, KerberosInvalidConfigurationException {
-    Map<String, Map<String, String>> existingConfigurations = 
calculateExistingConfigurations(cluster, null);
-    Map<String, Set<String>> installedServices = new HashMap<>();
-    Set<String> previouslyExistingServices = new HashSet<>();
-
-    // Calculate the map of installed services to installed components
-    Map<String, Service> clusterServices = cluster.getServices();
-    if(clusterServices != null) {
-      for (Service clusterService : clusterServices.values()) {
-        Set<String> installedComponents = 
installedServices.get(clusterService.getName());
-        if (installedComponents == null) {
-          installedComponents = new HashSet<>();
-          installedServices.put(clusterService.getName(), installedComponents);
-        }
+    final Map<String, Set<String>> installedServices = new HashMap<>();
+    final Set<String> previouslyExistingServices = new HashSet<>();
 
-        Map<String, ServiceComponent> clusterServiceComponents = 
clusterService.getServiceComponents();
-        if (clusterServiceComponents != null) {
-          for (ServiceComponent clusterServiceComponent : 
clusterServiceComponents.values()) {
-            installedComponents.add(clusterServiceComponent.getName());
-
-            // Determine if this component was PREVIOUSLY installed, which 
implies that its containing service was PREVIOUSLY installed
-            if 
(!previouslyExistingServices.contains(clusterService.getName())) {
-              Map<String, ServiceComponentHost> clusterServiceComponentHosts = 
clusterServiceComponent.getServiceComponentHosts();
-              if (clusterServiceComponentHosts != null) {
-                for (ServiceComponentHost clusterServiceComponentHost : 
clusterServiceComponentHosts.values()) {
-                  if 
(PREVIOUSLY_INSTALLED_STATES.contains(clusterServiceComponentHost.getState())) {
-                    previouslyExistingServices.add(clusterService.getName());
-                    break;
-                  }
-                }
+    // Calculate the map of installed services to installed components.
+    // We can create the map in the "shouldIncludeCommand" Command to avoid 
having to iterate
+    // over the returned ServiceComponentHost List.
+    getServiceComponentHosts(cluster,
+        new Command<Boolean, ServiceComponentHost>() {
+          @Override
+          public Boolean invoke(ServiceComponentHost sch) throws 
AmbariException {
+            if (sch != null) {
+              String serviceName = sch.getServiceName();
+
+              Set<String> installedComponents = 
installedServices.get(serviceName);
+              if (installedComponents == null) {
+                installedComponents = new HashSet<>();
+                installedServices.put(serviceName, installedComponents);
+              }
+              installedComponents.add(sch.getServiceComponentName());
+
+              // Determine if this component was PREVIOUSLY installed, which 
implies that its containing service was PREVIOUSLY installed
+              if (!previouslyExistingServices.contains(serviceName) && 
PREVIOUSLY_INSTALLED_STATES.contains(sch.getState())) {
+                previouslyExistingServices.add(serviceName);
               }
+
+              return true;
             }
+
+            return false;
           }
-        }
-      }
-    }
+        });
 
+    Map<String, Map<String, String>> existingConfigurations = 
calculateExistingConfigurations(cluster, null);
     Map<String, Map<String, String>> updates = 
getServiceConfigurationUpdates(cluster,
         existingConfigurations, installedServices, serviceFilter, 
previouslyExistingServices, true, true);
 
+    // Store the updates...
     for (Map.Entry<String, Map<String, String>> entry : updates.entrySet()) {
       configHelper.updateConfigType(cluster, cluster.getDesiredStackVersion(),
           ambariManagementController, entry.getKey(), entry.getValue(), null,
@@ -355,7 +359,7 @@ public class KerberosHelperImpl implements KerberosHelper {
 
     Map<String, Map<String, String>> kerberosConfigurations = new HashMap<>();
     KerberosDetails kerberosDetails = getKerberosDetails(cluster, null);
-    KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster);
+    KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, 
false);
 
     Map<String, String> kerberosDescriptorProperties = 
kerberosDescriptor.getProperties();
     Map<String, Map<String, String>> configurations = 
addAdditionalConfigurations(cluster,
@@ -363,10 +367,10 @@ public class KerberosHelperImpl implements KerberosHelper 
{
 
     Map<String, Set<String>> propertiesToIgnore = new HashMap<>();
 
-    // If Ambari is managing it own identities then add AMBARI to the set of 
installed servcie so
+    // If Ambari is managing it own identities then add AMBARI to the set of 
installed service so
     // that its Kerberos descriptor entries will be included.
-    if (createAmbariIdentities(existingConfigurations.get("kerberos-env"))) {
-      installedServices = new HashMap<>(installedServices);
+    if (createAmbariIdentities(existingConfigurations.get(KERBEROS_ENV))) {
+      installedServices = new HashMap<String, Set<String>>(installedServices);
       installedServices.put("AMBARI", Collections.singleton("AMBARI_SERVER"));
     }
 
@@ -403,7 +407,7 @@ public class KerberosHelperImpl implements KerberosHelper {
                   processIdentityConfigurations(identityConfigurations, 
kerberosConfigurations, configurations, propertiesToIgnore);
 
                   mergeConfigurations(kerberosConfigurations,
-                      
componentDescriptor.getConfigurations(!servicePreviouslyExisted), 
configurations);
+                      
componentDescriptor.getConfigurations(!servicePreviouslyExisted), 
configurations, null);
                 }
               }
             }
@@ -412,7 +416,7 @@ public class KerberosHelperImpl implements KerberosHelper {
       }
     }
 
-    setAuthToLocalRules(kerberosDescriptor, kerberosDetails.getDefaultRealm(), 
installedServices, configurations, kerberosConfigurations);
+    setAuthToLocalRules(cluster, kerberosDescriptor, 
kerberosDetails.getDefaultRealm(), installedServices, configurations, 
kerberosConfigurations, false);
 
     return (applyStackAdvisorUpdates)
         ? applyStackAdvisorUpdates(cluster, installedServices.keySet(), 
configurations, kerberosConfigurations, propertiesToIgnore,
@@ -420,6 +424,126 @@ public class KerberosHelperImpl implements KerberosHelper 
{
         : kerberosConfigurations;
   }
 
+  /**
+   * Adds host assignments, recommended by the Stack Advisor, to the 
configuration map (clusterHostInfo)
+   * for the components specified in the component filter 
<code>componentFilter</code> (or all if the
+   * component filter is <code>null</code>).
+   *
+   * @param cluster         the cluster
+   * @param services        the relevant services to consider
+   * @param componentFilter the set of components to add to the 
clusterHostInfo structure
+   * @param configurations  the configurations map to update
+   * @throws AmbariException if an error occurs
+   */
+  private void applyStackAdvisorHostRecommendations(Cluster cluster,
+                                                    Set<String> services,
+                                                    Set<String> 
componentFilter,
+                                                    Map<String, Map<String, 
String>> configurations)
+      throws AmbariException {
+    StackId stackVersion = cluster.getCurrentStackVersion();
+    List<String> hostNames = new ArrayList<>();
+
+    Collection<Host> hosts = cluster.getHosts();
+    if (hosts != null) {
+      for (Host host : hosts) {
+        hostNames.add(host.getHostName());
+      }
+    }
+
+    StackAdvisorRequest request = 
StackAdvisorRequest.StackAdvisorRequestBuilder
+        .forStack(stackVersion.getStackName(), stackVersion.getStackVersion())
+        .forServices(services)
+        .forHosts(hostNames)
+        .withComponentHostsMap(cluster.getServiceComponentHostMap(null, 
services))
+        .ofType(StackAdvisorRequest.StackAdvisorRequestType.HOST_GROUPS)
+        .build();
+
+    try {
+      RecommendationResponse response = stackAdvisorHelper.recommend(request);
+
+      RecommendationResponse.Recommendation recommendation = (response == 
null) ? null : response.getRecommendations();
+      RecommendationResponse.Blueprint blueprint = (recommendation == null) ? 
null : recommendation.getBlueprint();
+      Set<RecommendationResponse.HostGroup> hostGroups = (blueprint == null) ? 
null : blueprint.getHostGroups();
+
+
+      if (hostGroups != null) {
+        RecommendationResponse.BlueprintClusterBinding blueprintBinding = 
recommendation.getBlueprintClusterBinding();
+        Map<String, RecommendationResponse.BindingHostGroup> bindingMap = new 
HashMap<>();
+        if (blueprintBinding != null) {
+          Set<RecommendationResponse.BindingHostGroup> bindingHostGroups = 
blueprintBinding.getHostGroups();
+          if (bindingHostGroups != null) {
+            for (RecommendationResponse.BindingHostGroup bindingHostGroup : 
bindingHostGroups) {
+              bindingMap.put(bindingHostGroup.getName(), bindingHostGroup);
+            }
+          }
+        }
+
+        // Get (and created if needed) the clusterHostInfo map
+        Map<String, String> clusterHostInfoMap = 
configurations.get("clusterHostInfo");
+        if (clusterHostInfoMap == null) {
+          clusterHostInfoMap = new HashMap<>();
+          configurations.put("clusterHostInfo", clusterHostInfoMap);
+        }
+
+        Map<String, String> componentToClusterInfoMap = 
StageUtils.getComponentToClusterInfoKeyMap();
+
+        // Iterate through the recommendations to find the recommended host 
assignments
+        for (RecommendationResponse.HostGroup hostGroup : hostGroups) {
+          Set<Map<String, String>> components = hostGroup.getComponents();
+
+          if (components != null) {
+            RecommendationResponse.BindingHostGroup binding = 
bindingMap.get(hostGroup.getName());
+
+            if (binding != null) {
+              Set<Map<String, String>> hostGroupHosts = binding.getHosts();
+
+              if (hostGroupHosts != null) {
+                for (Map<String, String> component : components) {
+                  String componentName = component.get("name");
+
+                  // If the component filter is null or the current component 
is found in the filter,
+                  // include it in the map
+                  if ((componentFilter == null) || 
componentFilter.contains(componentName)) {
+                    String key = componentToClusterInfoMap.get(componentName);
+
+                    if(StringUtils.isEmpty(key)) {
+                      // If not found in the componentToClusterInfoMap, then 
keys are assumed to be
+                      // in the form of <component_name>_hosts (lowercase)
+                      key = componentName.toLowerCase() + "_hosts";
+                    }
+
+                    Set<String> fqdns = new TreeSet<>();
+
+                    // Values are a comma-delimited list of hosts.
+                    // If a value exists, split it and add the tokens to the 
set
+                    if (!StringUtils.isEmpty(clusterHostInfoMap.get(key))) {
+                      
fqdns.addAll(Arrays.asList(clusterHostInfoMap.get(key).split(",")));
+                    }
+
+                    // Add the set of hosts for the current host group
+                    for (Map<String, String> hostGroupHost : hostGroupHosts) {
+                      String fqdn = hostGroupHost.get("fqdn");
+
+                      if (!StringUtils.isEmpty(fqdn)) {
+                        fqdns.add(fqdn);
+                      }
+                    }
+
+                    // create the comma-delimited list of hosts
+                    clusterHostInfoMap.put(key, StringUtils.join(fqdns, ','));
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    } catch (StackAdvisorException e) {
+      LOG.error("Failed to obtain the recommended host groups for the 
preconfigured components.", e);
+      throw new AmbariException(e.getMessage(), e);
+    }
+  }
+
   @Override
   public Map<String, Map<String, String>> applyStackAdvisorUpdates(Cluster 
cluster, Set<String> services,
                                                                    Map<String, 
Map<String, String>> existingConfigurations,
@@ -489,9 +613,16 @@ public class KerberosHelperImpl implements KerberosHelper {
       }
 
       Set<StackId> visitedStacks = new HashSet<>();
+      Map<String, Service> installedServices = cluster.getServices();
 
       for (String serviceName : services) {
-        Service service = cluster.getService(serviceName);
+        Service service = installedServices.get(serviceName);
+
+        // Skip services that are not really installed
+        if(service == null) {
+          continue;
+        }
+
         StackId stackId = service.getDesiredStackId();
 
         if (visitedStacks.contains(stackId)) {
@@ -500,7 +631,7 @@ public class KerberosHelperImpl implements KerberosHelper {
 
         StackAdvisorRequest request = 
StackAdvisorRequest.StackAdvisorRequestBuilder
             .forStack(stackId.getStackName(), stackId.getStackVersion())
-            .forServices(new ArrayList<>(services))
+            .forServices(services)
             .forHosts(hostNames)
             .withComponentHostsMap(cluster.getServiceComponentHostMap(null, 
services))
             .withConfigurations(requestConfigurations)
@@ -568,7 +699,7 @@ public class KerberosHelperImpl implements KerberosHelper {
                     "\n\tConfigType: {}\n\tProperty: {}\n\tValue: {}",
                 configType, propertyName, recommendedValue);
 
-            if(kerberosConfigProperties == null) {
+            if (kerberosConfigProperties == null) {
               kerberosConfigProperties = new HashMap<>();
               kerberosConfigurations.put(configType, kerberosConfigProperties);
             }
@@ -644,7 +775,7 @@ public class KerberosHelperImpl implements KerberosHelper {
 
     // Only perform this task if Ambari manages Kerberos identities
     if (kerberosDetails.manageIdentities()) {
-      KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster);
+      KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, 
false);
 
       Map<String, String> kerberosDescriptorProperties = 
kerberosDescriptor.getProperties();
       Map<String, Map<String, String>> configurations = 
addAdditionalConfigurations(cluster,
@@ -868,16 +999,19 @@ public class KerberosHelperImpl implements KerberosHelper 
{
   }
 
   @Override
-  public void setAuthToLocalRules(KerberosDescriptor kerberosDescriptor, 
String realm,
+  public void setAuthToLocalRules(Cluster cluster,
+                                  KerberosDescriptor kerberosDescriptor,
+                                  String realm,
                                   Map<String, Set<String>> installedServices,
                                   Map<String, Map<String, String>> 
existingConfigurations,
-                                  Map<String, Map<String, String>> 
kerberosConfigurations)
+                                  Map<String, Map<String, String>> 
kerberosConfigurations,
+                                  boolean includePreconfigureData)
       throws AmbariException {
 
     boolean processAuthToLocalRules = true;
-    Map<String, String> kerberosEnvProperties = 
existingConfigurations.get("kerberos-env");
-    if (kerberosEnvProperties.containsKey("manage_auth_to_local")) {
-      processAuthToLocalRules = 
Boolean.valueOf(kerberosEnvProperties.get("manage_auth_to_local"));
+    Map<String, String> kerberosEnvProperties = 
existingConfigurations.get(KERBEROS_ENV);
+    if (kerberosEnvProperties.containsKey(MANAGE_AUTH_TO_LOCAL_RULES)) {
+      processAuthToLocalRules = 
Boolean.valueOf(kerberosEnvProperties.get(MANAGE_AUTH_TO_LOCAL_RULES));
     }
 
     if (kerberosDescriptor != null && processAuthToLocalRules) {
@@ -887,7 +1021,7 @@ public class KerberosHelperImpl implements KerberosHelper {
 
       // a flag to be used by the AuthToLocalBuilder marking whether the 
default realm rule should contain the //L option, indicating username case 
insensitive behaviour
       // the 'kerberos-env' structure is expected to be available here as it 
was previously validated
-      boolean caseInsensitiveUser = 
Boolean.valueOf(existingConfigurations.get("kerberos-env").get("case_insensitive_username_rules"));
+      boolean caseInsensitiveUser = 
Boolean.valueOf(existingConfigurations.get(KERBEROS_ENV).get(CASE_INSENSITIVE_USERNAME_RULES));
 
       // Additional realms that need to be handled according to the Kerberos 
Descriptor
       String additionalRealms = 
kerberosDescriptor.getProperty("additional_realms");
@@ -897,43 +1031,69 @@ public class KerberosHelperImpl implements 
KerberosHelper {
       filterContext.put("configurations", existingConfigurations);
       filterContext.put("services", installedServices.keySet());
 
-      // Determine which properties need to be set
       AuthToLocalBuilder authToLocalBuilder = new AuthToLocalBuilder(realm, 
additionalRealms, caseInsensitiveUser);
-      addIdentities(authToLocalBuilder, kerberosDescriptor.getIdentities(true, 
filterContext), null, existingConfigurations);
 
+      // Add in the default configurations for the services that need to be 
preconfigured. These
+      // configurations may be needed while calculating the auth-to-local 
rules.
+      Map<String, Map<String, String>> replacements = (includePreconfigureData)
+          ? 
addConfigurationsForPreProcessedServices(deepCopy(existingConfigurations), 
cluster, kerberosDescriptor, false)
+          : existingConfigurations;
+
+      // Process top-level identities
+      addIdentities(authToLocalBuilder, kerberosDescriptor.getIdentities(true, 
filterContext), null, replacements);
+
+      // Determine which properties need to be set
       authToLocalProperties = kerberosDescriptor.getAuthToLocalProperties();
       if (authToLocalProperties != null) {
         authToLocalPropertiesToSet.addAll(authToLocalProperties);
       }
 
-      for(Map.Entry<String, Set<String>> installedService: 
installedServices.entrySet()) {
-        String serviceName = installedService.getKey();
+      // Iterate through the services in the Kerberos descriptor. If a found 
service is installed
+      // or marked to be preconfigured, add the relevant data to the 
auth-to-local rules.
+      Map<String, KerberosServiceDescriptor> serviceDescriptors = 
kerberosDescriptor.getServices();
+      if (serviceDescriptors != null) {
+        for (KerberosServiceDescriptor serviceDescriptor : 
serviceDescriptors.values()) {
+          String serviceName = serviceDescriptor.getName();
+          boolean preconfigure = includePreconfigureData && 
serviceDescriptor.shouldPreconfigure();
+          boolean explicitlyAdded = installedServices.containsKey(serviceName);
+
+          // Add this service's identities if we are implicitly 
preconfigurring the service or if the
+          // service has been explicitly added to the cluster
+          if (preconfigure || explicitlyAdded) {
+            LOG.info("Adding identities for service {} to auth to local 
mapping [{}]",
+                serviceName,
+                (explicitlyAdded) ? "explicit" : "preconfigured");
 
-        KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(serviceName);
-        if(serviceDescriptor != null) {
-          LOG.info("Adding identities for service {} to auth to local 
mapping", installedService);
+            // Process the service-level Kerberos descriptor
+            addIdentities(authToLocalBuilder, 
serviceDescriptor.getIdentities(true, filterContext), null, replacements);
 
-          // Process the service-level Kerberos descriptor
-          addIdentities(authToLocalBuilder, 
serviceDescriptor.getIdentities(true, filterContext), null, 
existingConfigurations);
+            authToLocalProperties = 
serviceDescriptor.getAuthToLocalProperties();
+            if (authToLocalProperties != null) {
+              authToLocalPropertiesToSet.addAll(authToLocalProperties);
+            }
 
-          authToLocalProperties = serviceDescriptor.getAuthToLocalProperties();
-          if (authToLocalProperties != null) {
-            authToLocalPropertiesToSet.addAll(authToLocalProperties);
-          }
+            // Process the relevant component-level Kerberos descriptors
+            Map<String, KerberosComponentDescriptor> componentDescriptors = 
serviceDescriptor.getComponents();
+            if (componentDescriptors != null) {
+              Set<String> installedServiceComponents = 
installedServices.get(serviceName);
+              // Ensure installedComponents is not null....
+              if (installedServiceComponents == null) {
+                installedServiceComponents = Collections.emptySet();
+              }
 
-          // Process the relevant component-level Kerberos descriptors
-          Set<String> installedComponents = installedService.getValue();
-          if(installedComponents != null) {
-            for (String installedComponent : installedComponents) {
-              KerberosComponentDescriptor componentDescriptor = 
serviceDescriptor.getComponent(installedComponent);
+              for (KerberosComponentDescriptor componentDescriptor : 
componentDescriptors.values()) {
+                String componentName = componentDescriptor.getName();
 
-              if (componentDescriptor != null) {
-                LOG.info("Adding identities for component {} to auth to local 
mapping", installedComponent);
-                addIdentities(authToLocalBuilder, 
componentDescriptor.getIdentities(true, filterContext), null, 
existingConfigurations);
+                // Add this component's identities if we are implicitly 
preconfiguring the parent
+                // service or if the component has been explicitly added to 
the cluster
+                if (preconfigure || 
(installedServiceComponents.contains(componentName))) {
+                  LOG.info("Adding identities for component {} to auth to 
local mapping", componentName);
+                  addIdentities(authToLocalBuilder, 
componentDescriptor.getIdentities(true, filterContext), null, replacements);
 
-                authToLocalProperties = 
componentDescriptor.getAuthToLocalProperties();
-                if (authToLocalProperties != null) {
-                  authToLocalPropertiesToSet.addAll(authToLocalProperties);
+                  authToLocalProperties = 
componentDescriptor.getAuthToLocalProperties();
+                  if (authToLocalProperties != null) {
+                    authToLocalPropertiesToSet.addAll(authToLocalProperties);
+                  }
                 }
               }
             }
@@ -986,56 +1146,77 @@ public class KerberosHelperImpl implements 
KerberosHelper {
 
 
   @Override
-  public List<ServiceComponentHost> getServiceComponentHostsToProcess(Cluster 
cluster,
-                                                                      
KerberosDescriptor kerberosDescriptor,
-                                                                      
Map<String, ? extends Collection<String>> serviceComponentFilter,
-                                                                      
Collection<String> hostFilter, Collection<String> identityFilter,
-                                                                      
Command<Boolean, ServiceComponentHost> shouldProcessCommand)
+  public List<ServiceComponentHost> getServiceComponentHostsToProcess(final 
Cluster cluster,
+                                                                      final 
KerberosDescriptor kerberosDescriptor,
+                                                                      final 
Map<String, ? extends Collection<String>> serviceComponentFilter,
+                                                                      final 
Collection<String> hostFilter, Collection<String> identityFilter,
+                                                                      final 
Command<Boolean, ServiceComponentHost> shouldProcessCommand)
       throws AmbariException {
-    List<ServiceComponentHost> serviceComponentHostsToProcess = new 
ArrayList<>();
-    Map<String, Service> services = cluster.getServices();
-
-    if ((services != null) && !services.isEmpty()) {
-      Collection<Host> hosts = cluster.getHosts();
-
-      if ((hosts != null) && !hosts.isEmpty()) {
-        // Iterate over the hosts in the cluster to find the components 
installed in each.  For each
-        // component (aka service component host - sch) determine the 
configuration updates and
-        // and the principals an keytabs to create.
-        for (Host host : hosts) {
-          String hostname = host.getHostName();
-
-          // Filter hosts as needed....
-          if ((hostFilter == null) || hostFilter.contains(hostname)) {
-            // Get a list of components on the current host
-            List<ServiceComponentHost> serviceComponentHosts = 
cluster.getServiceComponentHosts(hostname);
-
-            if ((serviceComponentHosts != null) && 
!serviceComponentHosts.isEmpty()) {
-
-              // Iterate over the components installed on the current host to 
get the service and
-              // component-level Kerberos descriptors in order to determine 
which principals,
-              // keytab files, and configurations need to be created or 
updated.
-              for (ServiceComponentHost sch : serviceComponentHosts) {
-                String serviceName = sch.getServiceName();
-                String componentName = sch.getServiceComponentName();
-
-                // If there is no filter or the filter contains the current 
service name...
-                if ((serviceComponentFilter == null) || 
serviceComponentFilter.containsKey(serviceName)) {
-                  Collection<String> componentFilter = (serviceComponentFilter 
== null) ? null : serviceComponentFilter.get(serviceName);
-                  KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(serviceName);
-
-                  if (serviceDescriptor != null) {
-                    // If there is no filter or the filter contains the 
current component name,
-                    // test to see if this component should be processed by 
querying the handler...
-                    if (((componentFilter == null) || 
componentFilter.contains(componentName)) && shouldProcessCommand.invoke(sch)) {
-                      serviceComponentHostsToProcess.add(sch);
-                    }
-                  }
-                }
+
+    return getServiceComponentHosts(cluster, new Command<Boolean, 
ServiceComponentHost>() {
+      @Override
+      public Boolean invoke(ServiceComponentHost sch) throws AmbariException {
+        if (sch != null) {
+          // Check the host filter
+          if ((hostFilter == null) || hostFilter.contains(sch.getHostName())) {
+            String serviceName = sch.getServiceName();
+
+            // Check the service filter
+            if ((serviceComponentFilter == null) || 
serviceComponentFilter.containsKey(serviceName)) {
+              KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(serviceName);
+
+              if (serviceDescriptor != null) {
+                Collection<String> componentFilter = (serviceComponentFilter 
== null) ? null : serviceComponentFilter.get(serviceName);
+
+                // Check the service/component filter and the 
shouldProcessCommand
+                return (((componentFilter == null) || 
componentFilter.contains(sch.getServiceComponentName())) &&
+                    ((shouldProcessCommand == null) || 
shouldProcessCommand.invoke(sch)));
               }
             }
           }
         }
+
+        return false;
+      }
+    });
+  }
+
+  /**
+   * Find the {@link ServiceComponentHost}s for the cluster, filtering using 
the
+   * supplied "should include" command (<code>shouldIncludeCommand</code>).
+   * <p>
+   * If <code>shouldIncludeCommand</code> is <code>null/code>, no filtering 
will be performed causing
+   * all found {@link ServiceComponentHost}s to be returned.
+   *
+   * @param cluster              the cluster
+   * @param shouldIncludeCommand the filtering logic
+   * @return a list of (filtered) {@link ServiceComponentHost}s
+   * @throws AmbariException if an error occurs
+   */
+  private List<ServiceComponentHost> getServiceComponentHosts(Cluster cluster,
+                                                              Command<Boolean, 
ServiceComponentHost> shouldIncludeCommand)
+      throws AmbariException {
+    List<ServiceComponentHost> serviceComponentHostsToProcess = new 
ArrayList<>();
+    // Get the hosts in the cluster
+    Collection<Host> hosts = cluster.getHosts();
+
+    if ((hosts != null) && !hosts.isEmpty()) {
+      // Iterate over the hosts in the cluster to find the components 
installed in each.
+      for (Host host : hosts) {
+        String hostname = host.getHostName();
+
+        // Get a list of components on the current host
+        List<ServiceComponentHost> serviceComponentHosts = 
cluster.getServiceComponentHosts(hostname);
+
+        if ((serviceComponentHosts != null) && 
!serviceComponentHosts.isEmpty()) {
+          // Iterate over the components installed on the current host and 
execute the shouldIncludeCommand
+          // Command (if supplied) to get the desired ServiceComponentHost 
instances.
+          for (ServiceComponentHost sch : serviceComponentHosts) {
+            if ((shouldIncludeCommand == null) || 
shouldIncludeCommand.invoke(sch)) {
+              serviceComponentHostsToProcess.add(sch);
+            }
+          }
+        }
       }
     }
 
@@ -1061,41 +1242,30 @@ public class KerberosHelperImpl implements 
KerberosHelper {
   }
 
   @Override
-  public KerberosDescriptor getKerberosDescriptor(Cluster cluster) throws 
AmbariException {
-    return getKerberosDescriptor(KerberosDescriptorType.COMPOSITE, cluster, 
false, null);
+  public KerberosDescriptor getKerberosDescriptor(Cluster cluster, boolean 
includePreconfigureData) throws AmbariException {
+    return getKerberosDescriptor(KerberosDescriptorType.COMPOSITE, cluster, 
false, null, includePreconfigureData);
   }
 
   @Override
   public KerberosDescriptor getKerberosDescriptor(KerberosDescriptorType 
kerberosDescriptorType, Cluster cluster,
-                                                  boolean evaluateWhenClauses, 
Collection<String> additionalServices)
+                                                  boolean evaluateWhenClauses, 
Collection<String> additionalServices,
+                                                  boolean 
includePreconfigureData)
       throws AmbariException {
-    KerberosDescriptor kerberosDescriptor;
 
     KerberosDescriptor stackDescriptor = (kerberosDescriptorType == 
KerberosDescriptorType.STACK || kerberosDescriptorType == 
KerberosDescriptorType.COMPOSITE)
-        ? getKerberosDescriptorFromStack(cluster)
+        ? getKerberosDescriptorFromStack(cluster, includePreconfigureData)
         : null;
 
     KerberosDescriptor userDescriptor = (kerberosDescriptorType == 
KerberosDescriptorType.USER || kerberosDescriptorType == 
KerberosDescriptorType.COMPOSITE)
         ? getKerberosDescriptorUpdates(cluster)
         : null;
 
-    if (stackDescriptor == null) {
-      if (userDescriptor == null) {
-        return new KerberosDescriptor();  // return an empty Kerberos 
descriptor since we have no data
-      } else {
-        kerberosDescriptor = userDescriptor;
-      }
-    } else {
-      if (userDescriptor != null) {
-        stackDescriptor.update(userDescriptor);
-      }
-      kerberosDescriptor = stackDescriptor;
-    }
+    KerberosDescriptor kerberosDescriptor = 
combineKerberosDescriptors(stackDescriptor, userDescriptor);
 
     if (evaluateWhenClauses) {
       Set<String> services = new HashSet<>(cluster.getServices().keySet());
 
-      if(additionalServices != null) {
+      if (additionalServices != null) {
         services.addAll(additionalServices);
       }
 
@@ -1141,7 +1311,8 @@ public class KerberosHelperImpl implements KerberosHelper 
{
   @Override
   public Map<String, Map<String, String>> mergeConfigurations(Map<String, 
Map<String, String>> configurations,
                                                               Map<String, 
KerberosConfigurationDescriptor> updates,
-                                                              Map<String, 
Map<String, String>> replacements)
+                                                              Map<String, 
Map<String, String>> replacements,
+                                                              Set<String> 
configurationTypeFilter)
       throws AmbariException {
 
     if ((updates != null) && !updates.isEmpty()) {
@@ -1151,11 +1322,59 @@ public class KerberosHelperImpl implements 
KerberosHelper {
 
       for (Map.Entry<String, KerberosConfigurationDescriptor> entry : 
updates.entrySet()) {
         String type = entry.getKey();
-        KerberosConfigurationDescriptor configurationDescriptor = 
entry.getValue();
 
-        if (configurationDescriptor != null) {
-          Map<String, String> updatedProperties = 
configurationDescriptor.getProperties();
-          mergeConfigurations(configurations, type, updatedProperties, 
replacements);
+        if ((configurationTypeFilter == null) || 
(configurationTypeFilter.contains(type))) {
+          KerberosConfigurationDescriptor configurationDescriptor = 
entry.getValue();
+
+          if (configurationDescriptor != null) {
+            Map<String, String> updatedProperties = 
configurationDescriptor.getProperties();
+            mergeConfigurations(configurations, type, updatedProperties, 
replacements);
+          }
+        }
+      }
+    }
+
+    return configurations;
+  }
+
+  @Override
+  public Map<String, Map<String, String>> 
processPreconfiguredServiceConfigurations(Map<String, Map<String, String>> 
configurations,
+                                                                               
     Map<String, Map<String, String>> replacements,
+                                                                               
     Cluster cluster,
+                                                                               
     KerberosDescriptor kerberosDescriptor)
+      throws AmbariException {
+
+    // Ensure the Kerberos descriptor exists....
+    if (kerberosDescriptor == null) {
+      kerberosDescriptor = getKerberosDescriptor(cluster, true);
+    }
+
+    Map<String, KerberosServiceDescriptor> serviceDescriptors = 
kerberosDescriptor.getServices();
+
+    if (serviceDescriptors != null) {
+      if (configurations == null) {
+        configurations = new HashMap<>();
+      }
+
+      // Add in the default configurations for the services that need to be 
preconfigured. These
+      // configurations may be needed while calculating the auth-to-local 
rules.
+      Map<String, Map<String, String>> replacementsWithDefaults = 
addConfigurationsForPreProcessedServices(deepCopy(replacements), cluster, 
kerberosDescriptor, true);
+
+      Map<String, Service> existingServices = cluster.getServices();
+
+      for (KerberosServiceDescriptor serviceDescriptor : 
serviceDescriptors.values()) {
+        String serviceName = serviceDescriptor.getName();
+        boolean shouldPreconfigure = serviceDescriptor.shouldPreconfigure();
+
+        if (!existingServices.containsKey(serviceName) && shouldPreconfigure) {
+          configurations = mergeConfigurations(configurations, 
serviceDescriptor.getConfigurations(), replacementsWithDefaults, 
replacements.keySet());
+
+          Map<String, KerberosComponentDescriptor> componentDescriptors = 
serviceDescriptor.getComponents();
+          if (componentDescriptors != null) {
+            for (KerberosComponentDescriptor componentDescriptor : 
componentDescriptors.values()) {
+              configurations = mergeConfigurations(configurations, 
componentDescriptor.getConfigurations(), replacementsWithDefaults, 
replacements.keySet());
+            }
+          }
         }
       }
     }
@@ -1239,16 +1458,15 @@ public class KerberosHelperImpl implements 
KerberosHelper {
   @Override
   public Map<String, Map<String, String>> calculateConfigurations(Cluster 
cluster, String hostname,
                                                                   Map<String, 
String> kerberosDescriptorProperties)
-      throws AmbariException
-  {
+      throws AmbariException {
     return addAdditionalConfigurations(cluster,
-      calculateExistingConfigurations(cluster, hostname),
-      hostname, kerberosDescriptorProperties);
+        calculateExistingConfigurations(cluster, hostname),
+        hostname, kerberosDescriptorProperties);
   }
 
   private Map<String, String> principalNames(Cluster cluster, Map<String, 
Map<String, String>> configuration) throws AmbariException {
     Map<String, String> result = new HashMap<>();
-    for (Map.Entry<String, String> each : 
getKerberosDescriptor(cluster).principals().entrySet()) {
+    for (Map.Entry<String, String> each : getKerberosDescriptor(cluster, 
false).principals().entrySet()) {
       result.put(each.getKey(), 
variableReplacementHelper.replaceVariables(each.getValue(), configuration));
     }
     return result;
@@ -1276,12 +1494,11 @@ public class KerberosHelperImpl implements 
KerberosHelper {
 
     // Only calculate the active identities if the kerberos-env configurtaion 
is available.  Else
     // important information like the realm will be missing 
(kerberos-env/realm)
-    Config kerberosEnvConfig = cluster.getDesiredConfigByType("kerberos-env");
-    if(kerberosEnvConfig == null) {
+    Config kerberosEnvConfig = cluster.getDesiredConfigByType(KERBEROS_ENV);
+    if (kerberosEnvConfig == null) {
       LOG.debug("Calculating the active identities for {} is being skipped 
since the kerberos-env configuration is not available",
           clusterName, cluster.getSecurityType().name(), 
SecurityType.KERBEROS.name());
-    }
-    else {
+    } else {
       Collection<String> hosts;
       String ambariServerHostname = StageUtils.getHostName();
 
@@ -1304,7 +1521,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
       }
 
       if (!hosts.isEmpty()) {
-        KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster);
+        KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, 
false);
 
         if (kerberosDescriptor != null) {
           Map<String, String> kerberosDescriptorProperties = 
kerberosDescriptor.getProperties();
@@ -1331,7 +1548,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
             if (hostname.equals(ambariServerHostname)) {
               // Determine if we should _calculate_ the Ambari service 
identities.
               // If kerberos-env/create_ambari_principal is not set to false 
the identity should be calculated.
-              if(createAmbariIdentities(kerberosEnvConfig.getProperties())) {
+              if (createAmbariIdentities(kerberosEnvConfig.getProperties())) {
                 List<KerberosIdentityDescriptor> ambariIdentities = 
getAmbariServerIdentities(kerberosDescriptor);
                 if (ambariIdentities != null) {
                   identities.addAll(ambariIdentities);
@@ -1664,7 +1881,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
                                final Handler handler)
       throws AmbariException, KerberosOperationException {
 
-    final KerberosDescriptor kerberosDescriptor = 
getKerberosDescriptor(cluster);
+    final KerberosDescriptor kerberosDescriptor = 
getKerberosDescriptor(cluster, false);
     final SecurityState desiredSecurityState = 
handler.getNewServiceSecurityState();
 
     List<ServiceComponentHost> schToProcess = 
getServiceComponentHostsToProcess(
@@ -1809,7 +2026,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
       }
 
       List<ServiceComponentHost> serviceComponentHostsToProcess = new 
ArrayList<>();
-      KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster);
+      KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, 
false);
       KerberosIdentityDataFileWriter kerberosIdentityDataFileWriter = null;
 
       Map<String, String> kerberosDescriptorProperties = 
kerberosDescriptor.getProperties();
@@ -1990,7 +2207,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
       throw new AmbariException(message);
     }
 
-    Config configKerberosEnv = cluster.getDesiredConfigByType("kerberos-env");
+    Config configKerberosEnv = cluster.getDesiredConfigByType(KERBEROS_ENV);
     if (configKerberosEnv == null) {
       String message = "The 'kerberos-env' configuration is not available";
       LOG.error(message);
@@ -2005,14 +2222,14 @@ public class KerberosHelperImpl implements 
KerberosHelper {
     }
 
     kerberosDetails.setSecurityType(cluster.getSecurityType());
-    kerberosDetails.setDefaultRealm(kerberosEnvProperties.get("realm"));
+    kerberosDetails.setDefaultRealm(kerberosEnvProperties.get(DEFAULT_REALM));
 
     kerberosDetails.setKerberosEnvProperties(kerberosEnvProperties);
 
     // If set, override the manage identities behavior
     kerberosDetails.setManageIdentities(manageIdentities);
 
-    String kdcTypeProperty = kerberosEnvProperties.get("kdc_type");
+    String kdcTypeProperty = kerberosEnvProperties.get(KDC_TYPE);
     if ((kdcTypeProperty == null) && kerberosDetails.manageIdentities()) {
       String message = "The 'kerberos-env/kdc_type' value must be set to a 
valid KDC type";
       LOG.error(message);
@@ -2206,12 +2423,12 @@ public class KerberosHelperImpl implements 
KerberosHelper {
   /**
    * Creates a new stage
    *
-   * @param id              the new stage's id
-   * @param cluster         the relevant Cluster
-   * @param requestId       the relevant request Id
-   * @param requestContext  a String describing the stage
-   * @param commandParams   JSON-encoded command parameters
-   * @param hostParams      JSON-encoded host parameters
+   * @param id             the new stage's id
+   * @param cluster        the relevant Cluster
+   * @param requestId      the relevant request Id
+   * @param requestContext a String describing the stage
+   * @param commandParams  JSON-encoded command parameters
+   * @param hostParams     JSON-encoded host parameters
    * @return a newly created Stage
    */
   private Stage createNewStage(long id, Cluster cluster, long requestId,
@@ -2255,7 +2472,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
                                         Map<String, String> commandParameters, 
String commandDetail,
                                         Integer timeout) throws 
AmbariException {
 
-    Stage stage = createNewStage(id, cluster, requestId, requestContext,  
commandParams, hostParams);
+    Stage stage = createNewStage(id, cluster, requestId, requestContext, 
commandParams, hostParams);
     stage.addServerActionCommand(actionClass.getName(), null, 
Role.AMBARI_SERVER_ACTION,
         RoleCommand.EXECUTE, cluster.getClusterName(), event, 
commandParameters, commandDetail,
         ambariManagementController.findConfigurationTagsWithOverrides(cluster, 
null), timeout,
@@ -2582,10 +2799,11 @@ public class KerberosHelperImpl implements 
KerberosHelper {
    * 
<code>stacks/:stackName/versions/:version/artifacts/kerberos_descriptor</code>
    *
    * @param cluster the cluster
+   * @param includePreconfigureData <code>true</code> to include the 
preconfigure data; otherwise false
    * @return a Kerberos Descriptor
    * @throws AmbariException if an error occurs while retrieving the Kerberos 
descriptor
    */
-  private KerberosDescriptor getKerberosDescriptorFromStack(Cluster cluster) 
throws AmbariException {
+  private KerberosDescriptor getKerberosDescriptorFromStack(Cluster cluster, 
boolean includePreconfigureData) throws AmbariException {
     // !!! FIXME in a per-service view, what does this become?
     Set<StackId> stackIds = new HashSet<>();
 
@@ -2602,14 +2820,14 @@ public class KerberosHelperImpl implements 
KerberosHelper {
     // -------------------------------
     // Get the default Kerberos descriptor from the stack, which is the same 
as the value from
     // stacks/:stackName/versions/:version/artifacts/kerberos_descriptor
-    return ambariMetaInfo.getKerberosDescriptor(stackId.getStackName(), 
stackId.getStackVersion());
+    return ambariMetaInfo.getKerberosDescriptor(stackId.getStackName(), 
stackId.getStackVersion(), includePreconfigureData);
     // -------------------------------
   }
 
   /**
    * Recursively walk the Kerberos descriptor tree to find all Kerberos 
identity definitions and
    * determine which should be filtered out.
-   *
+   * <p>
    * No actual filtering is performed while processing since any referenced 
Kerberos identities need
    * to be accessible throughout the process. So a map of container path to a 
list of identities is
    * created an returned
@@ -2621,14 +2839,14 @@ public class KerberosHelperImpl implements 
KerberosHelper {
    * @return
    * @throws AmbariException
    */
-  private Map<String,Set<String>> processWhenClauses(String currentPath, 
AbstractKerberosDescriptorContainer container, Map<String, Object> context, 
Map<String,Set<String>> identitiesToRemove) throws AmbariException {
+  private Map<String, Set<String>> processWhenClauses(String currentPath, 
AbstractKerberosDescriptorContainer container, Map<String, Object> context, 
Map<String, Set<String>> identitiesToRemove) throws AmbariException {
 
     // Get the list of this container's identities.
     // Do not filter these identities using 
KerberosIdentityDescriptor#shouldInclude since we will do
     // that later.
     List<KerberosIdentityDescriptor> identities = 
container.getIdentities(true, null);
 
-    if((identities != null) && !identities.isEmpty()) {
+    if ((identities != null) && !identities.isEmpty()) {
       Set<String> set = null;
 
       for (KerberosIdentityDescriptor identity : identities) {
@@ -2644,8 +2862,8 @@ public class KerberosHelperImpl implements KerberosHelper 
{
     }
 
     Collection<? extends AbstractKerberosDescriptorContainer> children = 
container.getChildContainers();
-    if(children != null) {
-      for(AbstractKerberosDescriptorContainer child: children) {
+    if (children != null) {
+      for (AbstractKerberosDescriptorContainer child : children) {
         identitiesToRemove = processWhenClauses(currentPath + "/" + 
child.getName(), child, context, identitiesToRemove);
       }
     }
@@ -2702,6 +2920,142 @@ public class KerberosHelperImpl implements 
KerberosHelper {
 
   }
 
+  /**
+   * Gathers the Kerberos-related configurations for services not yet 
installed, but flagged to be
+   * preconfigured.
+   * <p>
+   * Only existing configuration types will be updated, new types will not be 
added since they are
+   * expected only when the relevant service has been installed. This is to 
help reduce the number
+   * of service restarts when new services are added to clusters where 
Kerberos has been enabled.
+   * <p>
+   * If desired, the Stack Advisor will be invoked to request recommended 
hosts for the component.
+   * This is needed to fill out the clusterHostInfo structure in the 
configuration map. For example,
+   * <code>clusterHostInfo/knox_gateway_hosts</code>
+   *
+   * @param configurations           the existing configurations (updated 
in-place)
+   * @param cluster                  the cluster
+   * @param kerberosDescriptor       the kerberos descriptor
+   * @param calculateClusterHostInfo true, to query the Stack Advisor for 
recommended hosts for the
+   *                                 preconfigured services and components; 
false, otherwise
+   * @return the updated configuration map
+   * @throws AmbariException if an error occurs
+   */
+  private Map<String, Map<String, String>> 
addConfigurationsForPreProcessedServices(Map<String, Map<String, String>> 
configurations,
+                                                                               
     Cluster cluster,
+                                                                               
     KerberosDescriptor kerberosDescriptor,
+                                                                               
     boolean calculateClusterHostInfo)
+      throws AmbariException {
+
+    Map<String, KerberosServiceDescriptor> serviceDescriptorMap = 
kerberosDescriptor.getServices();
+
+    if (serviceDescriptorMap != null) {
+      Map<String, Service> existingServices = cluster.getServices();
+      Set<String> allServices = new HashSet<>(existingServices.keySet());
+      Set<String> componentFilter = new HashSet<>();
+      StackId stackVersion = cluster.getCurrentStackVersion();
+
+      for (KerberosServiceDescriptor serviceDescriptor : 
serviceDescriptorMap.values()) {
+        String serviceName = serviceDescriptor.getName();
+        boolean shouldPreconfigure = serviceDescriptor.shouldPreconfigure();
+
+        if (shouldPreconfigure && !existingServices.containsKey(serviceName)) {
+          if (ambariMetaInfo.isValidService(stackVersion.getStackName(), 
stackVersion.getStackVersion(), serviceName)) {
+            ServiceInfo serviceInfo = 
ambariMetaInfo.getService(stackVersion.getStackName(), 
stackVersion.getStackVersion(), serviceName);
+
+            Collection<PropertyInfo> servicePropertiesInfos = 
serviceInfo.getProperties();
+            if (servicePropertiesInfos != null) {
+              Map<String, Map<String, String>> propertiesToAdd = new 
HashMap<>();
+
+              for (PropertyInfo propertyInfo : servicePropertiesInfos) {
+                String type = 
ConfigHelper.fileNameToConfigType(propertyInfo.getFilename());
+
+                Map<String, String> map = propertiesToAdd.get(type);
+                if (map == null) {
+                  map = new HashMap<>();
+                  propertiesToAdd.put(type, map);
+                }
+                map.put(propertyInfo.getName(), propertyInfo.getValue());
+              }
+
+              for (Map.Entry<String, Map<String, String>> entry : 
propertiesToAdd.entrySet()) {
+                if (!configurations.containsKey(entry.getKey())) {
+                  configurations.put(entry.getKey(), entry.getValue());
+                }
+              }
+            }
+
+            // This is only needed if the Stack Advisor is being called to get 
recommended host
+            // for components
+            if (calculateClusterHostInfo) {
+              // Add the service to preconfigure to the all services set for 
use later
+              allServices.add(serviceName);
+
+              // Add the components for the service to preconfigure to the 
component filter
+              List<ComponentInfo> componentInfos = serviceInfo.getComponents();
+              if (componentInfos != null) {
+                for (ComponentInfo componentInfo : componentInfos) {
+                  componentFilter.add(componentInfo.getName());
+                }
+              }
+            }
+          }
+        }
+      }
+
+      if (calculateClusterHostInfo && (allServices.size() > 
existingServices.size())) {
+        applyStackAdvisorHostRecommendations(cluster, allServices, 
componentFilter, configurations);
+      }
+    }
+
+    return configurations;
+  }
+
+  /**
+   * Combines a stack-level Kerberos descriptor with a user-suppled Kerberos 
descriptor to creae a
+   * composite {@link KerberosDescriptor} using the following logic:
+   * <p>
+   * <ul>
+   * <li>
+   * If both the stack-level and the user-supplied Kerberos descriptors are 
<code>null</code>,
+   * return an empty {@link KerberosDescriptor}.
+   * </li>
+   * <li>
+   * If the stack-level Kerberos descriptor is <code>null</code> and the 
user-supplied Kerberos
+   * descriptor is <code>non-null</code>, return the user-supplied Kerberos 
descriptor.
+   * </li>
+   * <li>
+   * If the stack-level Kerberos descriptor is <code>non-null</code> and the 
user-supplied
+   * Kerberos descriptor is <code>null</code>, return the stack-level Kerberos 
descriptor.
+   * </li>
+   * <li>
+   * If neither the stack-level nor the user-supplied Kerberos descriptors are 
<code>null</code>,
+   * return the stack-level Kerberos descriptor that has been updated using 
data from the
+   * user-supplied Kerberos descriptor.
+   * </li>
+   * </ul>
+   *
+   * @param stackDescriptor the stack-level Keberos descriptor
+   * @param userDescriptor  the user-supplied Kerberos descriptor
+   * @return a KerberosDescriptor
+   */
+  private KerberosDescriptor combineKerberosDescriptors(KerberosDescriptor 
stackDescriptor, KerberosDescriptor userDescriptor) {
+    KerberosDescriptor kerberosDescriptor ;
+    if (stackDescriptor == null) {
+      if (userDescriptor == null) {
+        return new KerberosDescriptor();  // return an empty Kerberos 
descriptor since we have no data
+      } else {
+        kerberosDescriptor = userDescriptor;
+      }
+    } else {
+      if (userDescriptor != null) {
+        stackDescriptor.update(userDescriptor);
+      }
+      kerberosDescriptor = stackDescriptor;
+    }
+
+    return kerberosDescriptor;
+  }
+
   /* 
********************************************************************************************
    * Helper classes and enums
    * 
********************************************************************************************
 *\
@@ -3034,19 +3388,18 @@ public class KerberosHelperImpl implements 
KerberosHelper {
     }
 
     void addDisableSecurityHookStage(Cluster cluster,
-                                            String clusterHostInfoJson,
-                                            String hostParamsJson,
-                                            Map<String, String> 
commandParameters,
-                                            RoleCommandOrder roleCommandOrder,
-                                            RequestStageContainer 
requestStageContainer)
-      throws AmbariException
-    {
+                                     String clusterHostInfoJson,
+                                     String hostParamsJson,
+                                     Map<String, String> commandParameters,
+                                     RoleCommandOrder roleCommandOrder,
+                                     RequestStageContainer 
requestStageContainer)
+        throws AmbariException {
       Stage stage = createNewStage(requestStageContainer.getLastStageId(),
-        cluster,
-        requestStageContainer.getId(),
-        "Disable security",
-        StageUtils.getGson().toJson(commandParameters),
-        hostParamsJson);
+          cluster,
+          requestStageContainer.getId(),
+          "Disable security",
+          StageUtils.getGson().toJson(commandParameters),
+          hostParamsJson);
       addDisableSecurityCommandToAllServices(cluster, stage);
       RoleGraph roleGraph = roleGraphFactory.createNew(roleCommandOrder);
       roleGraph.build(stage);
@@ -3058,27 +3411,26 @@ public class KerberosHelperImpl implements 
KerberosHelper {
     private void addDisableSecurityCommandToAllServices(Cluster cluster, Stage 
stage) throws AmbariException {
       for (Service service : cluster.getServices().values()) {
         for (ServiceComponent component : 
service.getServiceComponents().values()) {
-            if (!component.getServiceComponentHosts().isEmpty()) {
-              String firstHost = 
component.getServiceComponentHosts().keySet().iterator().next(); // it is only 
necessary to send it to one host
-              ActionExecutionContext exec = new ActionExecutionContext(
+          if (!component.getServiceComponentHosts().isEmpty()) {
+            String firstHost = 
component.getServiceComponentHosts().keySet().iterator().next(); // it is only 
necessary to send it to one host
+            ActionExecutionContext exec = new ActionExecutionContext(
                 cluster.getClusterName(),
                 "DISABLE_SECURITY",
                 singletonList(new RequestResourceFilter(service.getName(), 
component.getName(), singletonList(firstHost))),
                 Collections.<String, String>emptyMap());
-              customCommandExecutionHelper.addExecutionCommandsToStage(exec, 
stage, Collections.<String, String>emptyMap(), null);
+            customCommandExecutionHelper.addExecutionCommandsToStage(exec, 
stage, Collections.<String, String>emptyMap(), null);
           }
         }
       }
     }
 
     void addStopZookeeperStage(Cluster cluster,
-                                      String clusterHostInfoJson,
-                                      String hostParamsJson,
-                                      Map<String, String> commandParameters,
-                                      RoleCommandOrder roleCommandOrder,
-                                      RequestStageContainer 
requestStageContainer)
-      throws AmbariException
-    {
+                               String clusterHostInfoJson,
+                               String hostParamsJson,
+                               Map<String, String> commandParameters,
+                               RoleCommandOrder roleCommandOrder,
+                               RequestStageContainer requestStageContainer)
+        throws AmbariException {
       Service zookeeper;
       try {
         zookeeper = cluster.getService("ZOOKEEPER");
@@ -3086,19 +3438,19 @@ public class KerberosHelperImpl implements 
KerberosHelper {
         return;
       }
       Stage stage = createNewStage(requestStageContainer.getLastStageId(),
-        cluster,
-        requestStageContainer.getId(),
-        "Stopping ZooKeeper",
-        StageUtils.getGson().toJson(commandParameters),
-        hostParamsJson);
+          cluster,
+          requestStageContainer.getId(),
+          "Stopping ZooKeeper",
+          StageUtils.getGson().toJson(commandParameters),
+          hostParamsJson);
       for (ServiceComponent component : 
zookeeper.getServiceComponents().values()) {
-          Set<String> hosts = component.getServiceComponentHosts().keySet();
-          ActionExecutionContext exec = new ActionExecutionContext(
+        Set<String> hosts = component.getServiceComponentHosts().keySet();
+        ActionExecutionContext exec = new ActionExecutionContext(
             cluster.getClusterName(),
             "STOP",
             singletonList(new RequestResourceFilter(zookeeper.getName(), 
component.getName(), new ArrayList<>(hosts))),
             Collections.<String, String>emptyMap());
-          customCommandExecutionHelper.addExecutionCommandsToStage(exec, 
stage, Collections.<String, String>emptyMap(), null);
+        customCommandExecutionHelper.addExecutionCommandsToStage(exec, stage, 
Collections.<String, String>emptyMap(), null);
       }
       RoleGraph roleGraph = roleGraphFactory.createNew(roleCommandOrder);
       roleGraph.build(stage);
@@ -3303,6 +3655,7 @@ public class KerberosHelperImpl implements KerberosHelper 
{
       commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATIONS, 
"true");
       commandParameters.put(KerberosServerAction.DEFAULT_REALM, 
kerberosDetails.getDefaultRealm());
       commandParameters.put(KerberosServerAction.INCLUDE_AMBARI_IDENTITY, 
(kerberosDetails.createAmbariPrincipal()) ? "true" : "false");
+      commandParameters.put(KerberosServerAction.PRECONFIGURE_SERVICES, 
kerberosDetails.getPreconfigureServices());
 
       if (dataDirectory != null) {
         commandParameters.put(KerberosServerAction.DATA_DIRECTORY, 
dataDirectory.getAbsolutePath());
@@ -3434,10 +3787,10 @@ public class KerberosHelperImpl implements 
KerberosHelper {
       }
 
       addDisableSecurityHookStage(cluster, clusterHostInfoJson, 
hostParamsJson, commandParameters,
-        roleCommandOrder, requestStageContainer);
+          roleCommandOrder, requestStageContainer);
 
       addStopZookeeperStage(cluster, clusterHostInfoJson, hostParamsJson, 
commandParameters,
-        roleCommandOrder, requestStageContainer);
+          roleCommandOrder, requestStageContainer);
 
       // *****************************************************************
       // Create stage to prepare operations
@@ -3790,5 +4143,9 @@ public class KerberosHelperImpl implements KerberosHelper 
{
       return (kerberosEnvProperties == null) ||
           
!"false".equalsIgnoreCase(kerberosEnvProperties.get(CREATE_AMBARI_PRINCIPAL));
     }
+
+    public String getPreconfigureServices() {
+      return (kerberosEnvProperties == null) ? "" : 
kerberosEnvProperties.get(PRECONFIGURE_SERVICES);
+    }
   }
 }
\ No newline at end of file

Reply via email to