This is an automated email from the ASF dual-hosted git repository.

hapylestat pushed a commit to branch branch-2.7
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-2.7 by this push:
     new 17dc5fd  AMBARI-25332. Kerberos keytab regeneration working slow 
(dgrinenko) (#3120)
17dc5fd is described below

commit 17dc5fd5610e0037e197934ceb27b61e087c8d57
Author: Dmytro Grinenko <hapyles...@apache.org>
AuthorDate: Fri Nov 8 12:54:42 2019 +0200

    AMBARI-25332. Kerberos keytab regeneration working slow (dgrinenko) (#3120)
---
 .../ambari/server/configuration/Configuration.java |  19 ++-
 .../ambari/server/controller/KerberosHelper.java   |  60 ++++---
 .../server/controller/KerberosHelperImpl.java      |  83 +++++++--
 .../HostKerberosIdentityResourceProvider.java      |  11 +-
 .../ambari/server/orm/dao/KerberosKeytabDAO.java   |  14 +-
 .../server/orm/dao/KerberosKeytabPrincipalDAO.java |  22 ++-
 .../server/orm/dao/KerberosPrincipalDAO.java       |   6 +-
 .../server/orm/entities/KerberosKeytabEntity.java  |   7 +-
 .../entities/KerberosKeytabPrincipalEntity.java    |  30 +++-
 .../ConfigureAmbariIdentitiesServerAction.java     |  37 ++--
 .../kerberos/CreateKeytabFilesServerAction.java    | 190 +++++++++++----------
 .../kerberos/CreatePrincipalsServerAction.java     |  63 ++++---
 .../kerberos/FinalizeKerberosServerAction.java     |  17 +-
 .../kerberos/KerberosServerAction.java             |  69 +++++++-
 .../stageutils/KerberosKeytabController.java       |  40 ++++-
 .../server/controller/KerberosHelperTest.java      |  18 +-
 .../HostKerberosIdentityResourceProviderTest.java  |  18 +-
 .../ConfigureAmbariIdentitiesServerActionTest.java |  13 +-
 18 files changed, 530 insertions(+), 187 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index 6b0e383..87549a8 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -2616,6 +2616,13 @@ public class Configuration {
   public static final ConfigurationProperty<Integer> 
KERBEROS_SERVER_ACTION_FINALIZE_SECONDS = new ConfigurationProperty<>(
     "server.kerberos.finalize.timeout", 600);
 
+  /**
+   * The number of threads to use when executing server-side Kerberos 
commands, such as generate keytabs.
+   */
+  @Markdown(description = "The number of threads to use when executing 
server-side Kerberos commands, such as generate keytabs.")
+  public static final ConfigurationProperty<Integer> 
KERBEROS_SERVER_ACTION_THREADPOOL_SIZE = new ConfigurationProperty<>(
+    "server.kerberos.action.threadpool.size", 1);
+
   private static final Logger LOG = LoggerFactory.getLogger(
     Configuration.class);
 
@@ -3041,7 +3048,7 @@ public class Configuration {
     writeConfigFile(existingProperties, false);
 
     // reloading properties
-    this.properties = readConfigFile();
+    properties = readConfigFile();
   }
 
   /**
@@ -5569,6 +5576,16 @@ public class Configuration {
   }
 
   /**
+   * Gets the number of threads to use when executing server-side Kerberos
+   * commands, such as generate keytabs.
+   *
+   * @return the threadpool size, defaulting to 1
+   */
+  public int getKerberosServerActionThreadpoolSize() {
+    return 
Integer.parseInt(getProperty(KERBEROS_SERVER_ACTION_THREADPOOL_SIZE));
+  }
+
+  /**
    * Get the timeout, in seconds, when finalizing Kerberos
    * enable/disable/regenerate commands.
    *
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 3c4d6b2..5f84968 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
@@ -704,32 +704,52 @@ public interface KerberosHelper {
   Map<String, Map<String, String>> 
getIdentityConfigurations(List<KerberosIdentityDescriptor> identityDescriptors);
 
   /**
-   * Returns the active identities for the named cluster.  Results are 
filtered by host, service,
-   * and/or component; and grouped by host.
+   * Returns the active identities for the named cluster. Results are filtered
+   * by host, service, and/or component; and grouped by host.
    * <p/>
-   * The cluster name is mandatory; however the active identities may be 
filtered by one or more of
-   * host, service, or component. A <code>null</code> value for any of these 
filters indicates no
-   * filter for that parameter.
+   * The cluster name is mandatory; however the active identities may be
+   * filtered by one or more of host, service, or component. A 
<code>null</code>
+   * value for any of these filters indicates no filter for that parameter.
    * <p/>
-   * The return values are grouped by host and optionally <code>_HOST</code> 
in principals will be
-   * replaced with the relevant hostname if specified to do so.
-   *
-   * @param clusterName      the name of the relevant cluster (mandatory)
-   * @param hostName         the name of a host for which to find results, 
null indicates all hosts
-   * @param serviceName      the name of a service for which to find results, 
null indicates all
-   *                         services
-   * @param componentName    the name of a component for which to find 
results, null indicates all
-   *                         components
-   * @param replaceHostNames if true, _HOST in principals will be replace with 
the relevant host
-   *                         name
+   * The return values are grouped by host and optionally <code>_HOST</code> in
+   * principals will be replaced with the relevant hostname if specified to do
+   * so.
+   *
+   * @param clusterName
+   *          the name of the relevant cluster (mandatory)
+   * @param hostName
+   *          the name of a host for which to find results, null indicates all
+   *          hosts
+   * @param serviceName
+   *          the name of a service for which to find results, null indicates
+   *          all services
+   * @param componentName
+   *          the name of a component for which to find results, null indicates
+   *          all components
+   * @param replaceHostNames
+   *          if true, _HOST in principals will be replace with the relevant
+   *          host name
+   * @param hostConfigurations
+   *          a mapping of hostname to configurations for that host. Fetching
+   *          this ahead of time for every host in the cluster will ensure that
+   *          this method doesn't need to do it inside of a loop. If
+   *          {@code null} or empty, then this method will do the lookup 
itself,
+   *          at considerable cost.
+   * @param kerberosDescriptor
+   *          the kerberos descriptor to use when looking up identities. If
+   *          {@code null}, then this method will deserialize the descriptor
+   *          inside of a loop at considerable cost.
    * @return a map of host names to kerberos identities
-   * @throws AmbariException if an error occurs processing the cluster's 
active identities
+   * @throws AmbariException
+   *           if an error occurs processing the cluster's active identities
    */
   Map<String, Collection<KerberosIdentityDescriptor>> 
getActiveIdentities(String clusterName,
                                                                           
String hostName,
                                                                           
String serviceName,
                                                                           
String componentName,
-                                                                          
boolean replaceHostNames)
+                                                                          
boolean replaceHostNames,
+                                                                          
Map<String, Map<String, Map<String, String>>> hostConfigurations,
+                                                                          
KerberosDescriptor kerberosDescriptor)
       throws AmbariException;
 
   /**
@@ -814,7 +834,7 @@ public interface KerberosHelper {
    * @return a map of configuration types to sets of property names
    */
   Map<String, Set<String>> 
translateConfigurationSpecifications(Collection<String> 
configurationSpecifications);
-  
+
   /**
    * Gathers the Kerberos-related data from configurations and stores it in a 
new KerberosDetails
    * instance.
@@ -827,7 +847,7 @@ public interface KerberosHelper {
    * @throws AmbariException
    */
   KerberosDetails getKerberosDetails(Cluster cluster, Boolean manageIdentities)
-    throws KerberosInvalidConfigurationException, AmbariException;  
+    throws KerberosInvalidConfigurationException, AmbariException;
 
   /**
    * Types of Kerberos descriptors related to where the data is stored.
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 00b74e0..987b77d 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
@@ -37,6 +37,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 
 import org.apache.ambari.annotations.Experimental;
@@ -63,6 +64,7 @@ import org.apache.ambari.server.orm.dao.ArtifactDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.KerberosKeytabDAO;
 import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+import 
org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO.KeytabPrincipalFindOrCreateResult;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
 import org.apache.ambari.server.orm.entities.ArtifactEntity;
 import org.apache.ambari.server.orm.entities.HostEntity;
@@ -137,6 +139,7 @@ import 
org.apache.directory.server.kerberos.shared.keytab.Keytab;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Stopwatch;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
@@ -1745,7 +1748,9 @@ public class KerberosHelperImpl implements KerberosHelper 
{
                                                                                
  String hostName,
                                                                                
  String serviceName,
                                                                                
  String componentName,
-                                                                               
  boolean replaceHostNames)
+                                                                               
  boolean replaceHostNames,
+                                                                               
  Map<String, Map<String, Map<String, String>>> hostConfigurations,
+                                                                               
  KerberosDescriptor kerberosDescriptor)
     throws AmbariException {
 
     if ((clusterName == null) || clusterName.isEmpty()) {
@@ -1790,8 +1795,15 @@ public class KerberosHelperImpl implements 
KerberosHelper {
         hosts = Collections.singleton(hostName);
       }
 
+      if (null == hostConfigurations) {
+        hostConfigurations = new HashMap<>();
+      }
+
       if (!hosts.isEmpty()) {
-        KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, 
false);
+
+        if (null == kerberosDescriptor) {
+          kerberosDescriptor = getKerberosDescriptor(cluster, false);
+        }
 
         if (kerberosDescriptor != null) {
           Set<String> existingServices = cluster.getServices().keySet();
@@ -1799,12 +1811,17 @@ public class KerberosHelperImpl implements 
KerberosHelper {
           for (String host : hosts) {
             // Calculate the current host-specific configurations. These will 
be used to replace
             // variables within the Kerberos descriptor data
-            Map<String, Map<String, String>> configurations = 
calculateConfigurations(cluster,
+            Map<String, Map<String, String>> configurations = 
hostConfigurations.get(host);
+            if (configurations == null) {
+              configurations = calculateConfigurations(cluster,
                 (ambariServerHostnameIsForced && 
ambariServerHostname.equals(host)) ? null : host,
                 kerberosDescriptor,
                 false,
                 false);
 
+              hostConfigurations.put(host, configurations);
+            }
+
             // Create the context to use for filtering Kerberos Identities 
based on the state of the cluster
             Map<String, Object> filterContext = new HashMap<>();
             filterContext.put("configurations", configurations);
@@ -1951,15 +1968,27 @@ public class KerberosHelperImpl implements 
KerberosHelper {
   }
 
   /**
-   * Creates and saves  underlying  {@link 
org.apache.ambari.server.orm.entities.KerberosPrincipalEntity},
-   * {@link org.apache.ambari.server.orm.entities.KerberosKeytabEntity} 
entities in JPA storage.
+   * Creates and saves underlying
+   * {@link org.apache.ambari.server.orm.entities.KerberosPrincipalEntity},
+   * {@link org.apache.ambari.server.orm.entities.KerberosKeytabEntity} 
entities
+   * in JPA storage.
+   * <p>
+   * This method has to be very, very careful WRT how and when it merges
+   * bidirectional associations.For larger cluster, merging the
+   * {@link KerberosKeytabEntity} and {@link KerberosPrincipalEntity} even when
+   * a {@link KerberosKeytabPrincipalEntity} will result in major performance
+   * problems.
    *
-   * @param resolvedKerberosKeytab kerberos keytab to be persisted
+   * @param resolvedKerberosKeytab
+   *          kerberos keytab to be persisted
    */
   @Override
   public void createResolvedKeytab(ResolvedKerberosKeytab 
resolvedKerberosKeytab) {
-    if (kerberosKeytabDAO.find(resolvedKerberosKeytab.getFile()) == null) {
-      KerberosKeytabEntity kke = new 
KerberosKeytabEntity(resolvedKerberosKeytab.getFile());
+    Stopwatch stopwatch = Stopwatch.createStarted();
+
+    KerberosKeytabEntity kke = 
kerberosKeytabDAO.find(resolvedKerberosKeytab.getFile());
+    if (null == kke) {
+      kke = new KerberosKeytabEntity(resolvedKerberosKeytab.getFile());
       kke.setAmbariServerKeytab(resolvedKerberosKeytab.isAmbariServerKeytab());
       
kke.setWriteAmbariJaasFile(resolvedKerberosKeytab.isMustWriteAmbariJaasFile());
       kke.setOwnerName(resolvedKerberosKeytab.getOwnerName());
@@ -1968,24 +1997,43 @@ public class KerberosHelperImpl implements 
KerberosHelper {
       kke.setGroupAccess(resolvedKerberosKeytab.getGroupAccess());
       kerberosKeytabDAO.create(kke);
     }
+
     for (ResolvedKerberosPrincipal principal : 
resolvedKerberosKeytab.getPrincipals()) {
-      if (!kerberosPrincipalDAO.exists(principal.getPrincipal())) {
-        kerberosPrincipalDAO.create(principal.getPrincipal(), 
principal.isService());
+      KerberosPrincipalEntity kpe = 
kerberosPrincipalDAO.find(principal.getPrincipal());
+
+      if (null == kpe) {
+        kpe = kerberosPrincipalDAO.create(principal.getPrincipal(), 
principal.isService());
       }
+
+      // only need to merge the kke and kpe if a new kkp is created/added to 
their lists
+      boolean mergeBidirectionalAssociatedEntities = false;
       for (Map.Entry<String, String> mappingEntry : 
principal.getServiceMapping().entries()) {
         String serviceName = mappingEntry.getKey();
         HostEntity hostEntity = principal.getHostId() != null ? 
hostDAO.findById(principal.getHostId()) : null;
-        KerberosKeytabEntity kke = 
kerberosKeytabDAO.find(resolvedKerberosKeytab.getFile());
-        KerberosPrincipalEntity kpe = 
kerberosPrincipalDAO.find(principal.getPrincipal());
 
-        KerberosKeytabPrincipalEntity kkp = 
kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe);
+        KeytabPrincipalFindOrCreateResult result = 
kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe);
+        KerberosKeytabPrincipalEntity kkp = result.kkp;
+        mergeBidirectionalAssociatedEntities = 
mergeBidirectionalAssociatedEntities || result.created;
+
+        // updating the kkp service mappings does not affect kke/kpe 
bidirectional relationships
         if (kkp.putServiceMapping(serviceName, mappingEntry.getValue())) {
           kerberosKeytabPrincipalDAO.merge(kkp);
         }
-        kerberosKeytabDAO.merge(kke);
-        kerberosPrincipalDAO.merge(kpe);
+      }
+
+      // merge the keytab and the principal IFF at least one keytabprincipal 
was
+      // created causing the bi-directional lists associations to need updating
+      if(mergeBidirectionalAssociatedEntities) {
+        Stopwatch mergeStockwatch = Stopwatch.createStarted();
+        kke = kerberosKeytabDAO.merge(kke);
+        kpe = kerberosPrincipalDAO.merge(kpe);
+        LOG.info("Merging bidirectional associated entities for this keytab 
took {}ms"
+            + mergeStockwatch.elapsed(TimeUnit.MILLISECONDS));
       }
     }
+
+    LOG.info("Resolving this keytab and all associated principals took {}ms ",
+        stopwatch.elapsed(TimeUnit.MILLISECONDS));
   }
 
   @Override
@@ -2392,7 +2440,10 @@ public class KerberosHelperImpl implements 
KerberosHelper {
                 kerberosPrincipalDAO.create(kpe);
               }
 
-              KerberosKeytabPrincipalEntity kkp = 
kerberosKeytabPrincipalDAO.findOrCreate(kke, 
hostDAO.findById(sch.getHost().getHostId()), kpe);
+              KeytabPrincipalFindOrCreateResult result = 
kerberosKeytabPrincipalDAO.findOrCreate(
+                  kke, hostDAO.findById(sch.getHost().getHostId()), kpe);
+
+              KerberosKeytabPrincipalEntity kkp = result.kkp;
               if (kkp.putServiceMapping(sch.getServiceName(), 
sch.getServiceComponentName())) {
                 kerberosKeytabPrincipalDAO.merge(kkp);
               }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
index d90d5bf..3b82801 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
@@ -40,6 +40,9 @@ import 
org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
@@ -158,9 +161,15 @@ public class HostKerberosIdentityResourceProvider extends 
ReadOnlyResourceProvid
         String clusterName = (String) 
propertyMap.get(KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID);
         String hostName = (String) 
propertyMap.get(KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID);
 
+        Clusters clusters = getManagementController().getClusters();        
+        Cluster cluster = clusters.getCluster(clusterName);
+            
+        KerberosDescriptor kerberosDescriptor = 
kerberosHelper.getKerberosDescriptor(cluster, false);
+        
         // Retrieve the active identities for the cluster filtered and grouped 
by hostname
         Map<String, Collection<KerberosIdentityDescriptor>> hostDescriptors =
-            kerberosHelper.getActiveIdentities(clusterName, hostName, null, 
null, true);
+            kerberosHelper.getActiveIdentities(clusterName, hostName, null, 
null, true, null, 
+                kerberosDescriptor);
 
         if (hostDescriptors != null) {
           for (Map.Entry<String, Collection<KerberosIdentityDescriptor>> entry 
: hostDescriptors.entrySet()) {
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
index 60ec754..c999f9b 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
@@ -21,6 +21,7 @@ package org.apache.ambari.server.orm.dao;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -32,6 +33,7 @@ import org.apache.commons.collections.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Stopwatch;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -88,9 +90,15 @@ public class KerberosKeytabDAO {
 
   @RequiresSession
   public List<KerberosKeytabEntity> findByPrincipalAndHost(String 
principalName, Long hostId) {
+    Stopwatch stopwatch = Stopwatch.createStarted();
     if(hostId == null) {
-      return findByPrincipalAndNullHost(principalName);
+      List<KerberosKeytabEntity> result = 
findByPrincipalAndNullHost(principalName);
+      LOG.debug("Loading keytabs by principal name took {}ms",
+          stopwatch.elapsed(TimeUnit.MILLISECONDS));
+
+      return result;
     }
+
     TypedQuery<KerberosKeytabEntity> query = entityManagerProvider.get().
       createNamedQuery("KerberosKeytabEntity.findByPrincipalAndHost", 
KerberosKeytabEntity.class);
     query.setParameter("hostId", hostId);
@@ -99,6 +107,10 @@ public class KerberosKeytabDAO {
     if(result == null) {
       return Collections.emptyList();
     }
+
+    LOG.debug("Loading keytabs by principal name and host took {}ms",
+        stopwatch.elapsed(TimeUnit.MILLISECONDS));
+
     return result;
   }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
index 7a44f2c..bb8ac6c 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
@@ -74,20 +74,28 @@ public class KerberosKeytabPrincipalDAO {
    * @param kerberosPrincipalEntity      {@link KerberosPrincipalEntity} which 
related to this principal
    * @return evaluated entity
    */
-  public KerberosKeytabPrincipalEntity findOrCreate(KerberosKeytabEntity 
kerberosKeytabEntity, HostEntity hostEntity, KerberosPrincipalEntity 
kerberosPrincipalEntity) {
+  public KeytabPrincipalFindOrCreateResult findOrCreate(KerberosKeytabEntity 
kerberosKeytabEntity, HostEntity hostEntity, KerberosPrincipalEntity 
kerberosPrincipalEntity) {
+    KeytabPrincipalFindOrCreateResult result = new 
KeytabPrincipalFindOrCreateResult();
+    result.created = false;
+
     Long hostId = hostEntity == null ? null : hostEntity.getHostId();
     KerberosKeytabPrincipalEntity kkp = findByNaturalKey(hostId, 
kerberosKeytabEntity.getKeytabPath(), 
kerberosPrincipalEntity.getPrincipalName());
     if (kkp == null) {
+      result.created = true;
+
       kkp = new KerberosKeytabPrincipalEntity(
           kerberosKeytabEntity,
           hostEntity,
           kerberosPrincipalEntity
       );
       create(kkp);
+
       kerberosKeytabEntity.addKerberosKeytabPrincipal(kkp);
       kerberosPrincipalEntity.addKerberosKeytabPrincipal(kkp);
     }
-    return kkp;
+
+    result.kkp = kkp;
+    return result;
   }
 
   @Transactional
@@ -345,4 +353,14 @@ public class KerberosKeytabPrincipalDAO {
           principalNames);
     }
   }
+
+  /**
+   * Used to return a keytab principal and whether or not it was created. This
+   * is required so that callers know whether associated keytabs and principals
+   * need bi-direcitonal merges.
+   */
+  public static class KeytabPrincipalFindOrCreateResult {
+    public KerberosKeytabPrincipalEntity kkp;
+    public boolean created;
+  }
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
index 8774ca8..5db8590 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
@@ -69,8 +69,10 @@ public class KerberosPrincipalDAO {
    * @param service       a boolean value declaring whether the principal 
represents a service (true) or not )false).
    */
   @Transactional
-  public void create(String principalName, boolean service) {
-    create(new KerberosPrincipalEntity(principalName, service, null));
+  public KerberosPrincipalEntity create(String principalName, boolean service) 
{
+    KerberosPrincipalEntity kpe = new KerberosPrincipalEntity(principalName, 
service, null);
+    create(kpe);
+    return kpe;
   }
 
   /**
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
index 4fb4f96..b93bfa1 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
@@ -29,6 +29,7 @@ import javax.persistence.Id;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
+import javax.persistence.QueryHint;
 import javax.persistence.Table;
 
 @Entity
@@ -37,8 +38,10 @@ import javax.persistence.Table;
   @NamedQuery(name = "KerberosKeytabEntity.findAll", query = "SELECT kk FROM 
KerberosKeytabEntity kk"),
   @NamedQuery(
     name = "KerberosKeytabEntity.findByPrincipalAndHost",
-    query = "SELECT kk FROM KerberosKeytabEntity kk JOIN 
kk.kerberosKeytabPrincipalEntities kkp WHERE kkp.hostId=:hostId AND 
kkp.principalName=:principalName"
-  ),
+        query = "SELECT kk FROM KerberosKeytabEntity kk, 
KerberosKeytabPrincipalEntity kkp WHERE kkp.hostId=:hostId AND 
kkp.principalName=:principalName AND kkp.keytabPath = kk.keytabPath",
+        hints = {
+            @QueryHint(name = "eclipselink.query-results-cache", value = 
"true"),
+            @QueryHint(name = "eclipselink.query-results-cache.size", value = 
"500") }),
   @NamedQuery(
     name = "KerberosKeytabEntity.findByPrincipalAndNullHost",
     query = "SELECT kk FROM KerberosKeytabEntity kk JOIN 
kk.kerberosKeytabPrincipalEntities kkp WHERE kkp.hostId IS NULL AND 
kkp.principalName=:principalName"
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
index 676b3e6..3f87186 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
@@ -24,6 +24,7 @@ import java.util.List;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
@@ -32,9 +33,12 @@ import javax.persistence.ManyToOne;
 import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.OneToMany;
+import javax.persistence.QueryHint;
 import javax.persistence.Table;
 import javax.persistence.TableGenerator;
 
+import 
org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
+
 import com.google.common.base.Objects;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
@@ -72,6 +76,12 @@ import com.google.common.collect.Multimap;
     query = "SELECT kkpe FROM KerberosKeytabPrincipalEntity kkpe WHERE 
kkpe.hostId=:hostId"
   ),
   @NamedQuery(
+        name = "KerberosKeytabPrincipalEntity.findByPrincipalAndHost",
+        query = "SELECT kkpe FROM KerberosKeytabPrincipalEntity kkpe WHERE 
kkpe.hostId=:hostId AND kkpe.principalName=:principalName",
+        hints = {
+            @QueryHint(name = "eclipselink.query-results-cache", value = 
"true"),
+            @QueryHint(name = "eclipselink.query-results-cache.size", value = 
"500") }),
+    @NamedQuery(
     name = "KerberosKeytabPrincipalEntity.findByHostKeytabAndPrincipal",
     query = "SELECT kkpe FROM KerberosKeytabPrincipalEntity kkpe WHERE 
kkpe.hostId=:hostId AND kkpe.keytabPath=:keytabPath AND 
kkpe.principalName=:principalName"
   ),
@@ -98,7 +108,12 @@ public class KerberosKeytabPrincipalEntity {
   @Column(name = "is_distributed", nullable = false)
   private Integer isDistributed = 0;
 
-  @ManyToOne
+  /**
+   * The assocaited keytab entity must be fetched {@link FetchType#EAGER} to
+   * ensure that it can be discovered when only the principal and host are
+   * known.
+   */
+  @ManyToOne(fetch = FetchType.EAGER)
   @JoinColumn(name = "keytab_path", referencedColumnName = "keytab_path", 
updatable = false, nullable = false, insertable = false)
   private KerberosKeytabEntity kerberosKeytabEntity;
 
@@ -110,7 +125,16 @@ public class KerberosKeytabPrincipalEntity {
   @JoinColumn(name = "principal_name", referencedColumnName = 
"principal_name", updatable = false, nullable = false, insertable = false)
   private KerberosPrincipalEntity kerberosPrincipalEntity;
 
-  @OneToMany(cascade = CascadeType.ALL, mappedBy = 
"kerberosKeytabPrincipalEntity", orphanRemoval = true)
+  /**
+   * Service mappings must be fetched {@link FetchType#EAGER} since they are
+   * required when processing {@link KerberosKeytabPrincipalEntity} instances
+   * and turning them into {@link ResolvedKerberosPrincipal}.
+   */
+  @OneToMany(
+      cascade = CascadeType.ALL,
+      mappedBy = "kerberosKeytabPrincipalEntity",
+      orphanRemoval = true,
+      fetch = FetchType.EAGER)
   private List<KerberosKeytabServiceMappingEntity> serviceMapping = new 
ArrayList<>();
 
   public KerberosKeytabPrincipalEntity() {
@@ -144,7 +168,7 @@ public class KerberosKeytabPrincipalEntity {
   }
 
   public void setKerberosKeytabEntity(KerberosKeytabEntity kke) {
-    this.kerberosKeytabEntity = kke;
+    kerberosKeytabEntity = kke;
     if (kke != null) {
       keytabPath = kke.getKeytabPath();
     }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
index a2ddf12..8e8eeb9 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.agent.CommandReport;
@@ -46,6 +47,7 @@ import org.apache.commons.io.FileUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.Striped;
 import com.google.inject.Inject;
 
 /**
@@ -79,6 +81,11 @@ public class ConfigureAmbariIdentitiesServerAction extends 
KerberosServerAction
   private HostDAO hostDAO;
 
   /**
+   * Used to prevent multiple threads from working with the same keytab.
+   */
+  private Striped<Lock> m_locksByKeytab = Striped.lazyWeakLock(25);  
+  
+  /**
    * Called to execute this action.  Upon invocation, calls
    * {@link KerberosServerAction#processIdentities(Map)} )}
    * to iterate through the Kerberos identity metadata and call
@@ -136,18 +143,24 @@ public class ConfigureAmbariIdentitiesServerAction 
extends KerberosServerAction
           File hostDirectory = new File(dataDirectory, hostName);
           File srcKeytabFile = new File(hostDirectory, 
DigestUtils.sha256Hex(destKeytabFilePath));
 
-          if (srcKeytabFile.exists()) {
-            String ownerAccess = keytab.getOwnerAccess();
-            String groupAccess = keytab.getGroupAccess();
-
-            installAmbariServerIdentity(resolvedPrincipal, 
srcKeytabFile.getAbsolutePath(), destKeytabFilePath,
-              keytab.getOwnerName(), ownerAccess,
-              keytab.getGroupName(), groupAccess, actionLog);
-
-            if (serviceMappingEntry.getValue().contains("AMBARI_SERVER_SELF")) 
{
-              // Create/update the JAASFile...
-              configureJAAS(resolvedPrincipal.getPrincipal(), 
destKeytabFilePath, actionLog);
+          Lock lock = m_locksByKeytab.get(destKeytabFilePath);
+          lock.lock();
+          try {
+            if (srcKeytabFile.exists()) {
+              String ownerAccess = keytab.getOwnerAccess();
+              String groupAccess = keytab.getGroupAccess();
+  
+              installAmbariServerIdentity(resolvedPrincipal, 
srcKeytabFile.getAbsolutePath(), destKeytabFilePath,
+                keytab.getOwnerName(), ownerAccess,
+                keytab.getGroupName(), groupAccess, actionLog);
+  
+              if 
(serviceMappingEntry.getValue().contains("AMBARI_SERVER_SELF")) {
+                // Create/update the JAASFile...
+                configureJAAS(resolvedPrincipal.getPrincipal(), 
destKeytabFilePath, actionLog);
+              }
             }
+          } finally {
+            lock.unlock();
           }
         }
       }
@@ -215,7 +228,7 @@ public class ConfigureAmbariIdentitiesServerAction extends 
KerberosServerAction
       for(Map.Entry<String, String> mapping : 
principal.getServiceMapping().entries()) {
         String serviceName = mapping.getKey();
         String componentName = mapping.getValue();
-        KerberosKeytabPrincipalEntity entity = 
kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe);
+        KerberosKeytabPrincipalEntity entity = 
kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe).kkp;
         entity.setDistributed(true);
         entity.putServiceMapping(serviceName, componentName);
         kerberosKeytabPrincipalDAO.merge(entity);
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
index 351e861..6af7c56 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
@@ -20,11 +20,12 @@ package org.apache.ambari.server.serveraction.kerberos;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
@@ -32,7 +33,6 @@ import org.apache.ambari.server.agent.CommandReport;
 import 
org.apache.ambari.server.audit.event.kerberos.CreateKeyTabKerberosAuditEvent;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.KerberosHelper;
-import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
 import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
 import org.apache.ambari.server.serveraction.ActionLog;
@@ -44,6 +44,7 @@ import 
org.apache.directory.server.kerberos.shared.keytab.Keytab;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.Striped;
 import com.google.inject.Inject;
 
 /**
@@ -71,20 +72,19 @@ public class CreateKeytabFilesServerAction extends 
KerberosServerAction {
   @Inject
   private Configuration configuration;
 
-  /**
-   * HostDAO used to retrieveHost Entity object
-   */
-  @Inject
-  private HostDAO hostDAO;
-
   @Inject
   private KerberosKeytabController kerberosKeytabController;
 
   /**
+   * Used to prevent multiple threads from working with the same keytab.
+   */
+  private Striped<Lock> m_locksByKeytab = Striped.lazyWeakLock(25);
+
+  /**
    * A map of data used to track what has been processed in order to optimize 
the creation of keytabs
    * such as knowing when to create a cached keytab file or use a cached 
keytab file.
    */
-  Map<String, Set<String>> visitedIdentities = new HashMap<>();
+  Map<String, Set<String>> visitedIdentities = new ConcurrentHashMap<>();
 
   /**
    * Called to execute this action.  Upon invocation, calls
@@ -175,114 +175,122 @@ public class CreateKeytabFilesServerAction extends 
KerberosServerAction {
       } else {
         Map<String, String> principalPasswordMap = 
getPrincipalPasswordMap(requestSharedDataContext);
         Map<String, Integer> principalKeyNumberMap = 
getPrincipalKeyNumberMap(requestSharedDataContext);
+
         for (ResolvedKerberosKeytab rkk : keytabsToCreate) {
           String hostName = resolvedPrincipal.getHostName();
           String keytabFilePath = rkk.getFile();
 
           if ((hostName != null) && !hostName.isEmpty() && (keytabFilePath != 
null) && !keytabFilePath.isEmpty()) {
-            Set<String> visitedPrincipalKeys = 
visitedIdentities.get(resolvedPrincipal.getPrincipal());
-            String visitationKey = String.format("%s|%s", hostName, 
keytabFilePath);
-
-            if ((visitedPrincipalKeys == null) || 
!visitedPrincipalKeys.contains(visitationKey)) {
-              // Look up the current evaluatedPrincipal's password.
-              // If found create the keytab file, else try to find it in the 
cache.
-              String password = 
principalPasswordMap.get(resolvedPrincipal.getPrincipal());
-              Integer keyNumber = 
principalKeyNumberMap.get(resolvedPrincipal.getPrincipal());
-
-              message = String.format("Creating keytab file for %s on host 
%s", resolvedPrincipal.getPrincipal(), hostName);
-              LOG.info(message);
-              actionLog.writeStdOut(message);
-              
auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(keytabFilePath);
-
-              // Determine where to store the keytab file.  It should go into 
a host-specific
-              // directory under the previously determined data directory.
-              File hostDirectory = new File(dataDirectory, hostName);
-
-              // Ensure the host directory exists...
-              if (!hostDirectory.exists() && hostDirectory.mkdirs()) {
-                // Make sure only Ambari has access to this directory.
-                ensureAmbariOnlyAccess(hostDirectory);
-              }
+            Lock lock = m_locksByKeytab.get(keytabFilePath);
+            lock.lock();
+
+            try {
+              Set<String> visitedPrincipalKeys = 
visitedIdentities.get(resolvedPrincipal.getPrincipal());
+              String visitationKey = String.format("%s|%s", hostName, 
keytabFilePath);
+
+              if ((visitedPrincipalKeys == null) || 
!visitedPrincipalKeys.contains(visitationKey)) {
+                // Look up the current evaluatedPrincipal's password.
+                // If found create the keytab file, else try to find it in the 
cache.
+                String password = 
principalPasswordMap.get(resolvedPrincipal.getPrincipal());
+                Integer keyNumber = 
principalKeyNumberMap.get(resolvedPrincipal.getPrincipal());
+
+                message = String.format("Creating keytab file for %s on host 
%s", resolvedPrincipal.getPrincipal(), hostName);
+                LOG.info(message);
+                actionLog.writeStdOut(message);
+                
auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(keytabFilePath);
+
+                // Determine where to store the keytab file.  It should go 
into a host-specific
+                // directory under the previously determined data directory.
+                File hostDirectory = new File(dataDirectory, hostName);
+
+                // Ensure the host directory exists...
+                if (!hostDirectory.exists() && hostDirectory.mkdirs()) {
+                  // Make sure only Ambari has access to this directory.
+                  ensureAmbariOnlyAccess(hostDirectory);
+                }
 
-              if (hostDirectory.exists()) {
-                File destinationKeytabFile = new File(hostDirectory, 
DigestUtils.sha256Hex(keytabFilePath));
+                if (hostDirectory.exists()) {
+                  File destinationKeytabFile = new File(hostDirectory, 
DigestUtils.sha256Hex(keytabFilePath));
 
-                boolean regenerateKeytabs = 
getOperationType(getCommandParameters()) == OperationType.RECREATE_ALL;
+                  boolean regenerateKeytabs = 
getOperationType(getCommandParameters()) == OperationType.RECREATE_ALL;
 
-                if(!includedInFilter) {
-                  // If this principal is to be filtered out, skip... unless 
is has not yet been created...
-                  regenerateKeytabs = false;
-                }
+                  if(!includedInFilter) {
+                    // If this principal is to be filtered out, skip... unless 
is has not yet been created...
+                    regenerateKeytabs = false;
+                  }
 
-                KerberosPrincipalEntity principalEntity = 
kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
-                String cachedKeytabPath = (principalEntity == null) ? null : 
principalEntity.getCachedKeytabPath();
+                  KerberosPrincipalEntity principalEntity = 
kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
+                  String cachedKeytabPath = (principalEntity == null) ? null : 
principalEntity.getCachedKeytabPath();
 
-                if (password == null) {
-                  if (!regenerateKeytabs && 
hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) {
-                    // There is nothing to do for this since it must already 
exist and we don't want to
-                    // regenerate the keytab
-                    message = String.format("Skipping keytab file for %s, 
missing password indicates nothing to do", resolvedPrincipal.getPrincipal());
-                    LOG.info(message);
-                  } else {
-                    if (cachedKeytabPath == null) {
-                      message = String.format("Failed to create keytab for %s, 
missing cached file", resolvedPrincipal.getPrincipal());
-                      actionLog.writeStdErr(message);
-                      LOG.error(message);
-                      commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+                  if (password == null) {
+                    if (!regenerateKeytabs && 
hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) {
+                      // There is nothing to do for this since it must already 
exist and we don't want to
+                      // regenerate the keytab
+                      message = String.format("Skipping keytab file for %s, 
missing password indicates nothing to do", resolvedPrincipal.getPrincipal());
+                      LOG.info(message);
                     } else {
-                      try {
-                        operationHandler.createKeytabFile(new 
File(cachedKeytabPath), destinationKeytabFile);
-                      } catch (KerberosOperationException e) {
-                        message = String.format("Failed to create keytab file 
for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
+                      if (cachedKeytabPath == null) {
+                        message = String.format("Failed to create keytab for 
%s, missing cached file", resolvedPrincipal.getPrincipal());
                         actionLog.writeStdErr(message);
-                        LOG.error(message, e);
+                        LOG.error(message);
                         commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+                      } else {
+                        try {
+                          operationHandler.createKeytabFile(new 
File(cachedKeytabPath), destinationKeytabFile);
+                        } catch (KerberosOperationException e) {
+                          message = String.format("Failed to create keytab 
file for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
+                          actionLog.writeStdErr(message);
+                          LOG.error(message, e);
+                          commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+                        }
                       }
                     }
-                  }
-                } else {
-                  Keytab keytab = 
createKeytab(resolvedPrincipal.getPrincipal(), password, keyNumber, 
operationHandler, visitedPrincipalKeys != null, true, actionLog);
-
-                  if (keytab != null) {
-                    try {
-                      if (operationHandler.createKeytabFile(keytab, 
destinationKeytabFile)) {
-                        ensureAmbariOnlyAccess(destinationKeytabFile);
+                  } else {
+                    Keytab keytab = 
createKeytab(resolvedPrincipal.getPrincipal(), password, keyNumber, 
operationHandler, visitedPrincipalKeys != null, true, actionLog);
 
-                        message = String.format("Successfully created keytab 
file for %s at %s", resolvedPrincipal.getPrincipal(), 
destinationKeytabFile.getAbsolutePath());
-                        LOG.info(message);
-                        
auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
-                      } else {
-                        message = String.format("Failed to create keytab file 
for %s at %s", resolvedPrincipal.getPrincipal(), 
destinationKeytabFile.getAbsolutePath());
+                    if (keytab != null) {
+                      try {
+                        if (operationHandler.createKeytabFile(keytab, 
destinationKeytabFile)) {
+                          ensureAmbariOnlyAccess(destinationKeytabFile);
+
+                          message = String.format("Successfully created keytab 
file for %s at %s", resolvedPrincipal.getPrincipal(), 
destinationKeytabFile.getAbsolutePath());
+                          LOG.info(message);
+                          
auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
+                        } else {
+                          message = String.format("Failed to create keytab 
file for %s at %s", resolvedPrincipal.getPrincipal(), 
destinationKeytabFile.getAbsolutePath());
+                          actionLog.writeStdErr(message);
+                          LOG.error(message);
+                          commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+                        }
+                      } catch (KerberosOperationException e) {
+                        message = String.format("Failed to create keytab file 
for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
                         actionLog.writeStdErr(message);
-                        LOG.error(message);
+                        LOG.error(message, e);
                         commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
                       }
-                    } catch (KerberosOperationException e) {
-                      message = String.format("Failed to create keytab file 
for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
-                      actionLog.writeStdErr(message);
-                      LOG.error(message, e);
+                    } else {
                       commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
                     }
-                  } else {
-                    commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
-                  }
 
-                  if (visitedPrincipalKeys == null) {
-                    visitedPrincipalKeys = new HashSet<>();
-                    visitedIdentities.put(resolvedPrincipal.getPrincipal(), 
visitedPrincipalKeys);
-                  }
+                    if (visitedPrincipalKeys == null) {
+                      visitedPrincipalKeys = new HashSet<>();
+                      visitedIdentities.put(resolvedPrincipal.getPrincipal(), 
visitedPrincipalKeys);
+                    }
 
-                  visitedPrincipalKeys.add(visitationKey);
+                    visitedPrincipalKeys.add(visitationKey);
+                  }
+                } else {
+                  message = String.format("Failed to create keytab file for 
%s, the container directory does not exist: %s",
+                      resolvedPrincipal.getPrincipal(), 
hostDirectory.getAbsolutePath());
+                  actionLog.writeStdErr(message);
+                  LOG.error(message);
+                  commandReport = createCommandReport(1, 
HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
                 }
               } else {
-                message = String.format("Failed to create keytab file for %s, 
the container directory does not exist: %s",
-                    resolvedPrincipal.getPrincipal(), 
hostDirectory.getAbsolutePath());
-                actionLog.writeStdErr(message);
-                LOG.error(message);
-                commandReport = createCommandReport(1, HostRoleStatus.FAILED, 
"{}", actionLog.getStdOut(), actionLog.getStdErr());
+                LOG.debug("Skipping previously processed keytab for {} on host 
{}", resolvedPrincipal.getPrincipal(), hostName);
               }
-            } else {
-              LOG.debug("Skipping previously processed keytab for {} on host 
{}", resolvedPrincipal.getPrincipal(), hostName);
+            } finally {
+              lock.unlock();
             }
           }
         }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
index 4e710d5..7415d29 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
@@ -23,6 +23,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
@@ -39,6 +40,7 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.Striped;
 import com.google.inject.Inject;
 
 /**
@@ -68,6 +70,11 @@ public class CreatePrincipalsServerAction extends 
KerberosServerAction {
   private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
 
   /**
+   * Used to prevent multiple threads from working with the same principal.
+   */
+  private Striped<Lock> locksByPrincipal = Striped.lazyWeakLock(25);
+  
+  /**
    * A set of visited principal names used to prevent unnecessary processing 
on already processed
    * principal names
    */
@@ -161,32 +168,40 @@ public class CreatePrincipalsServerAction extends 
KerberosServerAction {
       if (processPrincipal) {
         Map<String, String> principalPasswordMap = 
getPrincipalPasswordMap(requestSharedDataContext);
 
-        String password = 
principalPasswordMap.get(resolvedPrincipal.getPrincipal());
-
-        if (password == null) {
-          CreatePrincipalResult result = 
createPrincipal(resolvedPrincipal.getPrincipal(), servicePrincipal, 
kerberosConfiguration, operationHandler, regenerateKeytabs, actionLog);
-          if (result == null) {
-            commandReport = createCommandReport(1, HostRoleStatus.FAILED, 
"{}", actionLog.getStdOut(), actionLog.getStdErr());
-          } else {
-            Map<String, Integer> principalKeyNumberMap = 
getPrincipalKeyNumberMap(requestSharedDataContext);
-
-            principalPasswordMap.put(resolvedPrincipal.getPrincipal(), 
result.getPassword());
-            principalKeyNumberMap.put(resolvedPrincipal.getPrincipal(), 
result.getKeyNumber());
-            // invalidate given principal for all keytabs to make them 
redistributed again
-            for (KerberosKeytabPrincipalEntity kkpe: 
kerberosKeytabPrincipalDAO.findByPrincipal(resolvedPrincipal.getPrincipal())) {
-              kkpe.setDistributed(false);
-              kerberosKeytabPrincipalDAO.merge(kkpe);
-            }
-            // invalidate principal cache
-            KerberosPrincipalEntity principalEntity = 
kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
-            try {
-              new File(principalEntity.getCachedKeytabPath()).delete();
-            } catch (Exception e) {
-              LOG.debug("Failed to delete cache file '{}'", 
principalEntity.getCachedKeytabPath());
+        String principal = resolvedPrincipal.getPrincipal();
+        Lock lock = locksByPrincipal.get(principal);
+        lock.lock();
+        
+        String password = principalPasswordMap.get(principal);
+
+        try {
+          if (password == null) {
+            CreatePrincipalResult result = 
createPrincipal(resolvedPrincipal.getPrincipal(), servicePrincipal, 
kerberosConfiguration, operationHandler, regenerateKeytabs, actionLog);
+            if (result == null) {
+              commandReport = createCommandReport(1, HostRoleStatus.FAILED, 
"{}", actionLog.getStdOut(), actionLog.getStdErr());
+            } else {
+              Map<String, Integer> principalKeyNumberMap = 
getPrincipalKeyNumberMap(requestSharedDataContext);
+  
+              principalPasswordMap.put(resolvedPrincipal.getPrincipal(), 
result.getPassword());
+              principalKeyNumberMap.put(resolvedPrincipal.getPrincipal(), 
result.getKeyNumber());
+              // invalidate given principal for all keytabs to make them 
redistributed again
+              for (KerberosKeytabPrincipalEntity kkpe: 
kerberosKeytabPrincipalDAO.findByPrincipal(resolvedPrincipal.getPrincipal())) {
+                kkpe.setDistributed(false);
+                kerberosKeytabPrincipalDAO.merge(kkpe);
+              }
+              // invalidate principal cache
+              KerberosPrincipalEntity principalEntity = 
kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
+              try {
+                new File(principalEntity.getCachedKeytabPath()).delete();
+              } catch (Exception e) {
+                LOG.debug("Failed to delete cache file '{}'", 
principalEntity.getCachedKeytabPath());
+              }
+              principalEntity.setCachedKeytabPath(null);
+              kerberosPrincipalDAO.merge(principalEntity);
             }
-            principalEntity.setCachedKeytabPath(null);
-            kerberosPrincipalDAO.merge(principalEntity);
           }
+        } finally {
+          lock.unlock();
         }
       }
     }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
index b3b3082..12369c6 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
@@ -22,6 +22,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
@@ -35,6 +36,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.util.concurrent.Striped;
 import com.google.inject.Inject;
 
 public class FinalizeKerberosServerAction extends KerberosServerAction {
@@ -42,6 +44,11 @@ public class FinalizeKerberosServerAction extends 
KerberosServerAction {
 
   private final TopologyHolder topologyHolder;
 
+  /**
+   * Used to prevent multiple threads from working with the same keytab.
+   */
+  private Striped<Lock> m_locksByKeytab = Striped.lazyWeakLock(25);
+
   @Inject
   public FinalizeKerberosServerAction(TopologyHolder topologyHolder) {
     this.topologyHolder = topologyHolder;
@@ -91,7 +98,13 @@ public class FinalizeKerberosServerAction extends 
KerberosServerAction {
 
           String keytabFilePath = resolvedPrincipal.getKeytabPath();
 
-          if (!StringUtils.isEmpty(keytabFilePath)) {
+          if (StringUtils.isEmpty(keytabFilePath)) {
+            return null;
+          }
+
+          Lock lock = m_locksByKeytab.get(keytabFilePath);
+          lock.lock();
+          try {
             Set<String> visited = (Set<String>) 
requestSharedDataContext.get(this.getClass().getName() + "_visited");
 
             if (!visited.contains(keytabFilePath)) {
@@ -154,6 +167,8 @@ public class FinalizeKerberosServerAction extends 
KerberosServerAction {
 
               visited.add(keytabFilePath);
             }
+          } finally {
+            lock.unlock();
           }
         }
       }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
index 55e90ca..468121e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
@@ -21,15 +21,26 @@ package org.apache.ambari.server.serveraction.kerberos;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
 import org.apache.ambari.server.orm.dao.HostDAO;
@@ -42,6 +53,8 @@ import 
org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerbero
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
 import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.io.FileUtils;
@@ -50,6 +63,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.reflect.TypeToken;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.google.inject.Inject;
 
 /**
@@ -189,6 +203,9 @@ public abstract class KerberosServerAction extends 
AbstractServerAction {
   @Inject
   private KerberosKeytabController kerberosKeytabController;
 
+  @Inject
+  private Configuration configuration;
+
   /**
    * Given a (command parameter) Map and a property name, attempts to safely 
retrieve the requested
    * data.
@@ -403,6 +420,9 @@ public abstract class KerberosServerAction extends 
AbstractServerAction {
    * Using {@link #getHostFilter()}, {@link #getIdentityFilter()} and {@link 
#getServiceComponentFilter()} it retrieve
    * list of filtered keytabs and their principals and process each principal 
using
    * {@link #processIdentity(ResolvedKerberosPrincipal, 
KerberosOperationHandler, Map, boolean, Map)}.
+   * The configuration option {@link 
Configuration#getKerberosServerActionThreadpoolSize()} defines
+   * how many threads will handle {@link 
#processIdentity(ResolvedKerberosPrincipal, KerberosOperationHandler, Map, 
boolean, Map)}.
+   * The default is {@code 1}, but this method must be thread-safe in the 
event that concurrent threads are used.
    *
    * @param requestSharedDataContext a Map to be used a shared data among all 
ServerActions related
    *                                 to a given request
@@ -437,20 +457,67 @@ public abstract class KerberosServerAction extends 
AbstractServerAction {
       }
 
       try {
+        // create the thread factory, executor, and completion service for
+        // running the identity processing in parallel
+        String factoryName = "process-identity-%d";
+        ExecutionCommand executionCommand = getExecutionCommand();
+        if( null != executionCommand ) {
+          factoryName = "process-identity-task-" + 
executionCommand.getTaskId() + "-thread-%d";
+        }
+
+        ThreadFactory threadFactory = new 
ThreadFactoryBuilder().setNameFormat(factoryName).build();
+
+        int threadCount = 
configuration.getKerberosServerActionThreadpoolSize();
+        ExecutorService executorService = 
Executors.newFixedThreadPool(threadCount, threadFactory);
+        CompletionService<CommandReport> completionService = new 
ExecutorCompletionService<>(executorService);
+
         Map<String, Collection<String>> serviceComponentFilter = 
getServiceComponentFilter();
         if (serviceComponentFilter != null && pruneServiceFilter()) {
           
kerberosKeytabController.adjustServiceComponentFilter(clusters.getCluster(getClusterName()),
 true, serviceComponentFilter);
         }
         final Collection<KerberosIdentityDescriptor> serviceIdentities = 
serviceComponentFilter == null ? null : 
kerberosKeytabController.getServiceIdentities(getClusterName(), 
serviceComponentFilter.keySet());
+        List<Future<CommandReport>> futures = new ArrayList<>();
         for (ResolvedKerberosKeytab rkk : 
kerberosKeytabController.getFilteredKeytabs(serviceIdentities, 
getHostFilter(),getIdentityFilter())) {
           for (ResolvedKerberosPrincipal principal : rkk.getPrincipals()) {
-            commandReport = processIdentity(principal, handler, 
kerberosConfiguration, isRelevantIdentity(serviceIdentities, principal), 
requestSharedDataContext);
+            // submit this method to the service to be processed concurrently
+            Future<CommandReport> future = completionService.submit(() -> {
+              try {
+                return processIdentity(principal, handler, 
kerberosConfiguration,
+                    isRelevantIdentity(serviceIdentities, principal), 
requestSharedDataContext);
+              } catch (AmbariException ambariException) {
+                throw new RuntimeException(ambariException);
+              }
+            });
+
+            // keep track of futures for total count and ability to cancel 
later
+            futures.add(future);
+          }
+        }
+
+        LOG.info("Processing {} identities concurrently...", futures.size());
+
+        // get each future as it completes (out of order is OK), cancelling if
+        // an error is found
+        try {
+          for( int i = 0; i < futures.size(); i++ ) {
+            Future<CommandReport> future = completionService.take();
+            commandReport = future.get();
+
             // If the principal processor returns a CommandReport, than it is 
time to stop
             // since an error condition has probably occurred, else all is 
assumed to be well.
             if (commandReport != null) {
               break;
             }
           }
+        } catch (Exception exception) {
+          LOG.error("Unable to process identities asynchronously", exception);
+          return createCommandReport(0, HostRoleStatus.FAILED, "{}", 
actionLog.getStdOut(),actionLog.getStdErr());
+        } finally {
+          futures.stream()
+            .filter(x -> !x.isCancelled() && !x.isDone())
+            .forEach(x -> x.cancel(true));
+
+          executorService.shutdown();
         }
       } finally {
         // The KerberosOperationHandler needs to be closed, if it fails to 
close ignore the
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
index ec01310..cac811e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
@@ -35,8 +35,12 @@ import 
org.apache.ambari.server.orm.entities.KerberosKeytabEntity;
 import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
 import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
+import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.collections.MapUtils;
 
 import com.google.common.collect.ImmutableSet;
@@ -56,6 +60,9 @@ public class KerberosKeytabController {
   @Inject
   private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
 
+  @Inject
+  private Clusters clusters;
+
   //TODO: due to circular dependencies in Guice this field cannot be injected 
with Guice's @Inject annotation; for now we should statically inject in 
AmbariServer
   private static KerberosHelper kerberosHelper;
 
@@ -100,7 +107,10 @@ public class KerberosKeytabController {
    * @return set of keytabs found
    */
   public Set<ResolvedKerberosKeytab> 
getFromPrincipal(ResolvedKerberosPrincipal rkp) {
-    return 
fromKeytabEntities(kerberosKeytabDAO.findByPrincipalAndHost(rkp.getPrincipal(), 
rkp.getHostId()));
+    List<KerberosKeytabEntity> keytabs = 
kerberosKeytabDAO.findByPrincipalAndHost(
+        rkp.getPrincipal(), rkp.getHostId());
+
+    return fromKeytabEntities(keytabs);
   }
 
   /**
@@ -224,6 +234,7 @@ public class KerberosKeytabController {
     ImmutableSet.Builder<ResolvedKerberosPrincipal> builder = 
ImmutableSet.builder();
     for (KerberosKeytabPrincipalEntity kkpe : principalEntities) {
       KerberosPrincipalEntity kpe = kkpe.getKerberosPrincipalEntity();
+
       if(kpe != null) {
         ResolvedKerberosPrincipal rkp = new ResolvedKerberosPrincipal(
             kkpe.getHostId(),
@@ -281,8 +292,33 @@ public class KerberosKeytabController {
 
   public Collection<KerberosIdentityDescriptor> getServiceIdentities(String 
clusterName, Collection<String> services) throws AmbariException {
     final Collection<KerberosIdentityDescriptor> serviceIdentities = new 
ArrayList<>();
+    Cluster cluster = clusters.getCluster(clusterName);
+    KerberosDescriptor kerberosDescriptor = 
kerberosHelper.getKerberosDescriptor(cluster, false);
+    Map<String, Map<String, Map<String, String>>> hostConfigurations = new 
HashMap<>();
+    Map<String, Host> hostMap = clusters.getHostsForCluster(clusterName);
+    Set<String> hosts = new HashSet<>(hostMap.keySet());
+
+    String ambariServerHostname = StageUtils.getHostName();
+    if (!hosts.contains(ambariServerHostname)) {
+      hosts.add(ambariServerHostname);
+    }
+    for( String hostName : hosts ) {
+      Map<String, Map<String, String>> configurations = 
kerberosHelper.calculateConfigurations(
+        cluster, hostName, kerberosDescriptor, false, false);
+      hostConfigurations.put(hostName, configurations);
+    }
     for (String service : services) {
-      for (Collection<KerberosIdentityDescriptor> activeIdentities : 
kerberosHelper.getActiveIdentities(clusterName, null, service, null, 
true).values()) {
+      Collection<Collection<KerberosIdentityDescriptor>> identities = 
kerberosHelper.getActiveIdentities(
+        clusterName,
+       null,
+        service,
+       null,
+       true,
+        hostConfigurations,
+        kerberosDescriptor
+      ).values();
+
+      for (Collection<KerberosIdentityDescriptor> activeIdentities : 
identities) {
         serviceIdentities.addAll(activeIdentities);
       }
     }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index c2e43e9..a31445b 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -84,6 +84,7 @@ import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.dao.ArtifactDAO;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
 import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+import 
org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO.KeytabPrincipalFindOrCreateResult;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
 import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
@@ -3429,7 +3430,12 @@ public class KerberosHelperTest extends EasyMockSupport {
   private void testCreateTestIdentity(final PrincipalKeyCredential 
PrincipalKeyCredential, Boolean manageIdentities) throws Exception {
     KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
     KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = 
injector.getInstance(KerberosKeytabPrincipalDAO.class);
-    expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), 
anyObject())).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).anyTimes();
+    KerberosKeytabPrincipalEntity kkp = 
createNiceMock(KerberosKeytabPrincipalEntity.class);
+    KeytabPrincipalFindOrCreateResult result = new 
KeytabPrincipalFindOrCreateResult();
+    result.created = true;
+    result.kkp = kkp;
+
+    expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), 
anyObject())).andReturn(result).anyTimes();
     boolean managingIdentities = !Boolean.FALSE.equals(manageIdentities);
 
     final Map<String, String> kerberosEnvProperties = new HashMap<>();
@@ -3620,7 +3626,12 @@ public class KerberosHelperTest extends EasyMockSupport {
   private void testDeleteTestIdentity(final PrincipalKeyCredential 
PrincipalKeyCredential) throws Exception {
     KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
     KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = 
injector.getInstance(KerberosKeytabPrincipalDAO.class);
-    expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), 
anyObject())).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).anyTimes();
+    KerberosKeytabPrincipalEntity kkp = 
createNiceMock(KerberosKeytabPrincipalEntity.class);
+    KeytabPrincipalFindOrCreateResult result = new 
KeytabPrincipalFindOrCreateResult();
+    result.created = true;
+    result.kkp = kkp;
+
+    expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), 
anyObject())).andReturn(result).anyTimes();
     Host host1 = createMock(Host.class);
     expect(host1.getHostId()).andReturn(1l).anyTimes();
 
@@ -4051,7 +4062,8 @@ public class KerberosHelperTest extends EasyMockSupport {
     injector.getInstance(AmbariMetaInfo.class).init();
 
     Map<String, Collection<KerberosIdentityDescriptor>> identities;
-    identities = kerberosHelper.getActiveIdentities(clusterName, hostName, 
serviceName, componentName, replaceHostNames);
+    identities = kerberosHelper.getActiveIdentities(clusterName, hostName, 
serviceName,
+        componentName, replaceHostNames, null, null);
 
     verifyAll();
 
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
index e271932..c9143a9 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
@@ -43,10 +43,14 @@ import 
org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosPrincipalType;
+import org.easymock.EasyMock;
 import org.easymock.EasyMockSupport;
 import org.junit.Assert;
 import org.junit.Test;
@@ -131,8 +135,12 @@ public class HostKerberosIdentityResourceProviderTest 
extends EasyMockSupport {
 
   @Test
   public void testGetResources() throws Exception {
+    Clusters clusters = createNiceMock(Clusters.class);
+    expect(clusters.getCluster(EasyMock.anyString())).andReturn(
+        createNiceMock(Cluster.class)).once();
 
     AmbariManagementController managementController = 
createMock(AmbariManagementController.class);
+    
expect(managementController.getClusters()).andReturn(clusters).atLeastOnce();
 
     KerberosPrincipalDescriptor principalDescriptor1 = 
createStrictMock(KerberosPrincipalDescriptor.class);
     
expect(principalDescriptor1.getValue()).andReturn("princip...@example.com");
@@ -214,9 +222,15 @@ public class HostKerberosIdentityResourceProviderTest 
extends EasyMockSupport {
     activeIdentities.put("Host100", identities);
 
     KerberosHelper kerberosHelper = createStrictMock(KerberosHelper.class);
-    expect(kerberosHelper.getActiveIdentities("Cluster100", "Host100", null, 
null, true))
+    KerberosDescriptor kerberosDescriptor = 
createNiceMock(KerberosDescriptor.class);
+    expect(kerberosHelper.getKerberosDescriptor(
+        EasyMock.anyObject(Cluster.class),
+        EasyMock.eq(false))).andReturn(kerberosDescriptor).atLeastOnce();
+
+    expect(kerberosHelper.getActiveIdentities("Cluster100", "Host100", null, 
null, true, null,
+        kerberosDescriptor))
         .andReturn(activeIdentities)
-        .times(1);
+        .once();
 
     // replay
     replayAll();
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
index beeac82..191b6cb 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
@@ -36,11 +36,13 @@ import org.apache.ambari.server.controller.RootService;
 import org.apache.ambari.server.orm.DBAccessor;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+import 
org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO.KeytabPrincipalFindOrCreateResult;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
 import org.apache.ambari.server.serveraction.ActionLog;
 import 
org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.stack.OsFamily;
 import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.io.FileUtils;
 import org.easymock.EasyMockSupport;
@@ -94,9 +96,13 @@ public class ConfigureAmbariIdentitiesServerActionTest 
extends EasyMockSupport {
 
     
expect(hostDAO.findByName(StageUtils.getHostName())).andReturn(hostEntity).once();
     KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = 
injector.getInstance(KerberosKeytabPrincipalDAO.class);
-    KerberosKeytabPrincipalEntity kke = 
createNiceMock(KerberosKeytabPrincipalEntity.class);
-    expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), 
eq(hostEntity), anyObject())).andReturn(kke).once();
-    
expect(kerberosKeytabPrincipalDAO.merge(kke)).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).once();
+    KerberosKeytabPrincipalEntity kkp = 
createNiceMock(KerberosKeytabPrincipalEntity.class);
+    KeytabPrincipalFindOrCreateResult result = new 
KeytabPrincipalFindOrCreateResult();
+    result.created = true;
+    result.kkp = kkp;
+
+    expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), 
eq(hostEntity), anyObject())).andReturn(result).once();
+    
expect(kerberosKeytabPrincipalDAO.merge(kkp)).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).once();
 
     // Mock the methods that do the actual file manipulation to avoid having 
to deal with ambari-sudo.sh used in
     // ShellCommandUtil#mkdir, ShellCommandUtil#copyFile, etc..
@@ -215,6 +221,7 @@ public class ConfigureAmbariIdentitiesServerActionTest 
extends EasyMockSupport {
         bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class));
         bind(Clusters.class).toInstance(createNiceMock(Clusters.class));
         
bind(KerberosHelper.class).toInstance(createNiceMock(KerberosHelper.class));
+        bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
 
         bind(HostDAO.class).toInstance(createMock(HostDAO.class));
         
bind(KerberosKeytabPrincipalDAO.class).toInstance(createMock(KerberosKeytabPrincipalDAO.class));

Reply via email to