You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mr...@apache.org on 2017/11/27 23:29:22 UTC

[10/30] ambari git commit: Merge trunk with feature branch and fix some UT compilation issues (mradhakrishnan)

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java
index 0997f65..bbcd6e8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/MITKerberosOperationHandler.java
@@ -18,21 +18,17 @@
 
 package org.apache.ambari.server.serveraction.kerberos;
 
-import java.text.NumberFormat;
-import java.text.ParseException;
 import java.util.ArrayList;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.Set;
 
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
 import org.apache.ambari.server.utils.ShellCommandUtil;
-import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
+import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,37 +41,24 @@ import com.google.inject.Inject;
  * It is assumed that a MIT Kerberos client is installed and that the kdamin shell command is
  * available
  */
-public class MITKerberosOperationHandler extends KerberosOperationHandler {
+public class MITKerberosOperationHandler extends KDCKerberosOperationHandler {
+
+  private final static Logger LOG = LoggerFactory.getLogger(MITKerberosOperationHandler.class);
 
   @Inject
   private Configuration configuration;
 
   /**
-   * A regular expression pattern to use to parse the key number from the text captured from the
-   * get_principal kadmin command
-   */
-  private final static Pattern PATTERN_GET_KEY_NUMBER = Pattern.compile("^.*?Key: vno (\\d+).*$", Pattern.DOTALL);
-
-  private final static Logger LOG = LoggerFactory.getLogger(MITKerberosOperationHandler.class);
-
-  /**
    * A String containing user-specified attributes used when creating principals
    */
   private String createAttributes = null;
 
-  private String adminServerHost = null;
-
   /**
    * A String containing the resolved path to the kdamin executable
    */
   private String executableKadmin = null;
 
   /**
-   * A String containing the resolved path to the kdamin.local executable
-   */
-  private String executableKadminLocal = null;
-
-  /**
    * Prepares and creates resources to be used by this KerberosOperationHandler
    * <p/>
    * It is expected that this KerberosOperationHandler will not be used before this call.
@@ -92,39 +75,25 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
    * @throws KerberosOperationException           if an unexpected error occurred
    */
   @Override
-  public void open(PrincipalKeyCredential administratorCredentials, String realm,
-                   Map<String, String> kerberosConfiguration)
+  public void open(PrincipalKeyCredential administratorCredentials, String realm, Map<String, String> kerberosConfiguration)
       throws KerberosOperationException {
 
-    setAdministratorCredential(administratorCredentials);
-    setDefaultRealm(realm);
-
     if (kerberosConfiguration != null) {
-      setKeyEncryptionTypes(translateEncryptionTypes(kerberosConfiguration.get(KERBEROS_ENV_ENCRYPTION_TYPES), "\\s+"));
-      setAdminServerHost(kerberosConfiguration.get(KERBEROS_ENV_ADMIN_SERVER_HOST));
-      setExecutableSearchPaths(kerberosConfiguration.get(KERBEROS_ENV_EXECUTABLE_SEARCH_PATHS));
-      setCreateAttributes(kerberosConfiguration.get(KERBEROS_ENV_KDC_CREATE_ATTRIBUTES));
-    } else {
-      setKeyEncryptionTypes(null);
-      setAdminServerHost(null);
-      setExecutableSearchPaths((String) null);
-      setCreateAttributes(null);
+      createAttributes = kerberosConfiguration.get(KERBEROS_ENV_KDC_CREATE_ATTRIBUTES);
     }
 
     // Pre-determine the paths to relevant Kerberos executables
     executableKadmin = getExecutable("kadmin");
-    executableKadminLocal = getExecutable("kadmin.local");
 
-    setOpen(true);
+    super.open(administratorCredentials, realm, kerberosConfiguration);
   }
 
   @Override
   public void close() throws KerberosOperationException {
-    // There is nothing to do here.
-    setOpen(false);
-
+    createAttributes = null;
     executableKadmin = null;
-    executableKadminLocal = null;
+
+    super.close();
   }
 
   /**
@@ -134,6 +103,7 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
    * the result from STDOUT to determine if the presence of the specified principal.
    *
    * @param principal a String containing the principal to test
+   * @param service   a boolean value indicating whether the principal is for a service or not
    * @return true if the principal exists; false otherwise
    * @throws KerberosKDCConnectionException       if a connection to the KDC cannot be made
    * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
@@ -141,26 +111,25 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
    * @throws KerberosOperationException           if an unexpected error occurred
    */
   @Override
-  public boolean principalExists(String principal)
+  public boolean principalExists(String principal, boolean service)
       throws KerberosOperationException {
 
     if (!isOpen()) {
       throw new KerberosOperationException("This operation handler has not been opened");
     }
 
-    if (principal == null) {
-      return false;
-    } else {
+    if (!StringUtils.isEmpty(principal)) {
       // Create the KAdmin query to execute:
-      ShellCommandUtil.Result result = invokeKAdmin(String.format("get_principal %s", principal), null);
+      ShellCommandUtil.Result result = invokeKAdmin(String.format("get_principal %s", principal));
 
       // If there is data from STDOUT, see if the following string exists:
       //    Principal: <principal>
       String stdOut = result.getStdout();
       return (stdOut != null) && stdOut.contains(String.format("Principal: %s", principal));
     }
-  }
 
+    return false;
+  }
 
   /**
    * Creates a new principal in a previously configured MIT KDC
@@ -188,72 +157,25 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
 
     if (StringUtils.isEmpty(principal)) {
       throw new KerberosOperationException("Failed to create new principal - no principal specified");
-    } else if (StringUtils.isEmpty(password)) {
-      throw new KerberosOperationException("Failed to create new principal - no password specified");
-    } else {
-      String createAttributes = getCreateAttributes();
-      // Create the kdamin query:  add_principal <-randkey|-pw <password>> [<options>] <principal>
-      ShellCommandUtil.Result result = invokeKAdmin(String.format("add_principal %s %s",
-          (createAttributes == null) ? "" : createAttributes, principal), password);
-
-      // If there is data from STDOUT, see if the following string exists:
-      //    Principal "<principal>" created
-      String stdOut = result.getStdout();
-      String stdErr = result.getStderr();
-      if ((stdOut != null) && stdOut.contains(String.format("Principal \"%s\" created", principal))) {
-        return getKeyNumber(principal);
-      } else if ((stdErr != null) && stdErr.contains(String.format("Principal or policy already exists while creating \"%s\"", principal))) {
-        throw new KerberosPrincipalAlreadyExistsException(principal);
-      } else {
-        LOG.error("Failed to execute kadmin query: add_principal -pw \"********\" {} {}\nSTDOUT: {}\nSTDERR: {}",
-            (createAttributes == null) ? "" : createAttributes, principal, stdOut, result.getStderr());
-        throw new KerberosOperationException(String.format("Failed to create service principal for %s\nSTDOUT: %s\nSTDERR: %s",
-            principal, stdOut, result.getStderr()));
-      }
-    }
-  }
-
-  /**
-   * Updates the password for an existing principal in a previously configured MIT KDC
-   * <p/>
-   * This implementation creates a query to send to the kadmin shell command and then interrogates
-   * the exit code to determine if the operation executed successfully.
-   *
-   * @param principal a String containing the principal to update
-   * @param password  a String containing the password to set
-   * @return an Integer declaring the new key number
-   * @throws KerberosKDCConnectionException         if a connection to the KDC cannot be made
-   * @throws KerberosAdminAuthenticationException   if the administrator credentials fail to authenticate
-   * @throws KerberosRealmException                 if the realm does not map to a KDC
-   * @throws KerberosPrincipalDoesNotExistException if the principal does not exist
-   * @throws KerberosOperationException             if an unexpected error occurred
-   */
-  @Override
-  public Integer setPrincipalPassword(String principal, String password) throws KerberosOperationException {
-    if (!isOpen()) {
-      throw new KerberosOperationException("This operation handler has not been opened");
     }
 
-    if (StringUtils.isEmpty(principal)) {
-      throw new KerberosOperationException("Failed to set password - no principal specified");
-    } else if (StringUtils.isEmpty(password)) {
-      throw new KerberosOperationException("Failed to set password - no password specified");
+    // Create the kdamin query:  add_principal <-randkey|-pw <password>> [<options>] <principal>
+    ShellCommandUtil.Result result = invokeKAdmin(String.format("add_principal -randkey %s %s",
+        (createAttributes == null) ? "" : createAttributes, principal));
+
+    // If there is data from STDOUT, see if the following string exists:
+    //    Principal "<principal>" created
+    String stdOut = result.getStdout();
+    String stdErr = result.getStderr();
+    if ((stdOut != null) && stdOut.contains(String.format("Principal \"%s\" created", principal))) {
+      return 0;
+    } else if ((stdErr != null) && stdErr.contains(String.format("Principal or policy already exists while creating \"%s\"", principal))) {
+      throw new KerberosPrincipalAlreadyExistsException(principal);
     } else {
-      // Create the kdamin query:  change_password <-randkey|-pw <password>> <principal>
-      ShellCommandUtil.Result result = invokeKAdmin(String.format("change_password %s", principal), password);
-
-      String stdOut = result.getStdout();
-      String stdErr = result.getStderr();
-      if ((stdOut != null) && stdOut.contains(String.format("Password for \"%s\" changed", principal))) {
-        return getKeyNumber(principal);
-      } else if ((stdErr != null) && stdErr.contains("Principal does not exist")) {
-        throw new KerberosPrincipalDoesNotExistException(principal);
-      } else {
-        LOG.error("Failed to execute kadmin query: change_password -pw \"********\" {} \nSTDOUT: {}\nSTDERR: {}",
-            principal, stdOut, result.getStderr());
-        throw new KerberosOperationException(String.format("Failed to update password for %s\nSTDOUT: %s\nSTDERR: %s",
-            principal, stdOut, result.getStderr()));
-      }
+      LOG.error("Failed to execute kadmin query: add_principal -pw \"********\" {} {}\nSTDOUT: {}\nSTDERR: {}",
+          (createAttributes == null) ? "" : createAttributes, principal, stdOut, result.getStderr());
+      throw new KerberosOperationException(String.format("Failed to create service principal for %s\nSTDOUT: %s\nSTDERR: %s",
+          principal, stdOut, result.getStderr()));
     }
   }
 
@@ -263,6 +185,7 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
    * The implementation is specific to a particular type of KDC.
    *
    * @param principal a String containing the principal to remove
+   * @param service   a boolean value indicating whether the principal is for a service or not
    * @return true if the principal was successfully removed; otherwise false
    * @throws KerberosKDCConnectionException       if a connection to the KDC cannot be made
    * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
@@ -270,181 +193,63 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
    * @throws KerberosOperationException           if an unexpected error occurred
    */
   @Override
-  public boolean removePrincipal(String principal) throws KerberosOperationException {
+  public boolean removePrincipal(String principal, boolean service) throws KerberosOperationException {
     if (!isOpen()) {
       throw new KerberosOperationException("This operation handler has not been opened");
     }
 
     if (StringUtils.isEmpty(principal)) {
-      throw new KerberosOperationException("Failed to remove new principal - no principal specified");
-    } else {
-      ShellCommandUtil.Result result = invokeKAdmin(String.format("delete_principal -force %s", principal), null);
-
-      // If there is data from STDOUT, see if the following string exists:
-      //    Principal "<principal>" created
-      String stdOut = result.getStdout();
-      return (stdOut != null) && !stdOut.contains("Principal does not exist");
+      throw new KerberosOperationException("Failed to remove principal - no principal specified");
     }
-  }
 
-  /**
-   * Sets the KDC administrator server host address
-   *
-   * @param adminServerHost the ip address or FQDN of the KDC administrator server
-   */
-  public void setAdminServerHost(String adminServerHost) {
-    this.adminServerHost = adminServerHost;
-  }
+    ShellCommandUtil.Result result = invokeKAdmin(String.format("delete_principal -force %s", principal));
 
-  /**
-   * Gets the IP address or FQDN of the KDC administrator server
-   *
-   * @return the IP address or FQDN of the KDC administrator server
-   */
-  public String getAdminServerHost() {
-    return this.adminServerHost;
-  }
-
-  /**
-   * Sets the (additional) principal creation attributes
-   *
-   * @param createAttributes the additional principal creations attributes
-   */
-  public void setCreateAttributes(String createAttributes) {
-    this.createAttributes = createAttributes;
-  }
-
-  /**
-   * Gets the (additional) principal creation attributes
-   *
-   * @return the additional principal creations attributes or null
-   */
-  public String getCreateAttributes() {
-    return createAttributes;
-  }
-
-  /**
-   * Retrieves the current key number assigned to the identity identified by the specified principal
-   *
-   * @param principal a String declaring the principal to look up
-   * @return an Integer declaring the current key number
-   * @throws KerberosKDCConnectionException       if a connection to the KDC cannot be made
-   * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
-   * @throws KerberosRealmException               if the realm does not map to a KDC
-   * @throws KerberosOperationException           if an unexpected error occurred
-   */
-  private Integer getKeyNumber(String principal) throws KerberosOperationException {
-    if (!isOpen()) {
-      throw new KerberosOperationException("This operation handler has not been opened");
-    }
-
-    if (StringUtils.isEmpty(principal)) {
-      throw new KerberosOperationException("Failed to get key number for principal  - no principal specified");
-    } else {
-      // Create the kdamin query:  get_principal <principal>
-      ShellCommandUtil.Result result = invokeKAdmin(String.format("get_principal %s", principal), null);
-
-      String stdOut = result.getStdout();
-      if (stdOut == null) {
-        String message = String.format("Failed to get key number for %s:\n\tExitCode: %s\n\tSTDOUT: NULL\n\tSTDERR: %s",
-            principal, result.getExitCode(), result.getStderr());
-        LOG.warn(message);
-        throw new KerberosOperationException(message);
-      }
-
-      Matcher matcher = PATTERN_GET_KEY_NUMBER.matcher(stdOut);
-      if (matcher.matches()) {
-        NumberFormat numberFormat = NumberFormat.getIntegerInstance();
-        String keyNumber = matcher.group(1);
-
-        numberFormat.setGroupingUsed(false);
-        try {
-          Number number = numberFormat.parse(keyNumber);
-          return (number == null) ? 0 : number.intValue();
-        } catch (ParseException e) {
-          String message = String.format("Failed to get key number for %s - invalid key number value (%s):\n\tExitCode: %s\n\tSTDOUT: NULL\n\tSTDERR: %s",
-              principal, keyNumber, result.getExitCode(), result.getStderr());
-          LOG.warn(message);
-          throw new KerberosOperationException(message);
-        }
-      } else {
-        String message = String.format("Failed to get key number for %s - unexpected STDOUT data:\n\tExitCode: %s\n\tSTDOUT: NULL\n\tSTDERR: %s",
-            principal, result.getExitCode(), result.getStderr());
-        LOG.warn(message);
-        throw new KerberosOperationException(message);
-      }
-    }
+    // If there is data from STDOUT, see if the following string exists:
+    //    Principal "<principal>" created
+    String stdOut = result.getStdout();
+    return (stdOut != null) && !stdOut.contains("Principal does not exist");
   }
 
   /**
    * Invokes the kadmin shell command to issue queries
    *
-   * @param query        a String containing the query to send to the kdamin command
-   * @param userPassword a String containing the user's password to set or update if necessary,
-   *                     null if not needed
+   * @param query a String containing the query to send to the kdamin command
    * @return a ShellCommandUtil.Result containing the result of the operation
    * @throws KerberosKDCConnectionException       if a connection to the KDC cannot be made
    * @throws KerberosAdminAuthenticationException if the administrator credentials fail to authenticate
    * @throws KerberosRealmException               if the realm does not map to a KDC
    * @throws KerberosOperationException           if an unexpected error occurred
    */
-  protected ShellCommandUtil.Result invokeKAdmin(String query, String userPassword)
+  protected ShellCommandUtil.Result invokeKAdmin(String query)
       throws KerberosOperationException {
     if (StringUtils.isEmpty(query)) {
       throw new KerberosOperationException("Missing kadmin query");
     }
 
-    ShellCommandUtil.Result result = null;
-    PrincipalKeyCredential administratorCredential = getAdministratorCredential();
-    String defaultRealm = getDefaultRealm();
+    if (StringUtils.isEmpty(executableKadmin)) {
+      throw new KerberosOperationException("No path for kadmin is available - this KerberosOperationHandler may not have been opened.");
+    }
 
     List<String> command = new ArrayList<>();
+    command.add(executableKadmin);
 
-    String adminPrincipal = (administratorCredential == null)
-        ? null
-        : administratorCredential.getPrincipal();
-
-    ShellCommandUtil.InteractiveHandler interactiveHandler = null;
-
-    if (StringUtils.isEmpty(adminPrincipal)) {
-      // Set the kdamin interface to be kadmin.local
-      if (StringUtils.isEmpty(executableKadminLocal)) {
-        throw new KerberosOperationException("No path for kadmin.local is available - this KerberosOperationHandler may not have been opened.");
-      }
-
-      command.add(executableKadminLocal);
-
-      if (userPassword != null) {
-        interactiveHandler = new InteractivePasswordHandler(null, userPassword);
-      }
-    } else {
-      if (StringUtils.isEmpty(executableKadmin)) {
-        throw new KerberosOperationException("No path for kadmin is available - this KerberosOperationHandler may not have been opened.");
-      }
-      char[] adminPassword = administratorCredential.getKey();
-
-      // Set the kdamin interface to be kadmin
-      command.add(executableKadmin);
-
-      // Add explicit KDC admin host, if available
-      if (!StringUtils.isEmpty(getAdminServerHost())) {
-        command.add("-s");
-        command.add(getAdminServerHost());
-      }
-
-      // Add the administrative principal
-      command.add("-p");
-      command.add(adminPrincipal);
+    // Add the credential cache, if available
+    String credentialCacheFilePath = getCredentialCacheFilePath();
+    if (!StringUtils.isEmpty(credentialCacheFilePath)) {
+      command.add("-c");
+      command.add(credentialCacheFilePath);
+    }
 
-      if (!ArrayUtils.isEmpty(adminPassword)) {
-        interactiveHandler = new InteractivePasswordHandler(String.valueOf(adminPassword), userPassword);
-      } else if (userPassword != null) {
-        interactiveHandler = new InteractivePasswordHandler(null, userPassword);
-      }
+    // Add explicit KDC admin host, if available
+    String adminSeverHost = getAdminServerHost();
+    if (!StringUtils.isEmpty(adminSeverHost)) {
+      command.add("-s");
+      command.add(adminSeverHost);
     }
 
+    // Add default realm clause, if available
+    String defaultRealm = getDefaultRealm();
     if (!StringUtils.isEmpty(defaultRealm)) {
-      // Add default realm clause
       command.add("-r");
       command.add(defaultRealm);
     }
@@ -457,12 +262,13 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
       LOG.debug("Executing: {}", command);
     }
 
+    ShellCommandUtil.Result result = null;
     int retryCount = configuration.getKerberosOperationRetries();
     int tries = 0;
 
     while (tries <= retryCount) {
       try {
-        result = executeCommand(command.toArray(new String[command.size()]), null, interactiveHandler);
+        result = executeCommand(command.toArray(new String[command.size()]));
       } catch (KerberosOperationException exception) {
         if (tries == retryCount) {
           throw exception;
@@ -486,13 +292,16 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
     }
 
 
-    if (!result.isSuccessful()) {
+    if ((result == null) || !result.isSuccessful()) {
+      int exitCode = (result == null) ? -999 : result.getExitCode();
+      String stdOut = (result == null) ? "" : result.getStdout();
+      String stdErr = (result == null) ? "" : result.getStderr();
+
       String message = String.format("Failed to execute kadmin:\n\tCommand: %s\n\tExitCode: %s\n\tSTDOUT: %s\n\tSTDERR: %s",
-          command, result.getExitCode(), result.getStdout(), result.getStderr());
+          command, exitCode, stdOut, stdErr);
       LOG.warn(message);
 
       // Test STDERR to see of any "expected" error conditions were encountered...
-      String stdErr = result.getStderr();
       // Did admin credentials fail?
       if (stdErr.contains("Client not found in Kerberos database")) {
         throw new KerberosAdminAuthenticationException(stdErr);
@@ -513,57 +322,56 @@ public class MITKerberosOperationHandler extends KerberosOperationHandler {
       } else {
         throw new KerberosOperationException(String.format("Unexpected error condition executing the kadmin command. STDERR: %s", stdErr));
       }
+    } else {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("Executed the following command:\n{}\nSTDOUT: {}\nSTDERR: {}",
+            StringUtils.join(command, " "), result.getStdout(), result.getStderr());
+      }
     }
 
     return result;
   }
 
-  /**
-   * InteractivePasswordHandler is a {@link org.apache.ambari.server.utils.ShellCommandUtil.InteractiveHandler}
-   * implementation that answers queries from kadmin or kdamin.local command for the admin and/or user
-   * passwords.
-   */
-  protected static class InteractivePasswordHandler implements ShellCommandUtil.InteractiveHandler {
-    /**
-     * The queue of responses to return
-     */
-    private LinkedList<String> responses;
-    private Queue<String> currentResponses;
-
-    /**
-     * Constructor.
-     *
-     * @param adminPassword the KDC administrator's password (optional)
-     * @param userPassword  the user's password (optional)
-     */
-    public InteractivePasswordHandler(String adminPassword, String userPassword) {
-      responses = new LinkedList<>();
-
-      if (adminPassword != null) {
-        responses.offer(adminPassword);
-      }
+  @Override
+  protected String[] getKinitCommand(String executableKinit, PrincipalKeyCredential credentials, String credentialsCache) {
+    // kinit -c <path> -S kadmin/`hostname -f` <principal>
+    return new String[]{
+        executableKinit,
+        "-c",
+        credentialsCache,
+        "-S",
+        String.format("kadmin/%s", getAdminServerHost()),
+        credentials.getPrincipal()
+    };
+  }
 
-      if (userPassword != null) {
-        responses.offer(userPassword);
-        responses.offer(userPassword);  // Add a 2nd time for the password "confirmation" request
+  @Override
+  protected void exportKeytabFile(String principal, String keytabFileDestinationPath, Set<EncryptionType> keyEncryptionTypes) throws KerberosOperationException {
+    String encryptionTypeSpec = null;
+    if (!CollectionUtils.isEmpty(keyEncryptionTypes)) {
+      StringBuilder encryptionTypeSpecBuilder = new StringBuilder();
+      for (EncryptionType encryptionType : keyEncryptionTypes) {
+        if (encryptionTypeSpecBuilder.length() > 0) {
+          encryptionTypeSpecBuilder.append(',');
+        }
+        encryptionTypeSpecBuilder.append(encryptionType.getName());
+        encryptionTypeSpecBuilder.append(":normal");
       }
 
-      currentResponses = new LinkedList<>(responses);
+      encryptionTypeSpec = encryptionTypeSpecBuilder.toString();
     }
 
-    @Override
-    public boolean done() {
-      return currentResponses.size() == 0;
-    }
+    String query = (StringUtils.isEmpty(encryptionTypeSpec))
+        ? String.format("xst -k \"%s\" %s", keytabFileDestinationPath, principal)
+        : String.format("xst -k \"%s\" -e %s %s", keytabFileDestinationPath, encryptionTypeSpec, principal);
 
-    @Override
-    public String getResponse(String query) {
-      return currentResponses.poll();
-    }
+    ShellCommandUtil.Result result = invokeKAdmin(query);
 
-    @Override
-    public void start() {
-      currentResponses = new LinkedList<>(responses);
+    if (!result.isSuccessful()) {
+      String message = String.format("Failed to export the keytab file for %s:\n\tExitCode: %s\n\tSTDOUT: %s\n\tSTDERR: %s",
+          principal, result.getExitCode(), result.getStdout(), result.getStderr());
+      LOG.warn(message);
+      throw new KerberosOperationException(message);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
index e1f8419..b9381b4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
@@ -107,7 +107,7 @@ public class PrepareDisableKerberosServerAction extends AbstractPrepareKerberosS
     Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor, false, false);
 
     processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
-        configurations, kerberosConfigurations, includeAmbariIdentity, propertiesToIgnore, false);
+        configurations, kerberosConfigurations, includeAmbariIdentity, propertiesToIgnore);
 
     // Add auth-to-local configurations to the set of changes
     Map<String, Set<String>> authToLocalProperties = kerberosHelper.translateConfigurationSpecifications(kerberosDescriptor.getAllAuthToLocalProperties());

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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 335451f..671ad95 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
@@ -119,7 +119,7 @@ public class PrepareEnableKerberosServerAction extends PrepareKerberosIdentities
     Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor, false, false);
 
     processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
-        configurations, kerberosConfigurations, true, propertiesToIgnore, false);
+        configurations, kerberosConfigurations, true, propertiesToIgnore);
 
     // Calculate the set of configurations to update and replace any variables
     // using the previously calculated Map of configurations for the host.

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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 038d1b5..83a2106 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
@@ -30,6 +30,8 @@ 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.controller.RootComponent;
+import org.apache.ambari.server.controller.RootService;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
@@ -110,7 +112,8 @@ public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerber
     if (serviceComponentFilter != null) {
       // If we are including the Ambari identity; then ensure that if a service/component filter is set,
       // it contains the AMBARI/AMBARI_SERVER component; else do not include the Ambari service identity.
-      includeAmbariIdentity &= (serviceComponentFilter.get("AMBARI") != null) && serviceComponentFilter.get("AMBARI").contains("AMBARI_SERVER");
+      includeAmbariIdentity &= (serviceComponentFilter.get(RootService.AMBARI.name()) != null)
+        && serviceComponentFilter.get(RootService.AMBARI.name()).contains(RootComponent.AMBARI_SERVER.name());
 
       if((operationType != OperationType.DEFAULT)) {
         // Update the identity filter, if necessary
@@ -123,8 +126,7 @@ public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerber
     Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor, false, false);
 
     processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
-        configurations, kerberosConfigurations, includeAmbariIdentity, propertiesToIgnore,
-        hostFilter != null);
+        configurations, kerberosConfigurations, includeAmbariIdentity, propertiesToIgnore);
 
     kerberosHelper.applyStackAdvisorUpdates(cluster, services, configurations, kerberosConfigurations,
         propertiesToIgnore, propertiesToRemove, true);

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/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
new file mode 100644
index 0000000..17e484a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/ResolvedKerberosKeytab.java
@@ -0,0 +1,257 @@
+/*
+ * 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.Map;
+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.
+ */
+// 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;
+  private String ownerAccess = null;
+  private String groupName = null;
+  private String groupAccess = null;
+  private String file = null;
+  private Set<Pair<Long, Pair<String, String>>> mappedPrincipals = null;
+  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
+  ) {
+    this.ownerName = ownerName;
+    this.ownerAccess = ownerAccess;
+    this.groupName = groupName;
+    this.groupAccess = groupAccess;
+    this.file = file;
+    this.mappedPrincipals = mappedPrincipals;
+    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)
+   */
+  public String getFile() {
+    return file;
+  }
+
+  /**
+   * Sets the path to the keytab file
+   *
+   * @param file a String declaring this keytab's file path
+   * @see #getFile()
+   */
+  public void setFile(String file) {
+    this.file = file;
+  }
+
+  /**
+   * Gets the local username to set as the owner of the keytab file
+   *
+   * @return a String declaring the name of the user to own the keytab file
+   */
+  public String getOwnerName() {
+    return ownerName;
+  }
+
+  /**
+   * Sets the local username to set as the owner of the keytab file
+   *
+   * @param name a String declaring the name of the user to own the keytab file
+   */
+  public void setOwnerName(String name) {
+    this.ownerName = name;
+  }
+
+  /**
+   * Gets the access permissions that should be set on the keytab file related to the file's owner
+   *
+   * @return a String declaring the access permissions that should be set on the keytab file related
+   * to the file's owner
+   * @see #ownerAccess
+   */
+  public String getOwnerAccess() {
+    return ownerAccess;
+  }
+
+  /**
+   * Sets the access permissions that should be set on the keytab file related to the file's owner
+   *
+   * @param access a String declaring the access permissions that should be set on the keytab file
+   *               related to the file's owner
+   * @see #ownerAccess
+   */
+  public void setOwnerAccess(String access) {
+    this.ownerAccess = access;
+  }
+
+  /**
+   * Gets the local group name to set as the group owner of the keytab file
+   *
+   * @return a String declaring the name of the group to own the keytab file
+   */
+  public String getGroupName() {
+    return groupName;
+  }
+
+  /**
+   * Sets the local group name to set as the group owner of the keytab file
+   *
+   * @param name a String declaring the name of the group to own the keytab file
+   */
+  public void setGroupName(String name) {
+    this.groupName = name;
+  }
+
+  /**
+   * Gets the access permissions that should be set on the keytab file related to the file's group
+   *
+   * @return a String declaring the access permissions that should be set on the keytab file related
+   * to the file's group
+   * @see #groupAccess
+   */
+  public String getGroupAccess() {
+    return groupAccess;
+  }
+
+  /**
+   * Sets the access permissions that should be set on the keytab file related to the file's group
+   *
+   * @param access a String declaring the access permissions that should be set on the keytab file
+   *               related to the file's group
+   * @see #groupAccess
+   */
+  public void setGroupAccess(String access) {
+    this.groupAccess = access;
+  }
+
+  /**
+   * Gets evaluated host-to-principal set associated with given keytab.
+   *
+   * @return a Set with mappedPrincipals associated with given keytab
+   */
+  public Set<Pair<Long, Pair<String, String>>> getMappedPrincipals() {
+    return mappedPrincipals;
+  }
+
+  /**
+   * 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
+   */
+  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());
+      }
+    }
+    return builder.build();
+  }
+
+  /**
+   * Gets a set of principals associated with given keytab.
+   *
+   * @return a Set of principals
+   */
+  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());
+    }
+    return builder.build();
+  }
+
+  /**
+   * Indicates if given keytab is Ambari Server keytab and can be distributed to host with Ambari Server side action.
+   *
+   * @return true, if given keytab is Ambari Server keytab.
+   */
+  public boolean isAmbariServerKeytab() {
+    return isAmbariServerKeytab;
+  }
+
+  /**
+   * Sets flag to indicate if given keytab is Ambari Server keytab and can be distributed to host with Ambari Server
+   * side action.
+   *
+   * @param isAmbariServerKeytab flag value
+   */
+  public void setAmbariServerKeytab(boolean isAmbariServerKeytab) {
+    this.isAmbariServerKeytab = isAmbariServerKeytab;
+  }
+
+  /**
+   * Indicates if this keytab must be written to Ambari Server jaas file.
+   *
+   * @return true, if this keytab must be written to Ambari Server jaas file.
+   */
+  public boolean isMustWriteAmbariJaasFile() {
+    return mustWriteAmbariJaasFile;
+  }
+
+  /**
+   * Sets flag to indicate if this keytab must be written to Ambari Server jaas file.
+   *
+   * @param mustWriteAmbariJaasFile flag value
+   */
+  public void setMustWriteAmbariJaasFile(boolean mustWriteAmbariJaasFile) {
+    this.mustWriteAmbariJaasFile = mustWriteAmbariJaasFile;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java
new file mode 100644
index 0000000..ad031c8
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/CreateAndConfigureAction.java
@@ -0,0 +1,164 @@
+/*
+ * 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.upgrades;
+
+import java.util.Iterator;
+import java.util.Map;
+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.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.ConfigurationRequest;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.DesiredConfig;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.UpgradeContext;
+import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask;
+import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+
+/**
+ * The {@link CreateAndConfigureAction} is used to alter a configuration property during
+ * an upgrade. It also creates the config type if it does not exist as a desired config for the cluster.
+ * It will only produce a new configuration if an actual change is
+ * occuring. For some configure tasks, the value is already at the desired
+ * property or the conditions of the task are not met. In these cases, a new
+ * configuration will not be created. This task can perform any of the following
+ * actions in a single declaration:
+ * <ul>
+ * <li>Copy a configuration to a new property key, optionally setting a default
+ * if the original property did not exist</li>
+ * <li>Copy a configuration to a new property key from one configuration type to
+ * another, optionally setting a default if the original property did not exist</li>
+ * <li>Rename a configuration, optionally setting a default if the original
+ * property did not exist</li>
+ * <li>Delete a configuration property</li>
+ * <li>Set a configuration property</li>
+ * <li>Conditionally set a configuration property based on another configuration
+ * property value</li>
+ * </ul>
+ */
+public class CreateAndConfigureAction extends ConfigureAction {
+
+  private static final Logger LOG = LoggerFactory.getLogger(CreateAndConfigureAction.class);
+
+  /**
+   * Used to lookup the cluster.
+   */
+  @Inject
+  private Clusters m_clusters;
+
+  /**
+   * Used to update the configuration properties.
+   */
+  @Inject
+  private AmbariManagementController m_controller;
+
+  /**
+   * Used to assist in the creation of a {@link ConfigurationRequest} to update
+   * configuration values.
+   */
+  @Inject
+  private ConfigHelper m_configHelper;
+
+
+  @Override
+  public CommandReport execute(
+      ConcurrentMap<String, Object> requestSharedDataContext)
+      throws AmbariException, InterruptedException {
+
+    LOG.info("Create and Configure...");
+
+    Map<String,String> commandParameters = getCommandParameters();
+    if( null == commandParameters || commandParameters.isEmpty() ){
+      return createCommandReport(0, HostRoleStatus.FAILED, "{}", "",
+          "Unable to change configuration values without command parameters");
+    }
+
+    String clusterName = commandParameters.get("clusterName");
+    Cluster cluster = m_clusters.getCluster(clusterName);
+    UpgradeContext upgradeContext = getUpgradeContext(cluster);
+
+    Direction direction = upgradeContext.getDirection();
+    if (direction == Direction.DOWNGRADE) {
+      return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", "", "Skip changing configuration values for downgrade");
+    }
+
+    String configType = commandParameters.get(CreateAndConfigureTask.PARAMETER_CONFIG_TYPE);
+    String serviceName = cluster.getServiceByConfigType(configType).getName();
+
+    if (StringUtils.isBlank(serviceName)) {
+      serviceName = commandParameters.get(CreateAndConfigureTask.PARAMETER_ASSOCIATED_SERVICE);
+    }
+
+    RepositoryVersionEntity sourceRepoVersion = upgradeContext.getSourceRepositoryVersion(serviceName);
+    RepositoryVersionEntity targetRepoVersion = upgradeContext.getTargetRepositoryVersion(serviceName);
+    StackId sourceStackId = sourceRepoVersion.getStackId();
+    StackId targetStackId = targetRepoVersion.getStackId();
+
+    if (!sourceStackId.equals(targetStackId)){
+      return createCommandReport(0, HostRoleStatus.FAILED, "{}", "",
+          "Unable to change configuration values across stacks. Use regular config task type instead.");
+    }
+
+    Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();
+    DesiredConfig desiredConfig = desiredConfigs.get(configType);
+    if (desiredConfig == null) {
+      LOG.info(String.format("Could not find desired config type with name %s. Create it with default values.", configType));
+
+      // populate a map with default configurations from the new stack
+      Map<String, Map<String, String>> newServiceDefaultConfigsByType = m_configHelper.getDefaultProperties(
+          targetStackId, serviceName);
+
+      if (!newServiceDefaultConfigsByType.containsKey(configType)){
+        String error = String.format("%s in %s does not contain configuration type %s", serviceName, targetStackId.getStackId(), configType);
+        LOG.error(error);
+        return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", error);
+      }
+
+      Map<String, String> defaultConfigsForType = newServiceDefaultConfigsByType.get(configType);
+      // Remove any property for the new config type whose value is NULL
+      Iterator<Map.Entry<String, String>> iter = defaultConfigsForType.entrySet().iterator();
+      while (iter.hasNext()) {
+        Map.Entry<String, String> entry = iter.next();
+        if (entry.getValue() == null) {
+          iter.remove();
+        }
+      }
+
+      String serviceVersionNote = String.format("%s %s %s", direction.getText(true),
+          direction.getPreposition(), upgradeContext.getRepositoryVersion().getVersion());
+
+      m_configHelper.createConfigType(cluster, targetStackId,
+          m_controller,
+          configType, defaultConfigsForType,
+          m_controller.getAuthName(), serviceVersionNote);
+    }
+
+    return super.execute(requestSharedDataContext);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosAction.java
index 02cb70a..36b0491 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosAction.java
@@ -38,8 +38,14 @@ import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.orm.dao.HostDAO;
+import org.apache.ambari.server.orm.dao.KerberosKeytabDAO;
+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.RepositoryVersionEntity;
 import org.apache.ambari.server.serveraction.kerberos.PreconfigureServiceType;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosKeytab;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
@@ -56,6 +62,7 @@ import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
 import org.apache.ambari.server.state.kerberos.VariableReplacementHelper;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
@@ -82,6 +89,18 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
   @Inject
   private VariableReplacementHelper variableReplacementHelper;
 
+  @Inject
+  private HostDAO hostDAO;
+
+  @Inject
+  private KerberosKeytabDAO kerberosKeytabDAO;
+
+  @Inject
+  KerberosPrincipalHostDAO kerberosPrincipalHostDAO;
+
+  @Inject
+  KerberosPrincipalDAO kerberosPrincipalDAO;
+
   @Override
   public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws AmbariException, InterruptedException {
     Map<String, String> commandParameters = getCommandParameters();
@@ -131,7 +150,7 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
             }
           }
 
-          processServiceComponentHosts(cluster, kerberosDescriptor, configurations, kerberosConfigurations, propertiesToIgnore);
+          processServiceComponentHosts(cluster, kerberosDescriptor, configurations, kerberosConfigurations, propertiesToIgnore, getDefaultRealm(configurations));
 
           // Calculate the set of configurations to update and replace any variables
           // using the previously calculated Map of configurations for the host.
@@ -280,7 +299,7 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
   private void processServiceComponentHosts(Cluster cluster, KerberosDescriptor kerberosDescriptor,
                                             Map<String, Map<String, String>> currentConfigurations,
                                             Map<String, Map<String, String>> kerberosConfigurations,
-                                            Map<String, Set<String>> propertiesToBeIgnored)
+                                            Map<String, Set<String>> propertiesToBeIgnored, String realm)
       throws AmbariException {
 
     Collection<Host> hosts = cluster.getHosts();
@@ -292,7 +311,7 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
 
       try {
         Map<String, Set<String>> propertiesToIgnore = null;
-
+        HashMap<String, ResolvedKerberosKeytab> resolvedKeytabs = new HashMap<>();
         for (Host host : hosts) {
           // Iterate over the components installed on the current host to get the service and
           // component-level Kerberos descriptors in order to determine which principals,
@@ -323,7 +342,8 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
 
               // Add service-level principals (and keytabs)
               kerberosHelper.addIdentities(null, serviceIdentities,
-                  null, hostName, serviceName, componentName, kerberosConfigurations, currentConfigurations, false);
+                  null, hostName, host.getHostId(), serviceName, componentName, kerberosConfigurations, currentConfigurations,
+                  resolvedKeytabs, realm);
               propertiesToIgnore = gatherPropertiesToIgnore(serviceIdentities, propertiesToIgnore);
 
               KerberosComponentDescriptor componentDescriptor = serviceDescriptor.getComponent(componentName);
@@ -338,7 +358,8 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
 
                 // Add component-level principals (and keytabs)
                 kerberosHelper.addIdentities(null, componentIdentities,
-                    null, hostName, serviceName, componentName, kerberosConfigurations, currentConfigurations, false);
+                    null, hostName, host.getHostId(), serviceName, componentName, kerberosConfigurations, currentConfigurations,
+                    resolvedKeytabs,realm);
                 propertiesToIgnore = gatherPropertiesToIgnore(componentIdentities, propertiesToIgnore);
               }
             }
@@ -359,7 +380,8 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
 
             List<KerberosIdentityDescriptor> componentIdentities = Collections.singletonList(identity);
             kerberosHelper.addIdentities(null, componentIdentities,
-                null, KerberosHelper.AMBARI_SERVER_HOST_NAME, "AMBARI", componentName, kerberosConfigurations, currentConfigurations, false);
+                null, KerberosHelper.AMBARI_SERVER_HOST_NAME, ambariServerHostID(), "AMBARI", componentName, kerberosConfigurations, currentConfigurations,
+                resolvedKeytabs, realm);
             propertiesToIgnore = gatherPropertiesToIgnore(componentIdentities, propertiesToIgnore);
           }
         }
@@ -367,6 +389,11 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
         if ((propertiesToBeIgnored != null) && (propertiesToIgnore != null)) {
           propertiesToBeIgnored.putAll(propertiesToIgnore);
         }
+
+        // create database records for keytabs that must be presented on cluster
+        for (ResolvedKerberosKeytab keytab : resolvedKeytabs.values()) {
+          kerberosHelper.processResolvedKeytab(keytab);
+        }
       } catch (IOException e) {
         throw new AmbariException(e.getMessage(), e);
       }
@@ -582,5 +609,14 @@ public class PreconfigureKerberosAction extends AbstractUpgradeServerAction {
       }
     }
   }
+
+  protected Long ambariServerHostID(){
+    String ambariServerHostName = StageUtils.getHostName();
+    HostEntity ambariServerHostEntity = hostDAO.findByName(ambariServerHostName);
+    return (ambariServerHostEntity == null)
+        ? null
+        : ambariServerHostEntity.getHostId();
+  }
+
 }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
index 073fd82..eaddb2e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/RepoUtil.java
@@ -191,6 +191,7 @@ public class RepoUtil {
     re.setRepositoryId(repoInfo.getRepoId());
     re.setDistribution(repoInfo.getDistribution());
     re.setComponents(repoInfo.getComponents());
+    re.setTags(repoInfo.getTags());
     return re;
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
index f0d6c27..760d987 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceDirectory.java
@@ -93,6 +93,11 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
   protected File checksDir;
 
   /**
+   * server side action directory path
+   */
+  protected File serverActionsDir;
+
+  /**
    * service metainfo file object representation
    */
   private ServiceMetainfoXml metaInfoXml;
@@ -118,6 +123,11 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
   protected static final String CHECKS_FOLDER_NAME = "checks";
 
   /**
+   * Server actions directory name
+   */
+  protected static final String SERVER_ACTIONS_FOLDER_NAME = "server_actions";
+
+  /**
    * service metainfo file name
    */
   private static final String SERVICE_METAINFO_FILE_NAME = "metainfo.xml";
@@ -172,6 +182,15 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
   }
 
   /**
+   * Obtain the server side actions directory path.
+   *
+   * @return server side actions directory path
+   */
+  public File getServerActionsDir() {
+    return serverActionsDir;
+  }
+
+  /**
    * Obtain the metrics file.
    *
    * @return metrics file
@@ -303,6 +322,7 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
 	  calculatePackageDirectory(stack, service);
 	  calculateUpgradesDirectory(stack, service);
 	  calculateChecksDirectory(stack, service);
+	  calculateServerActionsDirectory(stack, service);
   }
 
   /**
@@ -378,6 +398,15 @@ public abstract class ServiceDirectory extends StackDefinitionDirectory {
   }
 
   /**
+   * Sets the serverActionsDir if the dir exists and is not empty
+   * @param stack
+   * @param service
+   */
+  protected void calculateServerActionsDirectory(String stack, String service) {
+    serverActionsDir = resolveDirectory(SERVER_ACTIONS_FOLDER_NAME, stack, service);
+  }
+
+  /**
    * Unmarshal the metainfo file into its object representation.
    *
    * @throws AmbariException if the metainfo file doesn't exist or

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
index ec4382e..ea992e1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/ServiceModule.java
@@ -150,6 +150,7 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
     serviceInfo.setServicePackageFolder(serviceDirectory.getPackageDir());
     serviceInfo.setServiceUpgradesFolder(serviceDirectory.getUpgradesDir());
     serviceInfo.setChecksFolder(serviceDirectory.getChecksDir());
+    serviceInfo.setServerActionsFolder(serviceDirectory.getServerActionsDir());
     serviceInfo.setAdvisorFile(serviceDirectory.getAdvisorFile());
     serviceInfo.setAdvisorName(serviceDirectory.getAdvisorName(serviceInfo.getName()));
 
@@ -270,6 +271,13 @@ public class ServiceModule extends BaseModule<ServiceModule, ServiceInfo> implem
     }
 
     /*
+     * Use parent's server actions if the current one does not have any.
+     */
+    if (serviceInfo.getServerActionsFolder() == null) {
+      serviceInfo.setServerActionsFolder(parent.getServerActionsFolder());
+    }
+
+    /*
      * If current stack version does not specify the credential store information
      * for the service, then use parent definition.
      */

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
index 9b740d0..fc49a24 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/StackManager.java
@@ -85,11 +85,6 @@ public class StackManager {
   public static final String COMMON_SERVICES = "common-services";
 
   /**
-   * Prefix used for common stack hooks parent path string
-   */
-  public static final String DEFAULT_HOOKS_FOLDER = "stack-hooks";
-
-  /**
    * Prefix used for extension services parent path string
    */
   public static final String EXTENSIONS = "extensions";

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
index e999e24..4008c21 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
@@ -1508,22 +1508,22 @@ public class ConfigHelper {
   }
 
   /**
-   * Compares values as double in case they are numbers.
-   * @param actualValue
-   * @param newValue
-   * @return
+   * Checks for equality of parsed numbers if both values are numeric,
+   * otherwise using regular equality.
    */
-  private  boolean valuesAreEqual(String actualValue, String newValue) {
-    boolean actualValueIsNumber = NumberUtils.isNumber(actualValue);
-    boolean newValueIsNumber = NumberUtils.isNumber(newValue);
-    if (actualValueIsNumber && newValueIsNumber) {
-      Double ab = Double.parseDouble(actualValue);
-      Double bb = Double.parseDouble(newValue);
-      return ab.equals(bb);
-    } else if (!actualValueIsNumber && !newValueIsNumber) {
-      return actualValue.equals(newValue);
+  static boolean valuesAreEqual(String value1, String value2) { // exposed for unit test
+    if (NumberUtils.isNumber(value1) && NumberUtils.isNumber(value2)) {
+      try {
+        Number number1 = NumberUtils.createNumber(value1);
+        Number number2 = NumberUtils.createNumber(value2);
+        return Objects.equal(number1, number2) ||
+          number1.doubleValue() == number2.doubleValue();
+      } catch (NumberFormatException e) {
+        // fall back to regular equality
+      }
     }
-    return false;
+
+    return Objects.equal(value1, value2);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryInfo.java
index 8ab1fe9..a7bbc1b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/RepositoryInfo.java
@@ -18,7 +18,11 @@
 
 package org.apache.ambari.server.state;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import org.apache.ambari.server.controller.RepositoryResponse;
+import org.apache.ambari.server.state.stack.RepoTag;
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
@@ -36,6 +40,7 @@ public class RepositoryInfo {
   private boolean repoSaved = false;
   private boolean unique = false;
   private boolean ambariManagedRepositories = true;
+  private Set<RepoTag> tags = new HashSet<>();
 
   /**
    * @return the baseUrl
@@ -207,7 +212,8 @@ public class RepositoryInfo {
   public RepositoryResponse convertToResponse()
   {
     return new RepositoryResponse(getBaseUrl(), getOsType(), getRepoId(),
-            getRepoName(), getDistribution(), getComponents(), getMirrorsList(), getDefaultBaseUrl());
+            getRepoName(), getDistribution(), getComponents(), getMirrorsList(), getDefaultBaseUrl(),
+            getTags());
   }
 
   /**
@@ -259,4 +265,19 @@ public class RepositoryInfo {
   public void setAmbariManagedRepositories(boolean ambariManagedRepositories) {
     this.ambariManagedRepositories = ambariManagedRepositories;
   }
+
+  /**
+   * @return the tags for this repository
+   */
+  public Set<RepoTag> getTags() {
+    return tags;
+  }
+
+  /**
+   * @param repoTags the tags for this repository
+   */
+  public void setTags(Set<RepoTag> repoTags) {
+    tags = repoTags;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
index c6b187a..9dc77c1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java
@@ -822,7 +822,7 @@ public class ServiceImpl implements Service {
     List<Component> result = new ArrayList<>();
     for (ServiceComponent component : getServiceComponents().values()) {
       for (ServiceComponentHost host : component.getServiceComponentHosts().values()) {
-        result.add(new Component(host.getHostName(), getName(), component.getName()));
+        result.add(new Component(host.getHostName(), getName(), component.getName(), host.getHost().getHostId()));
       }
     }
     return result;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
index 0e6e3c3..184be38 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceInfo.java
@@ -307,6 +307,12 @@ public class ServiceInfo implements Validable, Cloneable {
   @XmlTransient
   private File checksFolder;
 
+  /**
+   * Stores the path to the server actions folder which contains server actions jars for the given service.
+   */
+  @XmlTransient
+  private File serverActionsFolder;
+
   public boolean isDeleted() {
     return isDeleted;
   }
@@ -833,6 +839,14 @@ public class ServiceInfo implements Validable, Cloneable {
     this.checksFolder = checksFolder;
   }
 
+  public File getServerActionsFolder() {
+    return serverActionsFolder;
+  }
+
+  public void setServerActionsFolder(File serverActionsFolder) {
+    this.serverActionsFolder = serverActionsFolder;
+  }
+
   /**
    * Exposes (and initializes on first use) map of os-specific details.
    * @return  map of OS specific details keyed by family

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
index 025015a..4f50a01 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
@@ -37,8 +37,8 @@ import org.apache.ambari.server.ClusterNotFoundException;
 import org.apache.ambari.server.agent.ActionQueue;
 import org.apache.ambari.server.agent.AgentCommand.AgentCommandType;
 import org.apache.ambari.server.agent.AlertDefinitionCommand;
-import org.apache.ambari.server.controller.RootServiceResponseFactory.Components;
-import org.apache.ambari.server.controller.RootServiceResponseFactory.Services;
+import org.apache.ambari.server.controller.RootComponent;
+import org.apache.ambari.server.controller.RootService;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.state.Cluster;
@@ -375,8 +375,8 @@ public class AlertDefinitionHash {
       return affectedHosts;
     }
 
-    String ambariServiceName = Services.AMBARI.name();
-    String agentComponentName = Components.AMBARI_AGENT.name();
+    String ambariServiceName = RootService.AMBARI.name();
+    String agentComponentName = RootComponent.AMBARI_AGENT.name();
 
     // intercept host agent alerts; they affect all hosts
     if (ambariServiceName.equals(definitionServiceName)

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index e3b5d6e..0b38b36 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -60,7 +60,7 @@ import org.apache.ambari.server.controller.AmbariSessionManager;
 import org.apache.ambari.server.controller.ClusterResponse;
 import org.apache.ambari.server.controller.ConfigurationResponse;
 import org.apache.ambari.server.controller.MaintenanceStateHelper;
-import org.apache.ambari.server.controller.RootServiceResponseFactory.Services;
+import org.apache.ambari.server.controller.RootService;
 import org.apache.ambari.server.controller.ServiceComponentHostResponse;
 import org.apache.ambari.server.controller.ServiceConfigVersionResponse;
 import org.apache.ambari.server.events.AmbariEvent.AmbariEventType;
@@ -2568,7 +2568,7 @@ public class ClusterImpl implements Cluster {
       // server-side events either don't have a service name or are AMBARI;
       // either way they are not handled by this method since it expects a
       // real service and component
-      if (StringUtils.isBlank(serviceName) || Services.AMBARI.name().equals(serviceName)) {
+      if (StringUtils.isBlank(serviceName) || RootService.AMBARI.name().equals(serviceName)) {
         continue;
       }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ClusterVersionSummary.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ClusterVersionSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ClusterVersionSummary.java
index e9d9920..5486ecd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ClusterVersionSummary.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ClusterVersionSummary.java
@@ -24,11 +24,14 @@ import java.util.Set;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
 
+import com.google.gson.annotations.SerializedName;
+
 /**
  * For a version, collects summary information for a cluster.
  */
 public class ClusterVersionSummary {
 
+  @SerializedName("services")
   @JsonProperty("services")
   private Map<String, ServiceVersionSummary> m_services;
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ServiceVersionSummary.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ServiceVersionSummary.java b/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ServiceVersionSummary.java
index 29505c8..d87caef 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ServiceVersionSummary.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/repository/ServiceVersionSummary.java
@@ -20,25 +20,26 @@ package org.apache.ambari.server.state.repository;
 import org.codehaus.jackson.annotate.JsonIgnore;
 import org.codehaus.jackson.annotate.JsonProperty;
 
+import com.google.gson.annotations.SerializedName;
+
 /**
  * Used to hold information about Service's ability to upgrade for a repository version.
  */
 public class ServiceVersionSummary {
 
-  @JsonProperty("display_name")
-  private String m_displayName;
-
+  @SerializedName("version")
   @JsonProperty("version")
   private String m_version;
 
+  @SerializedName("release_version")
   @JsonProperty("release_version")
   private String m_releaseVersion;
 
+  @SerializedName("upgrade")
   @JsonProperty("upgrade")
   private boolean m_upgrade = false;
 
-  ServiceVersionSummary(String displayName) {
-    m_displayName = displayName;
+  ServiceVersionSummary() {
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/repository/VersionDefinitionXml.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/repository/VersionDefinitionXml.java b/ambari-server/src/main/java/org/apache/ambari/server/state/repository/VersionDefinitionXml.java
index 099215e..7680ee1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/repository/VersionDefinitionXml.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/repository/VersionDefinitionXml.java
@@ -314,7 +314,7 @@ public class VersionDefinitionXml {
         continue;
       }
 
-      ServiceVersionSummary summary = new ServiceVersionSummary(service.getDisplayName());
+      ServiceVersionSummary summary = new ServiceVersionSummary();
       summaries.put(service.getName(), summary);
 
       String serviceVersion = service.getDesiredRepositoryVersion().getVersion();
@@ -322,13 +322,26 @@ public class VersionDefinitionXml {
       // !!! currently only one version is supported (unique service names)
       ManifestService manifest = manifests.get(serviceName);
 
-      summary.setVersions(manifest.version, StringUtils.isEmpty(manifest.releaseVersion) ?
-          release.version : manifest.releaseVersion);
+      final String versionToCompare;
+      final String summaryReleaseVersion;
+      if (StringUtils.isEmpty(manifest.releaseVersion)) {
+        versionToCompare = release.getFullVersion();
+        summaryReleaseVersion = release.version;
+      } else {
+        versionToCompare = manifest.releaseVersion;
+        summaryReleaseVersion = manifest.releaseVersion;
+      }
+
+      summary.setVersions(manifest.version, summaryReleaseVersion);
 
-      // !!! installed service already meets the release version, then nothing to upgrade
-      // !!! TODO should this be using the release compatible-with field?
-      if (VersionUtils.compareVersions(summary.getReleaseVersion(), serviceVersion, 4) > 0) {
+      if (RepositoryType.STANDARD == release.repositoryType) {
         summary.setUpgrade(true);
+      } else {
+        // !!! installed service already meets the release version, then nothing to upgrade
+        // !!! TODO should this be using the release compatible-with field?
+        if (VersionUtils.compareVersionsWithBuild(versionToCompare, serviceVersion, 4) > 0) {
+          summary.setUpgrade(true);
+        }
       }
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/services/AmbariServerAlertService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/services/AmbariServerAlertService.java b/ambari-server/src/main/java/org/apache/ambari/server/state/services/AmbariServerAlertService.java
index d3237a9..305f693 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/services/AmbariServerAlertService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/services/AmbariServerAlertService.java
@@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.ambari.server.AmbariService;
 import org.apache.ambari.server.alerts.AlertRunnable;
-import org.apache.ambari.server.controller.RootServiceResponseFactory.Components;
+import org.apache.ambari.server.controller.RootComponent;
 import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.state.Cluster;
@@ -133,7 +133,7 @@ public class AmbariServerAlertService extends AbstractScheduledService {
   /**
    * {@inheritDoc}
    * <p/>
-   * Compares all known {@link Components#AMBARI_SERVER} alerts with those that
+   * Compares all known {@link RootComponent#AMBARI_SERVER} alerts with those that
    * are scheduled. If any are not scheduled or have their intervals changed,
    * then reschedule those.
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/services/MetricsRetrievalService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/services/MetricsRetrievalService.java b/ambari-server/src/main/java/org/apache/ambari/server/state/services/MetricsRetrievalService.java
index 510e706..7e5aad1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/services/MetricsRetrievalService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/services/MetricsRetrievalService.java
@@ -101,7 +101,7 @@ public class MetricsRetrievalService extends AbstractService {
   /**
    * Logger.
    */
-  protected final static Logger LOG = LoggerFactory.getLogger(MetricsRetrievalService.class);
+  private static final Logger LOG = LoggerFactory.getLogger(MetricsRetrievalService.class);
 
   /**
    * The timeout for exceptions which are caught and then cached to prevent log

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/stack/OsFamily.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/OsFamily.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/OsFamily.java
index c0483e8..b84de01 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/OsFamily.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/OsFamily.java
@@ -53,7 +53,7 @@ public class OsFamily {
     private final String OS_VERSION = "versions";
     private final String LOAD_CONFIG_MSG = "Could not load OS family definition from %s file";
     private final String FILE_NAME = "os_family.json";
-    private final Logger LOG = LoggerFactory.getLogger(OsFamily.class);
+    private static final Logger LOG = LoggerFactory.getLogger(OsFamily.class);
 
     private Map<String, JsonOsFamilyEntry> osMap = null;
     private JsonOsFamilyRoot jsonOsFamily = null;

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepoTag.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepoTag.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepoTag.java
new file mode 100644
index 0000000..08a2635
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepoTag.java
@@ -0,0 +1,34 @@
+/*
+ * 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.state.stack;
+
+import javax.xml.bind.annotation.XmlEnum;
+
+/**
+ * A Repo tag is a way to allow Ambari to place logic surrounding repository details.
+ * This is used instead of random strings to tightly control how tags are used.
+ */
+@XmlEnum
+public enum RepoTag {
+
+  /**
+   * The repository may contain GPL-Licensed software
+   */
+  GPL
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepositoryXml.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepositoryXml.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepositoryXml.java
index c2209bb..ccb25e8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepositoryXml.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/RepositoryXml.java
@@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlTransient;
 
@@ -150,6 +151,10 @@ public class RepositoryXml implements Validable{
     private String components = null;
     private boolean unique = false;
 
+    @XmlElementWrapper(name="tags")
+    @XmlElement(name="tag")
+    private Set<RepoTag> tags = new HashSet<>();
+
     private Repo() {
     }
 
@@ -201,6 +206,13 @@ public class RepositoryXml implements Validable{
     public void setUnique(boolean unique) {
       this.unique = unique;
     }
+
+    /**
+     * @return the repo tags
+     */
+    public Set<RepoTag> getTags() {
+      return tags;
+    }
   }
 
   /**
@@ -224,6 +236,7 @@ public class RepositoryXml implements Validable{
           ri.setDistribution(r.getDistribution());
           ri.setComponents(r.getComponents());
           ri.setUnique(r.isUnique());
+          ri.setTags(r.tags);
 
           repos.add(ri);
         }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
index 7c32f37..7fb04f5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/UpgradePack.java
@@ -38,6 +38,7 @@ import javax.xml.bind.annotation.XmlValue;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.state.stack.upgrade.ClusterGrouping;
 import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
+import org.apache.ambari.server.state.stack.upgrade.CreateAndConfigureTask;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
 import org.apache.ambari.server.state.stack.upgrade.Grouping;
 import org.apache.ambari.server.state.stack.upgrade.ServiceCheckGrouping;
@@ -600,6 +601,8 @@ public class UpgradePack {
       for (Task task : tasks) {
         if (Task.Type.CONFIGURE == task.getType()) {
           ((ConfigureTask) task).associatedService = service;
+        } else if (Task.Type.CREATE_AND_CONFIGURE == task.getType()) {
+          ((CreateAndConfigureTask) task).associatedService = service;
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e83bf1bd/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
index 5bc3d8f..c9219d3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
@@ -135,6 +135,8 @@ public class ClusterGrouping extends Grouping {
     void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
       if (task.getType().equals(Task.Type.CONFIGURE) && StringUtils.isNotEmpty(service)) {
         ((ConfigureTask) task).associatedService = service;
+      } else if (task.getType().equals(Task.Type.CREATE_AND_CONFIGURE) && StringUtils.isNotEmpty(service)) {
+        ((CreateAndConfigureTask) task).associatedService = service;
       }
     }