You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2018/01/02 16:55:54 UTC

[03/37] ambari git commit: AMBARI-22530. Refactor internal code of handling info between kerberos wizard actions (echekanskiy)

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
----------------------------------------------------------------------
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 5ec4c10..a803dcf 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
@@ -34,10 +34,11 @@ 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.dao.KerberosPrincipalHostDAO;
-import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
 import org.apache.ambari.server.serveraction.ActionLog;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.KerberosKeytabController;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosKeytab;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.apache.directory.server.kerberos.shared.keytab.Keytab;
 import org.slf4j.Logger;
@@ -52,7 +53,7 @@ import com.google.inject.Inject;
  * This class mainly relies on the KerberosServerAction to iterate through metadata identifying
  * the Kerberos keytab files that need to be created. For each identity in the metadata, this
  * implementation's
- * {@link KerberosServerAction#processIdentity(Map, String, KerberosOperationHandler, Map, Map)}
+ * {@link KerberosServerAction#processIdentity(ResolvedKerberosPrincipal, KerberosOperationHandler, Map, Map)}
  * is invoked attempting the creation of the relevant keytab file.
  */
 public class CreateKeytabFilesServerAction extends KerberosServerAction {
@@ -65,12 +66,6 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
   private KerberosPrincipalDAO kerberosPrincipalDAO;
 
   /**
-   * KerberosPrincipalHostDAO used to get Kerberos principal details
-   */
-  @Inject
-  private KerberosPrincipalHostDAO kerberosPrincipalHostDAO;
-
-  /**
    * Configuration used to get the configured properties such as the keytab file cache directory
    */
   @Inject
@@ -82,6 +77,9 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
   @Inject
   private HostDAO hostDAO;
 
+  @Inject
+  private KerberosKeytabController kerberosKeytabController;
+
   /**
    * 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.
@@ -118,10 +116,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
    * If a password exists for the current evaluatedPrincipal, use a
    * {@link org.apache.ambari.server.serveraction.kerberos.KerberosOperationHandler} to generate
    * the keytab file. To help avoid filename collisions and to build a structure that is easy to
-   * discover, each keytab file is stored in host-specific
-   * ({@link org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileReader#HOSTNAME})
-   * directory using the SHA1 hash of its destination file path
-   * ({@link org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileReader#KEYTAB_FILE_PATH})
+   * discover, each keytab file is stored in host-specific directory using the SHA1 hash of its destination file path.
    * <p/>
    * <pre>
    *   data_directory
@@ -133,8 +128,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
    *   |  |- ...
    * </pre>
    *
-   * @param identityRecord           a Map containing the data for the current identity record
-   * @param evaluatedPrincipal       a String indicating the relevant principal
+   * @param resolvedPrincipal        a ResolvedKerberosPrincipal object to process
    * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
    *                                 tasks for specific Kerberos implementations
    *                                 (MIT, Active Directory, etc...)
@@ -145,7 +139,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
    * @throws AmbariException if an error occurs while processing the identity record
    */
   @Override
-  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal,
+  protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal,
                                           KerberosOperationHandler operationHandler,
                                           Map<String, String> kerberosConfiguration,
                                           Map<String, Object> requestSharedDataContext)
@@ -160,40 +154,42 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
 
     CommandReport commandReport = null;
     String message = null;
-    try {
-      if (identityRecord != null) {
-        String dataDirectory = getDataDirectoryPath();
 
-        if (operationHandler == null) {
-          message = String.format("Failed to create keytab file for %s, missing KerberosOperationHandler", evaluatedPrincipal);
-          actionLog.writeStdErr(message);
-          LOG.error(message);
-          commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
-        } else if (dataDirectory == null) {
-          message = "The data directory has not been set. Generated keytab files can not be stored.";
-          LOG.error(message);
-          commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
-        } else {
-          Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
-          Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext);
+    Set<ResolvedKerberosKeytab> keytabsToCreate = kerberosKeytabController.getFromPrincipal(resolvedPrincipal);
 
-          String hostName = identityRecord.get(KerberosIdentityDataFileReader.HOSTNAME);
-          String keytabFilePath = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_PATH);
+    try {
+      String dataDirectory = getDataDirectoryPath();
+
+      if (operationHandler == null) {
+        message = String.format("Failed to create keytab file for %s, missing KerberosOperationHandler", resolvedPrincipal.getPrincipal());
+        actionLog.writeStdErr(message);
+        LOG.error(message);
+        commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+      } else if (dataDirectory == null) {
+        message = "The data directory has not been set. Generated keytab files can not be stored.";
+        LOG.error(message);
+        commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+      } 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(evaluatedPrincipal);
+            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(evaluatedPrincipal);
-              Integer keyNumber = principalKeyNumberMap.get(evaluatedPrincipal);
+              String password = principalPasswordMap.get(resolvedPrincipal.getPrincipal());
+              Integer keyNumber = principalKeyNumberMap.get(resolvedPrincipal.getPrincipal());
 
-              message = String.format("Creating keytab file for %s on host %s", evaluatedPrincipal, hostName);
+              message = String.format("Creating keytab file for %s on host %s", resolvedPrincipal.getPrincipal(), hostName);
               LOG.info(message);
               actionLog.writeStdOut(message);
-              auditEventBuilder.withPrincipal(evaluatedPrincipal).withHostName(hostName).withKeyTabFilePath(keytabFilePath);
+              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.
@@ -206,32 +202,22 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
               }
 
               if (hostDirectory.exists()) {
-                File destinationKeytabFile = new File(hostDirectory, DigestUtils.sha1Hex(keytabFilePath));
-                HostEntity hostEntity = hostDAO.findByName(hostName);
-                // in case of ambari-server identity there's no host entity for ambari_server host
-                if (hostEntity == null && !hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) {
-                  message = "Failed to find HostEntity for hostname = " + hostName;
-                  actionLog.writeStdErr(message);
-                  LOG.error(message);
-                  commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
-                  return commandReport;
-                }
+                File destinationKeytabFile = new File(hostDirectory, DigestUtils.sha256Hex(keytabFilePath));
 
                 boolean regenerateKeytabs = getOperationType(getCommandParameters()) == OperationType.RECREATE_ALL;
 
-                KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal);
+                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) || kerberosPrincipalHostDAO
-                      .exists(evaluatedPrincipal, hostEntity.getHostId(), keytabFilePath)) && cachedKeytabPath == 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", evaluatedPrincipal);
+                    message = String.format("Skipping keytab file for %s, missing password indicates nothing to do", resolvedPrincipal.getPrincipal());
                     LOG.debug(message);
                   } else {
                     if (cachedKeytabPath == null) {
-                      message = String.format("Failed to create keytab for %s, missing cached file", evaluatedPrincipal);
+                      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());
@@ -239,7 +225,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
                       try {
                         operationHandler.createKeytabFile(new File(cachedKeytabPath), destinationKeytabFile);
                       } catch (KerberosOperationException e) {
-                        message = String.format("Failed to create keytab file for %s - %s", evaluatedPrincipal, e.getMessage());
+                        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());
@@ -247,24 +233,24 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
                     }
                   }
                 } else {
-                  Keytab keytab = createKeytab(evaluatedPrincipal, password, keyNumber, operationHandler, visitedPrincipalKeys != null, true, actionLog);
+                  Keytab keytab = createKeytab(resolvedPrincipal.getPrincipal(), password, keyNumber, operationHandler, visitedPrincipalKeys != null, true, actionLog);
 
                   if (keytab != null) {
                     try {
                       if (operationHandler.createKeytabFile(keytab, destinationKeytabFile)) {
                         ensureAmbariOnlyAccess(destinationKeytabFile);
 
-                        message = String.format("Successfully created keytab file for %s at %s", evaluatedPrincipal, destinationKeytabFile.getAbsolutePath());
+                        message = String.format("Successfully created keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
                         LOG.debug(message);
-                        auditEventBuilder.withPrincipal(evaluatedPrincipal).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
+                        auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
                       } else {
-                        message = String.format("Failed to create keytab file for %s at %s", evaluatedPrincipal, destinationKeytabFile.getAbsolutePath());
+                        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", evaluatedPrincipal, e.getMessage());
+                      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());
@@ -275,20 +261,20 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
 
                   if (visitedPrincipalKeys == null) {
                     visitedPrincipalKeys = new HashSet<>();
-                    visitedIdentities.put(evaluatedPrincipal, visitedPrincipalKeys);
+                    visitedIdentities.put(resolvedPrincipal.getPrincipal(), visitedPrincipalKeys);
                   }
 
                   visitedPrincipalKeys.add(visitationKey);
                 }
               } else {
                 message = String.format("Failed to create keytab file for %s, the container directory does not exist: %s",
-                    evaluatedPrincipal, hostDirectory.getAbsolutePath());
+                    resolvedPrincipal.getPrincipal(), hostDirectory.getAbsolutePath());
                 actionLog.writeStdErr(message);
                 LOG.error(message);
                 commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
               }
             } else {
-              LOG.debug("Skipping previously processed keytab for {} on host {}", evaluatedPrincipal, hostName);
+              LOG.debug("Skipping previously processed keytab for {} on host {}", resolvedPrincipal.getPrincipal(), hostName);
             }
           }
         }
@@ -420,7 +406,7 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
       }
     }
 
-    File cachedKeytabFile = new File(cacheDirectory, DigestUtils.sha1Hex(principal + String.valueOf(System.currentTimeMillis())));
+    File cachedKeytabFile = new File(cacheDirectory, DigestUtils.sha256Hex(principal + String.valueOf(System.currentTimeMillis())));
 
     try {
       keytab.write(cachedKeytabFile);

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
----------------------------------------------------------------------
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 0c90659..a108c9b 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
@@ -28,12 +28,13 @@ 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.audit.event.kerberos.CreatePrincipalKerberosAuditEvent;
+import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
-import org.apache.ambari.server.orm.dao.KerberosPrincipalHostDAO;
+import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
 import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
-import org.apache.ambari.server.orm.entities.KerberosPrincipalHostEntity;
 import org.apache.ambari.server.security.SecurePasswordHelper;
 import org.apache.ambari.server.serveraction.ActionLog;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -45,7 +46,7 @@ import com.google.inject.Inject;
  * <p/>
  * This class mainly relies on the KerberosServerAction to iterate through metadata identifying
  * the Kerberos principals that need to be created. For each identity in the metadata, this implementation's
- * {@link KerberosServerAction#processIdentity(Map, String, KerberosOperationHandler, Map, Map)}
+ * {@link KerberosServerAction#processIdentity(ResolvedKerberosPrincipal, KerberosOperationHandler, Map, Map)}
  * is invoked attempting the creation of the relevant principal.
  */
 public class CreatePrincipalsServerAction extends KerberosServerAction {
@@ -58,17 +59,14 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
   private KerberosPrincipalDAO kerberosPrincipalDAO;
 
   /**
-   * KerberosPrincipalHostDAO used to get Kerberos principal details
-   */
-  @Inject
-  private KerberosPrincipalHostDAO kerberosPrincipalHostDAO;
-
-  /**
    * SecurePasswordHelper used to generate secure passwords for newly created principals
    */
   @Inject
   private SecurePasswordHelper securePasswordHelper;
 
+  @Inject
+  private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
+
   /**
    * A set of visited principal names used to prevent unnecessary processing on already processed
    * principal names
@@ -106,8 +104,7 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
    * store the new key numbers in the shared principal-to-key_number map so that subsequent process
    * may use the data if necessary.
    *
-   * @param identityRecord           a Map containing the data for the current identity record
-   * @param evaluatedPrincipal       a String indicating the relevant principal
+   * @param resolvedPrincipal        a ResolvedKerberosPrincipal object to process
    * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
    *                                 tasks for specific Kerberos implementations
    *                                 (MIT, Active Directory, etc...)
@@ -118,7 +115,7 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
    * @throws AmbariException if an error occurs while processing the identity record
    */
   @Override
-  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal,
+  protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal,
                                           KerberosOperationHandler operationHandler,
                                           Map<String, String> kerberosConfiguration,
                                           Map<String, Object> requestSharedDataContext)
@@ -126,16 +123,16 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
     CommandReport commandReport = null;
 
     //  Only process this principal name if we haven't already processed it
-    if (!seenPrincipals.contains(evaluatedPrincipal)) {
-      seenPrincipals.add(evaluatedPrincipal);
+    // TODO optimize - split invalidation and principal creation to separate stages
+    if (!seenPrincipals.contains(resolvedPrincipal.getPrincipal())) {
+      seenPrincipals.add(resolvedPrincipal.getPrincipal());
 
       boolean processPrincipal;
 
-      // TODO add invalidate_principals option to make keytabs invalid all over the cluster.
-      KerberosPrincipalEntity kerberosPrincipalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal);
+      KerberosPrincipalEntity kerberosPrincipalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
 
       boolean regenerateKeytabs = getOperationType(getCommandParameters()) == OperationType.RECREATE_ALL;
-      boolean servicePrincipal = "service".equalsIgnoreCase(identityRecord.get(KerberosIdentityDataFileReader.PRINCIPAL_TYPE));
+      boolean servicePrincipal = resolvedPrincipal.isService();
       if (regenerateKeytabs) {
         // force recreation of principal due to keytab regeneration
         // regenerate only service principals if request filtered by hosts
@@ -154,24 +151,24 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
       if (processPrincipal) {
         Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
 
-        String password = principalPasswordMap.get(evaluatedPrincipal);
+        String password = principalPasswordMap.get(resolvedPrincipal.getPrincipal());
 
         if (password == null) {
-          CreatePrincipalResult result = createPrincipal(evaluatedPrincipal, servicePrincipal, kerberosConfiguration, operationHandler, regenerateKeytabs, actionLog);
+          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(evaluatedPrincipal, result.getPassword());
-            principalKeyNumberMap.put(evaluatedPrincipal, result.getKeyNumber());
+            principalPasswordMap.put(resolvedPrincipal.getPrincipal(), result.getPassword());
+            principalKeyNumberMap.put(resolvedPrincipal.getPrincipal(), result.getKeyNumber());
             // invalidate given principal for all keytabs to make them redistributed again
-            for (KerberosPrincipalHostEntity kphe: kerberosPrincipalHostDAO.findByPrincipal(evaluatedPrincipal)) {
-              kphe.setDistributed(false);
-              kerberosPrincipalHostDAO.merge(kphe);
+            for (KerberosKeytabPrincipalEntity kkpe: kerberosKeytabPrincipalDAO.findByPrincipal(resolvedPrincipal.getPrincipal())) {
+              kkpe.setDistributed(false);
+              kerberosKeytabPrincipalDAO.merge(kkpe);
             }
             // invalidate principal cache
-            KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal);
+            KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
             try {
               new File(principalEntity.getCachedKeytabPath()).delete();
             } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java
index 4c80bd4..7c28494 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/DestroyPrincipalsServerAction.java
@@ -29,8 +29,13 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.audit.event.kerberos.DestroyPrincipalKerberosAuditEvent;
 import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.orm.dao.KerberosKeytabDAO;
+import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
 import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
+import org.apache.ambari.server.orm.entities.KerberosKeytabEntity;
 import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosKeytab;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.ambari.server.utils.ShellCommandUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,7 +48,7 @@ import com.google.inject.Inject;
  * This class mainly relies on the KerberosServerAction to iterate through metadata identifying
  * the Kerberos principals that need to be removed from the relevant KDC. For each identity in the
  * metadata, this implementation's
- * {@link KerberosServerAction#processIdentity(Map, String, KerberosOperationHandler, Map, Map)}
+ * {@link KerberosServerAction#processIdentity(ResolvedKerberosPrincipal, KerberosOperationHandler, Map, Map)}
  * is invoked attempting the removal of the relevant principal.
  */
 public class DestroyPrincipalsServerAction extends KerberosServerAction {
@@ -52,6 +57,12 @@ public class DestroyPrincipalsServerAction extends KerberosServerAction {
   @Inject
   private KerberosPrincipalDAO kerberosPrincipalDAO;
 
+  @Inject
+  private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
+
+  @Inject
+  private KerberosKeytabDAO kerberosKeytabDAO;
+
   /**
    * A set of visited principal names used to prevent unnecessary processing on already processed
    * principal names
@@ -81,8 +92,7 @@ public class DestroyPrincipalsServerAction extends KerberosServerAction {
   /**
    * For each identity, remove the principal from the configured KDC.
    *
-   * @param identityRecord           a Map containing the data for the current identity record
-   * @param evaluatedPrincipal       a String indicating the relevant principal
+   * @param resolvedPrincipal        a ResolvedKerberosPrincipal object to process
    * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
    *                                 tasks for specific Kerberos implementations
    *                                 (MIT, Active Directory, etc...)
@@ -93,69 +103,73 @@ public class DestroyPrincipalsServerAction extends KerberosServerAction {
    * @throws org.apache.ambari.server.AmbariException if an error occurs while processing the identity record
    */
   @Override
-  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal,
+  protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal,
                                           KerberosOperationHandler operationHandler,
                                           Map<String, String> kerberosConfiguration,
                                           Map<String, Object> requestSharedDataContext)
       throws AmbariException {
 
     // Only process this principal if we haven't already processed it
-    if (!seenPrincipals.contains(evaluatedPrincipal)) {
-      seenPrincipals.add(evaluatedPrincipal);
+    if (!seenPrincipals.contains(resolvedPrincipal.getPrincipal())) {
+      seenPrincipals.add(resolvedPrincipal.getPrincipal());
 
-      String message = String.format("Destroying identity, %s", evaluatedPrincipal);
+      String message = String.format("Destroying identity, %s", resolvedPrincipal.getPrincipal());
       LOG.info(message);
       actionLog.writeStdOut(message);
       DestroyPrincipalKerberosAuditEvent.DestroyPrincipalKerberosAuditEventBuilder auditEventBuilder = DestroyPrincipalKerberosAuditEvent.builder()
           .withTimestamp(System.currentTimeMillis())
           .withRequestId(getHostRoleCommand().getRequestId())
           .withTaskId(getHostRoleCommand().getTaskId())
-          .withPrincipal(evaluatedPrincipal);
+          .withPrincipal(resolvedPrincipal.getPrincipal());
 
       try {
         try {
-          boolean servicePrincipal = "service".equalsIgnoreCase(identityRecord.get(KerberosIdentityDataFileReader.PRINCIPAL_TYPE));
-          operationHandler.removePrincipal(evaluatedPrincipal, servicePrincipal);
+          boolean servicePrincipal = resolvedPrincipal.isService();
+          operationHandler.removePrincipal(resolvedPrincipal.getPrincipal(), servicePrincipal);
         } catch (KerberosOperationException e) {
-          message = String.format("Failed to remove identity for %s from the KDC - %s", evaluatedPrincipal, e.getMessage());
+          message = String.format("Failed to remove identity for %s from the KDC - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
           LOG.warn(message);
           actionLog.writeStdErr(message);
           auditEventBuilder.withReasonOfFailure(message);
         }
 
         try {
-          KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(evaluatedPrincipal);
+          KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
 
           if (principalEntity != null) {
             String cachedKeytabPath = principalEntity.getCachedKeytabPath();
-
+            KerberosKeytabEntity kke = kerberosKeytabDAO.find(resolvedPrincipal.getResolvedKerberosKeytab().getFile());
+            kerberosKeytabDAO.remove(kke);
             kerberosPrincipalDAO.remove(principalEntity);
 
             // If a cached  keytabs file exists for this principal, delete it.
             if (cachedKeytabPath != null) {
               if (!new File(cachedKeytabPath).delete()) {
-                LOG.debug("Failed to remove cached keytab for {}", evaluatedPrincipal);
+                LOG.debug("Failed to remove cached keytab for {}", resolvedPrincipal.getPrincipal());
               }
             }
           }
 
           // delete Ambari server keytab
-          String hostName = identityRecord.get(KerberosIdentityDataFileReader.HOSTNAME);
+          String hostName = resolvedPrincipal.getHostName();
           if (hostName != null && hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) {
-            String keytabFilePath = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_PATH);
-            if (keytabFilePath != null) {
-              try {
-                ShellCommandUtil.Result result = ShellCommandUtil.delete(keytabFilePath, true, true);
-                if (!result.isSuccessful()) {
-                  LOG.warn("Failed to remove ambari keytab for {} due to {}", evaluatedPrincipal, result.getStderr());
+            ResolvedKerberosKeytab resolvedKeytab = resolvedPrincipal.getResolvedKerberosKeytab();
+            if (resolvedKeytab != null) {
+              String keytabFilePath = resolvedKeytab.getFile();
+              if (keytabFilePath != null) {
+                try {
+                  ShellCommandUtil.Result result = ShellCommandUtil.delete(keytabFilePath, true, true);
+                  if (!result.isSuccessful()) {
+                    LOG.warn("Failed to remove ambari keytab for {} due to {}", resolvedPrincipal.getPrincipal(), result.getStderr());
+                  }
+                } catch (IOException|InterruptedException e) {
+                  LOG.warn("Failed to remove ambari keytab for " + resolvedPrincipal.getPrincipal(), e);
                 }
-              } catch (IOException|InterruptedException e) {
-                LOG.warn("Failed to remove ambari keytab for " + evaluatedPrincipal, e);
               }
             }
           }
         } catch (Throwable t) {
-          message = String.format("Failed to remove identity for %s from the Ambari database - %s", evaluatedPrincipal, t.getMessage());
+          message = String.format("Failed to remove identity for %s from the Ambari database - %s", resolvedPrincipal.getPrincipal(), t.getMessage());
           LOG.warn(message);
           actionLog.writeStdErr(message);
           auditEventBuilder.withReasonOfFailure(message);

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
----------------------------------------------------------------------
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 bfd5e40..225e53e 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
@@ -26,9 +26,10 @@ import java.util.concurrent.ConcurrentMap;
 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.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.ambari.server.utils.ShellCommandUtil;
 import org.apache.ambari.server.utils.StageUtils;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,8 +44,7 @@ public class FinalizeKerberosServerAction extends KerberosServerAction {
    * some user accounts and groups may not have been available (at the OS level) when the keytab files
    * were created.
    *
-   * @param identityRecord           a Map containing the data for the current identity record
-   * @param evaluatedPrincipal       a String indicating the relevant principal
+   * @param resolvedPrincipal        a ResolvedKerberosPrincipal object to process
    * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
    *                                 tasks for specific Kerberos implementations
    *                                 (MIT, Active Directory, etc...)
@@ -54,39 +54,39 @@ public class FinalizeKerberosServerAction extends KerberosServerAction {
    * @throws AmbariException
    */
   @Override
-  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal,
+  protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal,
                                           KerberosOperationHandler operationHandler,
                                           Map<String, String> kerberosConfiguration,
                                           Map<String, Object> requestSharedDataContext)
       throws AmbariException {
 
-    if (identityRecord != null) {
+    if (resolvedPrincipal != null) {
       // If the record's HOSTNAME value is "ambari-server", rather than an actual hostname it will
       // not match the Ambari server's host name. This will occur if the there is no agent installed
       // on the Ambari server host.  This is ok, since any keytab files installed on the Ambari server
       // host will already have the permissions set so that only the Ambari server can read it.
       // There is no need to update the permissions for those keytab files so that installed services
       // can access them since no services will be installed on the host.
-      if (StageUtils.getHostName().equals(identityRecord.get(KerberosIdentityDataFile.HOSTNAME))) {
+      if (StageUtils.getHostName().equals(resolvedPrincipal.getHostName())) {
 
         // If the principal name exists in one of the shared data maps, it has been processed by the
         // current "Enable Kerberos" or "Add component" workflow and therefore should already have
         // the correct permissions assigned. The relevant keytab files can be skipped.
         Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
-        if ((principalPasswordMap == null) || !principalPasswordMap.containsKey(evaluatedPrincipal)) {
+        if ((principalPasswordMap == null) || !principalPasswordMap.containsKey(resolvedPrincipal.getPrincipal())) {
 
-          String keytabFilePath = identityRecord.get(KerberosIdentityDataFile.KEYTAB_FILE_PATH);
+          String keytabFilePath = resolvedPrincipal.getKeytabPath();
 
           if (!StringUtils.isEmpty(keytabFilePath)) {
             Set<String> visited = (Set<String>) requestSharedDataContext.get(this.getClass().getName() + "_visited");
 
             if (!visited.contains(keytabFilePath)) {
-              String ownerName = identityRecord.get(KerberosIdentityDataFile.KEYTAB_FILE_OWNER_NAME);
-              String ownerAccess = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_OWNER_ACCESS);
+              String ownerName = resolvedPrincipal.getResolvedKerberosKeytab().getOwnerName();
+              String ownerAccess = resolvedPrincipal.getResolvedKerberosKeytab().getOwnerAccess();
               boolean ownerWritable = "w".equalsIgnoreCase(ownerAccess) || "rw".equalsIgnoreCase(ownerAccess);
               boolean ownerReadable = "r".equalsIgnoreCase(ownerAccess) || "rw".equalsIgnoreCase(ownerAccess);
-              String groupName = identityRecord.get(KerberosIdentityDataFile.KEYTAB_FILE_GROUP_NAME);
-              String groupAccess = identityRecord.get(KerberosIdentityDataFileReader.KEYTAB_FILE_OWNER_ACCESS);
+              String groupName = resolvedPrincipal.getResolvedKerberosKeytab().getGroupName();
+              String groupAccess = resolvedPrincipal.getResolvedKerberosKeytab().getGroupAccess();
               boolean groupWritable = "w".equalsIgnoreCase(groupAccess) || "rw".equalsIgnoreCase(groupAccess);
               boolean groupReadable = "r".equalsIgnoreCase(groupAccess) || "rw".equalsIgnoreCase(groupAccess);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
----------------------------------------------------------------------
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 ff5f5ce..2c9aa8c 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
@@ -18,11 +18,10 @@
 
 package org.apache.ambari.server.serveraction.kerberos;
 
-import static org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileReader.DATA_FILE_NAME;
-
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Type;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -36,6 +35,9 @@ import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
 import org.apache.ambari.server.serveraction.AbstractServerAction;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.KerberosKeytabController;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosKeytab;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.utils.StageUtils;
@@ -178,6 +180,10 @@ public abstract class KerberosServerAction extends AbstractServerAction {
 
   @Inject
   HostDAO hostDAO;
+
+  @Inject
+  KerberosKeytabController kerberosKeytabController;
+
   /**
    * Given a (command parameter) Map and a property name, attempts to safely retrieve the requested
    * data.
@@ -235,10 +241,9 @@ public abstract class KerberosServerAction extends AbstractServerAction {
    */
   protected static OperationType getOperationType(Map<String, String> commandParameters) {
     String value = getCommandParameterValue(commandParameters, OPERATION_TYPE);
-    if(StringUtils.isEmpty(value)) {
+    if (StringUtils.isEmpty(value)) {
       return OperationType.DEFAULT;
-    }
-    else {
+    } else {
       return OperationType.valueOf(value.toUpperCase());
     }
   }
@@ -365,14 +370,32 @@ public abstract class KerberosServerAction extends AbstractServerAction {
   }
 
   /**
+   * Returns preconfigure type passed to current action.
+   *
+   * @return PreconfigureServiceType
+   */
+  protected PreconfigureServiceType getCommandPreconfigureType() {
+    String preconfigureServices = getCommandParameterValue(getCommandParameters(), PRECONFIGURE_SERVICES);
+    PreconfigureServiceType type = null;
+    if (!StringUtils.isEmpty(preconfigureServices)) {
+      try {
+        type = PreconfigureServiceType.valueOf(preconfigureServices.toUpperCase());
+      } catch (Throwable t) {
+        LOG.warn("Invalid preconfigure_services value, assuming DEFAULT: {}", preconfigureServices);
+        type = PreconfigureServiceType.DEFAULT;
+      }
+    }
+    return type;
+  }
+
+  /**
    * Iterates through the Kerberos identity metadata from the
    * {@link org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileReader} and calls
    * the implementing class to handle each identity found.
    * <p/>
-   * Using the "data_directory" value from this action's command parameters map, creates a
-   * {@link KerberosIdentityDataFileReader} to parse
-   * the relative identity.dat file and iterate through its "records".  Each "record" is process using
-   * {@link #processRecord(Map, String, KerberosOperationHandler, Map, Map)}.
+   * 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, Map)}.
    *
    * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
    *                                 to a given request
@@ -390,102 +413,43 @@ public abstract class KerberosServerAction extends AbstractServerAction {
     if (commandParameters != null) {
       // Grab the relevant data from this action's command parameters map
       PrincipalKeyCredential administratorCredential = kerberosHelper.getKDCAdministratorCredentials(getClusterName());
-      String defaultRealm = getDefaultRealm(commandParameters);
       KDCType kdcType = getKDCType(commandParameters);
-      String dataDirectoryPath = getDataDirectoryPath(commandParameters);
-
-      if (dataDirectoryPath != null) {
-        File dataDirectory = new File(dataDirectoryPath);
-
-        // If the data directory exists, attempt to process further, else assume there is no work to do
-        if (dataDirectory.exists()) {
-          if (!dataDirectory.isDirectory() || !dataDirectory.canRead()) {
-            String message = String.format("Failed to process the identities, the data directory is not accessible: %s",
-                dataDirectory.getAbsolutePath());
-            actionLog.writeStdErr(message);
-            LOG.error(message);
-            throw new AmbariException(message);
-          }
-          // The "identity data" file may or may not exist in the data directory, depending on if
-          // there is work to do or not.
-          File identityDataFile = new File(dataDirectory, DATA_FILE_NAME);
-
-          if (identityDataFile.exists()) {
-            if (!identityDataFile.canRead()) {
-              String message = String.format("Failed to process the identities, cannot read the index file: %s",
-                  identityDataFile.getAbsolutePath());
-              actionLog.writeStdErr(message);
-              LOG.error(message);
-              throw new AmbariException(message);
-            }
-
-            KerberosOperationHandler handler = kerberosOperationHandlerFactory.getKerberosOperationHandler(kdcType);
-            if (handler == null) {
-              String message = String.format("Failed to process the identities, a KDC operation handler was not found for the KDC type of : %s",
-                  kdcType.toString());
-              actionLog.writeStdErr(message);
-              LOG.error(message);
-              throw new AmbariException(message);
-            }
-
-            Map<String, String> kerberosConfiguration = getConfiguration("kerberos-env");
+      String defaultRealm = getDefaultRealm(commandParameters);
 
-            try {
-              handler.open(administratorCredential, defaultRealm, kerberosConfiguration);
-            } catch (KerberosOperationException e) {
-              String message = String.format("Failed to process the identities, could not properly open the KDC operation handler: %s",
-                  e.getMessage());
-              actionLog.writeStdErr(message);
-              LOG.error(message);
-              throw new AmbariException(message, e);
-            }
+      KerberosOperationHandler handler = kerberosOperationHandlerFactory.getKerberosOperationHandler(kdcType);
+      Map<String, String> kerberosConfiguration = getConfiguration("kerberos-env");
+
+      try {
+        handler.open(administratorCredential, defaultRealm, kerberosConfiguration);
+      } catch (KerberosOperationException e) {
+        String message = String.format("Failed to process the identities, could not properly open the KDC operation handler: %s",
+            e.getMessage());
+        actionLog.writeStdErr(message);
+        LOG.error(message);
+        throw new AmbariException(message, e);
+      }
 
-            // Create the data file reader to parse and iterate through the records
-            KerberosIdentityDataFileReader reader = null;
-            try {
-              reader = kerberosIdentityDataFileReaderFactory.createKerberosIdentityDataFileReader(identityDataFile);
-              for (Map<String, String> record : reader) {
-                // Process the current record
-                commandReport = processRecord(record, defaultRealm, handler, kerberosConfiguration, requestSharedDataContext);
-
-                // 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 (AmbariException e) {
-              // Catch this separately from IOException since the reason it was thrown was not the same
-              // Note: AmbariException is an IOException, so there may be some confusion
-              throw new AmbariException(e.getMessage(), e);
-            } catch (IOException e) {
-              String message = String.format("Failed to process the identities, cannot read the index file: %s",
-                  identityDataFile.getAbsolutePath());
-              actionLog.writeStdErr(message);
-              LOG.error(message, e);
-              throw new AmbariException(message, e);
-            } finally {
-              if (reader != null) {
-                // The reader needs to be closed, if it fails to close ignore the exception since
-                // there is little we can or care to do about it now.
-                try {
-                  reader.close();
-                } catch (IOException e) {
-                  // Ignore this...
-                }
-              }
-
-              // The KerberosOperationHandler needs to be closed, if it fails to close ignore the
-              // exception since there is little we can or care to do about it now.
-              try {
-                handler.close();
-              } catch (KerberosOperationException e) {
-                // Ignore this...
-              }
+      try {
+        for (ResolvedKerberosKeytab rkk : kerberosKeytabController.getFilteredKeytabs((Map<String, Collection<String>>) getServiceComponentFilter(), getHostFilter(), getIdentityFilter())) {
+          for (ResolvedKerberosPrincipal principal : rkk.getPrincipals()) {
+            commandReport = processIdentity(principal, handler, kerberosConfiguration, requestSharedDataContext);
+            // 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;
             }
           }
         }
+      } finally {
+        // The KerberosOperationHandler needs to be closed, if it fails to close ignore the
+        // exception since there is little we can or care to do about it now.
+        try {
+          handler.close();
+        } catch (KerberosOperationException e) {
+          // Ignore this...
+        }
       }
+
     }
 
     actionLog.writeStdOut("Processing identities completed.");
@@ -502,11 +466,10 @@ public abstract class KerberosServerAction extends AbstractServerAction {
    * Processes an identity as necessary.
    * <p/>
    * This method is called from {@link #processIdentities(Map)} for each
-   * identity "record" found in the Kerberos identity metadata file. After processing, it is expected
+   * principal found by specified filter. After processing, it is expected
    * that the return value is null on success and a CommandReport (indicating the error) on failure.
    *
-   * @param identityRecord           a Map containing the data for the current identity record
-   * @param evaluatedPrincipal       a String indicating the relevant principal
+   * @param resolvedPrincipal        a ResolvedKerberosPrincipal object to process
    * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
    *                                 tasks for specific Kerberos implementations
    *                                 (MIT, Active Directory, etc...)
@@ -516,48 +479,12 @@ public abstract class KerberosServerAction extends AbstractServerAction {
    *                                 condition; or null, indicating a success condition
    * @throws AmbariException if an error occurs while processing the identity record
    */
-  protected abstract CommandReport processIdentity(Map<String, String> identityRecord,
-                                                   String evaluatedPrincipal,
+  protected abstract CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal,
                                                    KerberosOperationHandler operationHandler,
                                                    Map<String, String> kerberosConfiguration,
                                                    Map<String, Object> requestSharedDataContext)
       throws AmbariException;
 
-  /**
-   * Process and prepares an identity record to be handled by the implementing class.
-   * <p/>
-   * Given the data from the record Map, attempts to replace variables in the principal pattern to
-   * generate a concrete principal value to further process. This "evaluated principal" is then passed to
-   * {@link #processIdentity(Map, String, KerberosOperationHandler, Map, Map)}
-   * to be handled as needed.
-   *
-   * @param record                   a Map containing the data for the current identity record
-   * @param defaultRealm             a String declaring the default Kerberos realm
-   * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
-   *                                 tasks for specific Kerberos implementations
-   *                                 (MIT, Active Directory, etc...)
-   * @param kerberosConfiguration    a Map of configuration properties from kerberos-env
-   * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
-   *                                 to a given request  @return a CommandReport, indicating an error
-   *                                 condition; or null, indicating a success condition
-   * @throws AmbariException if an error occurs while processing the identity record
-   */
-  private CommandReport processRecord(Map<String, String> record, String defaultRealm,
-                                      KerberosOperationHandler operationHandler,
-                                      Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext)
-      throws AmbariException {
-    CommandReport commandReport = null;
-
-    if (record != null) {
-      String principal = record.get(KerberosIdentityDataFileReader.PRINCIPAL);
-      if (principal != null) {
-        commandReport = processIdentity(record, principal, operationHandler, kerberosConfiguration, requestSharedDataContext);
-      }
-    }
-
-    return commandReport;
-  }
-
   protected void deleteDataDirectory(String dataDirectoryPath) {
     // Make sure this is a relevant directory. We don't want to accidentally allow _ANY_ directory
     // to be deleted.
@@ -600,7 +527,32 @@ public abstract class KerberosServerAction extends AbstractServerAction {
     return hostFilers != null && hostFilers.size() > 0;
   }
 
-  protected Long ambariServerHostID(){
+
+  protected Map<String, ? extends Collection<String>> getServiceComponentFilter() {
+    String serializedValue = getCommandParameterValue(SERVICE_COMPONENT_FILTER);
+
+    if (serializedValue != null) {
+      Type type = new TypeToken<Map<String, ? extends Collection<String>>>() {
+      }.getType();
+      return StageUtils.getGson().fromJson(serializedValue, type);
+    } else {
+      return null;
+    }
+  }
+
+  protected Collection<String> getIdentityFilter() {
+    String serializedValue = getCommandParameterValue(IDENTITY_FILTER);
+
+    if (serializedValue != null) {
+      Type type = new TypeToken<Collection<String>>() {
+      }.getType();
+      return StageUtils.getGson().fromJson(serializedValue, type);
+    } else {
+      return null;
+    }
+  }
+
+  protected Long ambariServerHostID() {
     String ambariServerHostName = StageUtils.getHostName();
     HostEntity ambariServerHostEntity = hostDAO.findByName(ambariServerHostName);
     return (ambariServerHostEntity == null)
@@ -608,6 +560,65 @@ public abstract class KerberosServerAction extends AbstractServerAction {
         : ambariServerHostEntity.getHostId();
   }
 
+
+  public static class KerberosCommandParameters {
+    private Map<String, String> params;
+
+    public KerberosCommandParameters(ExecutionCommand ec) {
+      params = ec.getCommandParams();
+    }
+
+    public KerberosCommandParameters(AbstractServerAction serverAction) {
+      this(serverAction.getExecutionCommand());
+    }
+
+    public Set<String> getHostFilter() {
+      String serializedValue = getCommandParameterValue(HOST_FILTER);
+
+      if (serializedValue != null) {
+        Type type = new TypeToken<Set<String>>() {
+        }.getType();
+        return StageUtils.getGson().fromJson(serializedValue, type);
+      } else {
+        return null;
+      }
+    }
+
+    public boolean hasHostFilters() {
+      Set<String> hostFilers = getHostFilter();
+      return hostFilers != null && hostFilers.size() > 0;
+    }
+
+    public Map<String, ? extends Collection<String>> getServiceComponentFilter() {
+      String serializedValue = getCommandParameterValue(SERVICE_COMPONENT_FILTER);
+
+      if (serializedValue != null) {
+        Type type = new TypeToken<Map<String, ? extends Collection<String>>>() {
+        }.getType();
+        return StageUtils.getGson().fromJson(serializedValue, type);
+      } else {
+        return null;
+      }
+    }
+
+    public Collection<String> getIdentityFilter() {
+      String serializedValue = getCommandParameterValue(IDENTITY_FILTER);
+
+      if (serializedValue != null) {
+        Type type = new TypeToken<Collection<String>>() {
+        }.getType();
+        return StageUtils.getGson().fromJson(serializedValue, type);
+      } else {
+        return null;
+      }
+    }
+
+    public String getCommandParameterValue(String propertyName) {
+      Map<String, String> commandParameters = params;
+      return (commandParameters == null) ? null : commandParameters.get(propertyName);
+    }
+  }
+
   /**
    * A Kerberos operation type
    * <ul>
@@ -623,7 +634,7 @@ public abstract class KerberosServerAction extends AbstractServerAction {
     RECREATE_ALL,
 
     /**
-     *  Generate keytabs for only those that are missing
+     * Generate keytabs for only those that are missing
      */
     CREATE_MISSING,
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
index 671ad95..2d29bdc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
@@ -29,11 +29,11 @@ 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.controller.KerberosHelper;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
-import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,17 +70,7 @@ public class PrepareEnableKerberosServerAction extends PrepareKerberosIdentities
 
     Map<String, String> commandParameters = getCommandParameters();
 
-    String preconfigureServices = getCommandParameterValue(commandParameters, PRECONFIGURE_SERVICES);
-    PreconfigureServiceType type = null;
-    if (!StringUtils.isEmpty(preconfigureServices)) {
-      try {
-        type = PreconfigureServiceType.valueOf(preconfigureServices.toUpperCase());
-      } catch (Throwable t) {
-        LOG.warn("Invalid preconfigure_services value, assuming DEFAULT: {}", preconfigureServices);
-        type = PreconfigureServiceType.DEFAULT;
-      }
-    }
-
+    PreconfigureServiceType type = getCommandPreconfigureType();
     KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, type != PreconfigureServiceType.NONE);
     if (type == PreconfigureServiceType.ALL) {
       // Force all services to be flagged for pre-configuration...
@@ -144,7 +134,7 @@ public class PrepareEnableKerberosServerAction extends PrepareKerberosIdentities
   }
 
   @Override
-  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal, KerberosOperationHandler operationHandler, Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException {
+  protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal, KerberosOperationHandler operationHandler, Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException {
     throw new UnsupportedOperationException();
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
index 83a2106..c7f2003 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
@@ -140,15 +140,6 @@ public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerber
     return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
   }
 
-  @Override
-  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal,
-                                          KerberosOperationHandler operationHandler,
-                                          Map<String, String> kerberosConfiguration,
-                                          Map<String, Object> requestSharedDataContext)
-      throws AmbariException {
-    throw new UnsupportedOperationException();
-  }
-
   /**
    * Calls {@link KerberosHelper#getKerberosDescriptor(Cluster, boolean)}
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..4993902
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos.stageutils;
+
+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 org.apache.ambari.server.orm.dao.KerberosKeytabDAO;
+import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+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 com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * Helper class to construct convenient wrappers around database entities related to kerberos.
+ */
+@Singleton
+public class KerberosKeytabController {
+  @Inject
+  private KerberosKeytabDAO kerberosKeytabDAO;
+
+  @Inject
+  private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
+
+  /**
+   * Tries to find keytab by keytab path in destination filesystem.
+   *
+   * @param file keytab path
+   * @return found keytab or null
+   */
+  public ResolvedKerberosKeytab getKeytabByFile(String file) {
+    return getKeytabByFile(file, true);
+  }
+
+  /**
+   * Tries to find keytab by keytab path in destination filesystem.
+   *
+   * @param file keytab path
+   * @param resolvePrincipals include resolved principals
+   * @return found keytab or null
+   */
+  public ResolvedKerberosKeytab getKeytabByFile(String file, boolean resolvePrincipals) {
+    return fromKeytabEntity(kerberosKeytabDAO.find(file), resolvePrincipals);
+  }
+
+  /**
+   * Returns all keytabs managed by ambari.
+   *
+   * @return all keytabs
+   */
+  public Set<ResolvedKerberosKeytab> getAllKeytabs() {
+    return fromKeytabEntities(kerberosKeytabDAO.findAll());
+  }
+
+  /**
+   * Returns all keytabs that contains given principal.
+   *
+   * @param rkp principal to filter keytabs by
+   * @return set of keytabs found
+   */
+  public Set<ResolvedKerberosKeytab> getFromPrincipal(ResolvedKerberosPrincipal rkp) {
+    return fromKeytabEntities(kerberosKeytabDAO.findByPrincipalAndHost(rkp.getPrincipal(), rkp.getHostId()));
+  }
+
+  /**
+   * Returns keytabs with principals filtered by host, principal name or service(and component) names.
+   *
+   * @param serviceComponentFilter service-component filter
+   * @param hostFilter host filter
+   * @param identityFilter identity(principal) filter
+   * @return set of keytabs found
+   */
+  public Set<ResolvedKerberosKeytab> getFilteredKeytabs(Map<String, Collection<String>> serviceComponentFilter,
+                                                        Set<String> hostFilter, Collection<String> identityFilter) {
+    if (serviceComponentFilter == null && hostFilter == null && identityFilter == null) {
+      return getAllKeytabs();
+    }
+    List<KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter> filters = splitServiceFilter(serviceComponentFilter);
+    for (KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter filter : filters) {
+      filter.setHostNames(hostFilter);
+      filter.setPrincipals(identityFilter);
+    }
+
+    Set<ResolvedKerberosPrincipal> filteredPrincipals = fromPrincipalEntities(kerberosKeytabPrincipalDAO.findByFilters(filters));
+    HashMap<String, ResolvedKerberosKeytab> resultMap = new HashMap<>();
+    for (ResolvedKerberosPrincipal principal : filteredPrincipals) {
+      if (!resultMap.containsKey(principal.getKeytabPath())) {
+        resultMap.put(principal.getKeytabPath(), getKeytabByFile(principal.getKeytabPath(), false));
+      }
+      ResolvedKerberosKeytab keytab = resultMap.get(principal.getKeytabPath());
+      keytab.addPrincipal(principal);
+    }
+    return Sets.newHashSet(resultMap.values());
+  }
+
+  /**
+   * This function split serviceComponentFilter to two filters, one with specific components, and another one with service
+   * only. Can return only one filter if filter contain only one type of mapping(whole service or component based)
+   * or empty filter if no serviceComponentFilter provided.
+   *
+   * @param serviceComponentFilter
+   * @return
+   */
+  private List<KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter> splitServiceFilter(Map<String, Collection<String>> serviceComponentFilter) {
+    if (serviceComponentFilter != null && serviceComponentFilter.size() > 0) {
+      Set<String> serviceSet = new HashSet<>();
+      Set<String> componentSet = new HashSet<>();
+      Set<String> serviceOnlySet = new HashSet<>();
+      serviceSet.addAll(serviceComponentFilter.keySet());
+      for (String serviceName : serviceSet) {
+        Collection<String> serviceComponents = serviceComponentFilter.get(serviceName);
+        if (serviceComponents.contains("*")) { // star means that this is filtered by whole SERVICE
+          serviceOnlySet.add(serviceName);
+          serviceSet.remove(serviceName); // remove service from regular
+        } else {
+          componentSet.addAll(serviceComponents);
+        }
+      }
+      List<KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter> result = new ArrayList<>();
+      if (serviceSet.size() > 0) {
+        result.add(new KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter(
+          null,
+          serviceSet,
+          componentSet,
+          null
+        ));
+      }
+      if (serviceOnlySet.size() > 0) {
+        result.add(new KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter(
+          null,
+          serviceOnlySet,
+          null,
+          null
+        ));
+      }
+      if (result.size() > 0) {
+        return result;
+      }
+    }
+
+    return Lists.newArrayList(new KerberosKeytabPrincipalDAO.KerberosKeytabPrincipalFilter(null,null,null,null));
+  }
+
+  private ResolvedKerberosKeytab fromKeytabEntity(KerberosKeytabEntity kke, boolean resolvePrincipals) {
+    Set<ResolvedKerberosPrincipal> principals = resolvePrincipals ? fromPrincipalEntities(kke.getKerberosKeytabPrincipalEntities()) : new HashSet<>();
+    return new ResolvedKerberosKeytab(
+      kke.getKeytabPath(),
+      kke.getOwnerName(),
+      kke.getOwnerAccess(),
+      kke.getGroupName(),
+      kke.getGroupAccess(),
+      principals,
+      kke.isAmbariServerKeytab(),
+      kke.isWriteAmbariJaasFile()
+    );
+  }
+
+  private ResolvedKerberosKeytab fromKeytabEntity(KerberosKeytabEntity kke) {
+    return fromKeytabEntity(kke, true);
+  }
+
+  private Set<ResolvedKerberosKeytab> fromKeytabEntities(Collection<KerberosKeytabEntity> keytabEntities) {
+    ImmutableSet.Builder<ResolvedKerberosKeytab> builder = ImmutableSet.builder();
+    for (KerberosKeytabEntity kkpe : keytabEntities) {
+      builder.add(fromKeytabEntity(kkpe));
+    }
+    return builder.build();
+  }
+
+  private Set<ResolvedKerberosPrincipal> fromPrincipalEntities(Collection<KerberosKeytabPrincipalEntity> principalEntities) {
+    ImmutableSet.Builder<ResolvedKerberosPrincipal> builder = ImmutableSet.builder();
+    for (KerberosKeytabPrincipalEntity kkpe : principalEntities) {
+      KerberosPrincipalEntity kpe = kkpe.getPrincipalEntity();
+      ResolvedKerberosPrincipal rkp = new ResolvedKerberosPrincipal(
+        kkpe.getHostId(),
+        kkpe.getHostName(),
+        kkpe.getPrincipalName(),
+        kpe.isService(),
+        kpe.getCachedKeytabPath(),
+        kkpe.getKeytabPath(),
+        kkpe.getServiceMappingAsMultimap());
+      builder.add(rkp);
+    }
+    return builder.build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosKeytab.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosKeytab.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosKeytab.java
index 17e484a..3233915 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosKeytab.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosKeytab.java
@@ -18,21 +18,17 @@
 
 package org.apache.ambari.server.serveraction.kerberos.stageutils;
 
-import java.util.Map;
+import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.ambari.server.state.kerberos.VariableReplacementHelper;
-import org.apache.commons.lang3.tuple.Pair;
-
-import com.google.common.collect.ImmutableSet;
 
 /**
  * Class that represents keytab. Contains principals that mapped to host.
- * Same keytab can have different set of principals on different hosts.
+ * Same keytab can have different set of principals on different hosts for different services.
+ * Each principal identified by host and keytab it belongs to and contain mapping that shows in which services and
+ * components given principal used.
  */
-// TODO This class need to replace {@link org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFile}
-// TODO and all related structures and become main item that {@link org.apache.ambari.server.serveraction.kerberos.KerberosServerAction}
-// TODO operates with instead of identity records.
 public class ResolvedKerberosKeytab {
 
   private String ownerName = null;
@@ -40,43 +36,36 @@ public class ResolvedKerberosKeytab {
   private String groupName = null;
   private String groupAccess = null;
   private String file = null;
-  private Set<Pair<Long, Pair<String, String>>> mappedPrincipals = null;
+  private Set<ResolvedKerberosPrincipal> principals = new HashSet<>();
   private boolean isAmbariServerKeytab = false;
   private boolean mustWriteAmbariJaasFile = false;
 
   public ResolvedKerberosKeytab(
-      String file,
-      String ownerName,
-      String ownerAccess,
-      String groupName,
-      String groupAccess,
-      Set<Pair<Long, Pair<String, String>>> mappedPrincipals,
-      boolean isAmbariServerKeytab,
-      boolean writeAmbariJaasFile
+    String file,
+    String ownerName,
+    String ownerAccess,
+    String groupName,
+    String groupAccess,
+    Set<ResolvedKerberosPrincipal> principals,
+    boolean isAmbariServerKeytab,
+    boolean writeAmbariJaasFile
   ) {
     this.ownerName = ownerName;
     this.ownerAccess = ownerAccess;
     this.groupName = groupName;
     this.groupAccess = groupAccess;
     this.file = file;
-    this.mappedPrincipals = mappedPrincipals;
+    setPrincipals(principals);
     this.isAmbariServerKeytab = isAmbariServerKeytab;
     this.mustWriteAmbariJaasFile = writeAmbariJaasFile;
+
   }
 
   /**
    * Gets the path to the keytab file
-   * <p/>
-   * The value may include variable placeholders to be replaced as needed
-   * <ul>
-   * <li>
-   * ${variable} placeholders are replaced on the server - see
-   * {@link VariableReplacementHelper#replaceVariables(String, Map)}
-   * </li>
-   * </ul>
    *
    * @return a String declaring the keytab file's absolute path
-   * @see VariableReplacementHelper#replaceVariables(String, Map)
+   * @see VariableReplacementHelper#replaceVariables(String, java.util.Map)
    */
   public String getFile() {
     return file;
@@ -175,47 +164,36 @@ public class ResolvedKerberosKeytab {
   /**
    * Gets evaluated host-to-principal set associated with given keytab.
    *
-   * @return a Set with mappedPrincipals associated with given keytab
+   * @return a Set with principals associated with given keytab
    */
-  public Set<Pair<Long, Pair<String, String>>> getMappedPrincipals() {
-    return mappedPrincipals;
+  public Set<ResolvedKerberosPrincipal> getPrincipals() {
+    return principals;
   }
 
   /**
    * Sets evaluated host-to-principal set associated with given keytab.
    *
-   * @param mappedPrincipals a Map with host-to-principal mapping associated with given keytab
-   */
-  public void setMappedPrincipals(Set<Pair<Long, Pair<String, String>>> mappedPrincipals) {
-    this.mappedPrincipals = mappedPrincipals;
-  }
-
-  /**
-   * Gets set of hosts associated with given keytab.
-   *
-   * @return a Set with hosts
+   * @param principals set of principals to add
    */
-  public Set<Long> getHosts() {
-    ImmutableSet.Builder<Long> builder = ImmutableSet.builder();
-    for (Pair<Long, Pair<String, String>> principal : getMappedPrincipals()) {
-      if (principal.getLeft() != null) {
-        builder.add(principal.getLeft());
+  public void setPrincipals(Set<ResolvedKerberosPrincipal> principals) {
+    this.principals = principals;
+    if (principals != null) {
+      for (ResolvedKerberosPrincipal principal : this.principals) {
+        principal.setResolvedKerberosKeytab(this);
       }
     }
-    return builder.build();
   }
 
   /**
-   * Gets a set of principals associated with given keytab.
+   * Add principal to keytab.
    *
-   * @return a Set of principals
+   * @param principal resolved principal to add
    */
-  public Set<Pair<String, String>> getPrincipals() {
-    ImmutableSet.Builder<Pair<String, String>> builder = ImmutableSet.builder();
-    for (Pair<Long, Pair<String, String>> principal : getMappedPrincipals()) {
-      builder.add(principal.getRight());
+  public void addPrincipal(ResolvedKerberosPrincipal principal) {
+    if (!principals.contains(principal)) {
+      principal.setResolvedKerberosKeytab(this);
+      principals.add(principal);
     }
-    return builder.build();
   }
 
   /**
@@ -254,4 +232,37 @@ public class ResolvedKerberosKeytab {
   public void setMustWriteAmbariJaasFile(boolean mustWriteAmbariJaasFile) {
     this.mustWriteAmbariJaasFile = mustWriteAmbariJaasFile;
   }
+
+  /**
+   * Merge principals from one keytab to given.
+   *
+   * @param otherKeytab keytab to merge principals from
+   */
+  public void mergePrincipals(ResolvedKerberosKeytab otherKeytab) {
+    for (ResolvedKerberosPrincipal rkp : otherKeytab.getPrincipals()) {
+      ResolvedKerberosPrincipal existent = findPrincipal(rkp.getHostId(), rkp.getPrincipal(), rkp.getKeytabPath());
+      if (existent != null) {
+        existent.mergeComponentMapping(rkp);
+      } else {
+        principals.add(rkp);
+      }
+    }
+  }
+
+  private ResolvedKerberosPrincipal findPrincipal(Long hostId, String principal, String keytabPath) {
+    for (ResolvedKerberosPrincipal rkp : principals) {
+      boolean hostIdIsSame;
+      if(hostId != null && rkp.getHostId() != null){
+        hostIdIsSame = hostId.equals(rkp.getHostId());
+      } else if(hostId == null && rkp.getHostId() == null) {
+        hostIdIsSame = true;
+      } else {
+        hostIdIsSame = false;
+      }
+      if (hostIdIsSame && principal.equals(rkp.getPrincipal())&& keytabPath.equals(rkp.getKeytabPath())) {
+        return rkp;
+      }
+    }
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/67fc4a37/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosPrincipal.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosPrincipal.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosPrincipal.java
new file mode 100644
index 0000000..100c1e2
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosPrincipal.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.ambari.server.serveraction.kerberos.stageutils;
+
+import org.apache.ambari.server.utils.StageUtils;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Class that represents principal and it info(host, keytab path, service and component mapping).
+ */
+public class ResolvedKerberosPrincipal {
+  private Long hostId;
+  private String hostName;
+  private String principal;
+  private boolean isService;
+  private String cacheFile;
+  private Multimap<String, String> serviceMapping = ArrayListMultimap.create();
+  private String keytabPath;
+  private ResolvedKerberosKeytab resolvedKerberosKeytab;
+
+  public ResolvedKerberosPrincipal(Long hostId, String hostName, String principal, boolean isService, String cacheFile, String serviceName, String componentName, String keytabPath) {
+    this.hostId = hostId;
+    this.hostName = hostName;
+    this.principal = principal;
+    this.isService = isService;
+    this.cacheFile = cacheFile;
+    this.keytabPath = keytabPath;
+    addComponentMapping(serviceName, componentName);
+  }
+
+  public ResolvedKerberosPrincipal(Long hostId, String hostName, String principal, boolean isService, String cacheFile, String keytabPath) {
+    this.hostId = hostId;
+    this.hostName = hostName;
+    this.principal = principal;
+    this.isService = isService;
+    this.cacheFile = cacheFile;
+    this.keytabPath = keytabPath;
+  }
+
+  public ResolvedKerberosPrincipal(Long hostId, String hostName, String principal, boolean isService, String cacheFile, String keytabPath, Multimap<String, String> serviceMapping) {
+    this.hostId = hostId;
+    this.hostName = hostName;
+    this.principal = principal;
+    this.isService = isService;
+    this.cacheFile = cacheFile;
+    this.keytabPath = keytabPath;
+    this.serviceMapping = serviceMapping;
+  }
+
+  public void addComponentMapping(String serviceName, String componentName) {
+    if (serviceName == null){
+      serviceName = "";
+    }
+    if (componentName == null) {
+      componentName = "*";
+    }
+    serviceMapping.get(serviceName).add(componentName);
+  }
+
+  public void mergeComponentMapping(ResolvedKerberosPrincipal other) {
+    serviceMapping.putAll(other.getServiceMapping());
+  }
+
+  public String getKeytabPath() {
+    return keytabPath;
+  }
+
+  public void setKeytabPath(String keytabPath) {
+    this.keytabPath = keytabPath;
+  }
+
+  public Long getHostId() {
+    return hostId;
+  }
+
+  public void setHostId(Long hostId) {
+    this.hostId = hostId;
+  }
+
+  public String getHostName() {
+    if (hostName == null) {
+      return StageUtils.getHostName();
+    }
+    return hostName;
+  }
+
+  public void setHostName(String hostName) {
+    this.hostName = hostName;
+  }
+
+  public String getPrincipal() {
+    return principal;
+  }
+
+  public void setPrincipal(String principal) {
+    this.principal = principal;
+  }
+
+  public boolean isService() {
+    return isService;
+  }
+
+  public void setService(boolean service) {
+    isService = service;
+  }
+
+  public String getCacheFile() {
+    return cacheFile;
+  }
+
+  public void setCacheFile(String cacheFile) {
+    this.cacheFile = cacheFile;
+  }
+
+  public Multimap<String, String> getServiceMapping() {
+    return serviceMapping;
+  }
+
+  public void setServiceMapping(Multimap<String, String>  serviceMapping) {
+    this.serviceMapping = serviceMapping;
+  }
+
+  public ResolvedKerberosKeytab getResolvedKerberosKeytab() {
+    return resolvedKerberosKeytab;
+  }
+
+  public void setResolvedKerberosKeytab(ResolvedKerberosKeytab resolvedKerberosKeytab) {
+    this.resolvedKerberosKeytab = resolvedKerberosKeytab;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ResolvedKerberosPrincipal principal1 = (ResolvedKerberosPrincipal) o;
+    return isService == principal1.isService &&
+      Objects.equal(hostId, principal1.hostId) &&
+      Objects.equal(hostName, principal1.hostName) &&
+      Objects.equal(principal, principal1.principal) &&
+      Objects.equal(cacheFile, principal1.cacheFile) &&
+      Objects.equal(serviceMapping, principal1.serviceMapping) &&
+      Objects.equal(keytabPath, principal1.keytabPath);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(hostId, hostName, principal, isService, cacheFile, serviceMapping, keytabPath);
+  }
+}