AMBARI-10576. Add the ability to obtain details about required Kerberos 
identities (rlevas)


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

Branch: refs/heads/trunk
Commit: 66e42cbab8a714eac49b4ae84eb238d0c08accb5
Parents: 1e3afa8
Author: Robert Levas <rle...@hortonworks.com>
Authored: Wed Apr 29 13:01:52 2015 -0400
Committer: Robert Levas <rle...@hortonworks.com>
Committed: Wed Apr 29 13:02:01 2015 -0400

----------------------------------------------------------------------
 .../query/render/ClusterBlueprintRenderer.java  |   2 +-
 .../render/HostKerberosIdentityCsvRenderer.java |  83 +++
 .../api/resources/BaseResourceDefinition.java   |  28 +-
 .../HostComponentResourceDefinition.java        |   2 +-
 .../HostKerberosIdentityResourceDefinition.java |  56 ++
 .../api/resources/HostResourceDefinition.java   |   1 +
 .../resources/ResourceInstanceFactoryImpl.java  |   4 +
 .../ambari/server/api/services/BaseService.java |   6 +
 .../server/api/services/ClusterService.java     |  16 +
 .../services/HostKerberosIdentityService.java   | 121 ++++
 .../ambari/server/api/services/HostService.java |  11 +
 .../api/services/ResultPostProcessorImpl.java   |   4 +-
 .../api/services/serializers/CsvSerializer.java | 224 +++++++
 .../services/serializers/JsonSerializer.java    |   4 +-
 .../apache/ambari/server/api/util/TreeNode.java |  12 +-
 .../ambari/server/api/util/TreeNodeImpl.java    |  14 +-
 .../server/controller/ControllerModule.java     |   2 +
 .../server/controller/KerberosHelper.java       | 167 +++++
 .../controller/ResourceProviderFactory.java     |   3 +
 .../AbstractControllerResourceProvider.java     |   2 +
 .../HostKerberosIdentityResourceProvider.java   | 243 +++++++
 .../ambari/server/controller/spi/Resource.java  |   4 +-
 .../kerberos/KerberosIdentityDescriptor.java    |  13 +
 .../kerberos/KerberosKeytabDescriptor.java      |  21 +
 .../kerberos/KerberosPrincipalDescriptor.java   |  58 +-
 .../render/ClusterBlueprintRendererTest.java    |   4 +-
 .../api/query/render/MinimalRendererTest.java   |   4 +-
 .../resources/BaseResourceDefinitionTest.java   |   4 +-
 ...tKerberosIdentityResourceDefinitionTest.java |  48 ++
 .../resources/HostResourceDefinitionTest.java   |   3 +-
 .../ResourceInstanceFactoryImplTest.java        |  10 +
 .../HostKerberosIdentityServiceTest.java        |  93 +++
 .../services/serializers/CsvSerializerTest.java | 258 ++++++++
 .../server/controller/KerberosHelperTest.java   | 631 +++++++++++++++++++
 ...ostKerberosIdentityResourceProviderTest.java | 362 +++++++++++
 35 files changed, 2482 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
index 5c84d4c..351f6b4 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRenderer.java
@@ -264,7 +264,7 @@ public class ClusterBlueprintRenderer extends BaseRenderer 
implements Renderer {
    * @return true if the node represents a collection; false otherwise
    */
   private boolean isCollection(TreeNode<Resource> node) {
-    String isCollection = node.getProperty("isCollection");
+    String isCollection = node.getStringProperty("isCollection");
     return isCollection != null && isCollection.equals("true");
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostKerberosIdentityCsvRenderer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostKerberosIdentityCsvRenderer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostKerberosIdentityCsvRenderer.java
new file mode 100644
index 0000000..31e95e1
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/query/render/HostKerberosIdentityCsvRenderer.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.query.render;
+
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.serializers.CsvSerializer;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Renderer which updates a KerberosHostIdentity resource so it may be 
serialized using a CSV serializer.
+ * <p/>
+ * This implementation extends the DefaultRenderer to add the header mapping 
and column order information
+ * to the root of TreeNode structure.
+ *
+ * @see CsvSerializer
+ */
+public class HostKerberosIdentityCsvRenderer extends DefaultRenderer {
+
+  @Override
+  public Result finalizeResult(Result queryResult) {
+    TreeNode<Resource> resultTree = queryResult.getResultTree();
+
+    if(resultTree != null) {
+      // TODO: Determine which columns/fields are relevant for the query and 
prune as needed.
+      Map<String, String> columnMap = new HashMap<String, String>() {{
+        put("KerberosIdentity/host_name", "host");
+        put("KerberosIdentity/description", "description");
+        put("KerberosIdentity/principal_name", "principal name");
+        put("KerberosIdentity/principal_type", "principal type");
+        put("KerberosIdentity/principal_local_username", "local username");
+        put("KerberosIdentity/keytab_file_path", "keytab file path");
+        put("KerberosIdentity/keytab_file_owner", "keytab file owner");
+        put("KerberosIdentity/keytab_file_owner_access", "keytab file owner 
access");
+        put("KerberosIdentity/keytab_file_group", "keytab file group");
+        put("KerberosIdentity/keytab_file_group_access", "keytab file group 
access");
+        put("KerberosIdentity/keytab_file_mode", "keytab file mode");
+        put("KerberosIdentity/keytab_file_installed", "keytab file installed");
+      }};
+
+      List<String> columnOrder = new ArrayList<String>() {{
+        add("KerberosIdentity/host_name");
+        add("KerberosIdentity/description");
+        add("KerberosIdentity/principal_name");
+        add("KerberosIdentity/principal_type");
+        add("KerberosIdentity/principal_local_username");
+        add("KerberosIdentity/keytab_file_path");
+        add("KerberosIdentity/keytab_file_owner");
+        add("KerberosIdentity/keytab_file_owner_access");
+        add("KerberosIdentity/keytab_file_group");
+        add("KerberosIdentity/keytab_file_group_access");
+        add("KerberosIdentity/keytab_file_mode");
+        add("KerberosIdentity/keytab_file_installed");
+      }};
+
+      resultTree.setProperty(CsvSerializer.PROPERTY_COLUMN_MAP, columnMap);
+      resultTree.setProperty(CsvSerializer.PROPERTY_COLUMN_ORDER, columnOrder);
+    }
+
+    return queryResult;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
index 02342a8..e63f7eb 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
@@ -28,6 +28,8 @@ import 
org.apache.ambari.server.controller.spi.ClusterController;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.Schema;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.net.URLCodec;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -180,13 +182,33 @@ public abstract class BaseResourceDefinition implements 
ResourceDefinition {
         }
 
         Schema schema = getClusterController().getSchema(r.getType());
-        Object id     = 
r.getPropertyValue(schema.getKeyPropertyId(r.getType()));
+        Object id = r.getPropertyValue(schema.getKeyPropertyId(r.getType()));
 
-        href = parent.getProperty("isCollection").equals("true") ?
-            href + id : href + parent.getName() + '/' + id;
+        String hrefIdPart = urlencode(id);
+
+        href = parent.getStringProperty("isCollection").equals("true") ?
+            href + hrefIdPart : href + parent.getName() + '/' + hrefIdPart;
       }
       resultNode.setProperty("href", href);
     }
+
+    /**
+     * URL encodes the id (string) value
+     *
+     * @param id the id to URL encode
+     * @return null if id is null, else the URL encoded value of the id
+     */
+    protected String urlencode(Object id) {
+      if (id == null)
+        return "";
+      else {
+        try {
+          return new URLCodec().encode(id.toString());
+        } catch (EncoderException e) {
+          return id.toString();
+        }
+      }
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
index 6dc9e2d..33d4c46 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
@@ -106,7 +106,7 @@ public class HostComponentResourceDefinition extends 
BaseResourceDefinition {
       //todo: look at partial request fields to ensure that hosts should be 
returned
       if (request.getResource().getResourceDefinition().getType() == 
getType()) {
         // only add host if query host_resource was directly queried
-        String nodeHref = resultNode.getProperty("href");
+        String nodeHref = resultNode.getStringProperty("href");
         
resultNode.getObject().setProperty(PropertyHelper.getPropertyId("host", "href"),
             nodeHref.substring(0, nodeHref.indexOf("/host_components/")));
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinition.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinition.java
new file mode 100644
index 0000000..65cf1de
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinition.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.ambari.server.api.resources;
+
+import 
org.apache.ambari.server.api.query.render.HostKerberosIdentityCsvRenderer;
+import org.apache.ambari.server.api.query.render.Renderer;
+import org.apache.ambari.server.controller.spi.Resource;
+
+/**
+ * HostKerberosIdentity resource definition.
+ */
+public class HostKerberosIdentityResourceDefinition extends 
BaseResourceDefinition {
+
+  /**
+   * Constructor.
+   */
+  public HostKerberosIdentityResourceDefinition() {
+    super(Resource.Type.HostKerberosIdentity);
+  }
+
+  @Override
+  public String getPluralName() {
+    return "kerberos_identities";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "kerberos_identity";
+  }
+
+  @Override
+  public Renderer getRenderer(String name) {
+    if ("csv".equalsIgnoreCase(name)) {
+      return new HostKerberosIdentityCsvRenderer();
+    } else {
+      return super.getRenderer(name);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java
index 380e751..c9b0878 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java
@@ -52,6 +52,7 @@ public class HostResourceDefinition extends 
BaseResourceDefinition {
     subs.add(new SubResourceDefinition(Resource.Type.HostComponent));
     subs.add(new SubResourceDefinition(Resource.Type.Alert));
     subs.add(new SubResourceDefinition(Resource.Type.HostStackVersion));
+    subs.add(new SubResourceDefinition(Resource.Type.HostKerberosIdentity));
     return subs;
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
index 776f1f4..1e219ff 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
@@ -384,6 +384,10 @@ public class ResourceInstanceFactoryImpl implements 
ResourceInstanceFactory {
         resourceDefinition = new ActiveWidgetLayoutResourceDefinition();
         break;
 
+      case HostKerberosIdentity:
+        resourceDefinition = new HostKerberosIdentityResourceDefinition();
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + 
type);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
index 3afc23d..1016ed7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java
@@ -24,6 +24,7 @@ import 
org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
 import org.apache.ambari.server.api.services.parsers.BodyParseException;
 import org.apache.ambari.server.api.services.parsers.JsonRequestBodyParser;
 import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.CsvSerializer;
 import org.apache.ambari.server.api.services.serializers.JsonSerializer;
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 import org.apache.ambari.server.controller.spi.Resource;
@@ -41,6 +42,7 @@ import java.util.Set;
  * Provides common functionality to all services.
  */
 public abstract class BaseService {
+  public final static MediaType MEDIA_TYPE_TEXT_CSV_TYPE = new 
MediaType("text", "csv");
 
   /**
    * Factory for creating resource instances.
@@ -175,6 +177,10 @@ public abstract class BaseService {
         }
       };
     }
+    else if (mediaType.equals(MEDIA_TYPE_TEXT_CSV_TYPE)) {
+      return new CsvSerializer();
+    }
+
     throw new IllegalArgumentException("The media type " + mediaType + " is 
not supported.");
   }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
index be40bc4..7bb0a72 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterService.java
@@ -419,6 +419,22 @@ public class ClusterService extends BaseService {
   }
 
   /**
+   * Get the host Kerberos identity resource without specifying the parent 
host component.
+   * Allows accessing host Kerberos identity resources across hosts.
+   *
+   * @param request      the request
+   * @param clusterName  the cluster name
+   *
+   * @return  the host component service with no parent set
+   */
+  @Path("{clusterName}/kerberos_identities")
+  public HostKerberosIdentityService getHostKerberosIdentityHandler(@Context 
javax.ws.rs.core.Request request, @PathParam("clusterName") String clusterName) 
{
+
+    hasPermission(Request.Type.valueOf(request.getMethod()), clusterName);
+    return new HostKerberosIdentityService(clusterName, null);
+  }
+
+  /**
    * Get the component resource without specifying the parent service.
    * Allows accessing component resources across services.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostKerberosIdentityService.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostKerberosIdentityService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostKerberosIdentityService.java
new file mode 100644
index 0000000..624a335
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostKerberosIdentityService.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services;
+
+import org.apache.ambari.server.api.resources.ResourceInstance;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Service responsible for kerberos identity resource requests.
+ */
+public class HostKerberosIdentityService extends BaseService {
+
+  /**
+   * Parent cluster id.
+   */
+  private String clusterName;
+
+  /**
+   * Relevant hostname
+   */
+  private String hostName;
+
+  /**
+   * Constructor.
+   *
+   * @param clusterName cluster name
+   * @param hostName    host name
+   */
+  public HostKerberosIdentityService(String clusterName, String hostName) {
+    this.clusterName = clusterName;
+    this.hostName = hostName;
+  }
+
+  /**
+   * Handles GET: 
/clusters/{clusterID}/services/{serviceID}/components/{componentID}/kerberos_identities/{identityId}
+   * Get a specific Kerberos identity.
+   *
+   * @param headers    http headers
+   * @param ui         uri info
+   * @param identityID Kerberos identity id
+   * @param format     output format
+   * @return a component resource representation
+   */
+  @GET
+  @Path("{kerberosIdentityID}")
+  @Produces("text/plain")
+  public Response getKerberosIdentity(String body, @Context HttpHeaders 
headers, @Context UriInfo ui,
+                                      @PathParam("kerberosIdentityID") String 
identityID,
+                                      @QueryParam("format") String format) {
+
+    MediaType mediaType;
+    if ("csv".equalsIgnoreCase(format)) {
+      mediaType = MEDIA_TYPE_TEXT_CSV_TYPE;
+    } else {
+      mediaType = null;
+    }
+
+    return handleRequest(headers, body, ui, Request.Type.GET, mediaType, 
createResource(clusterName, hostName, identityID));
+  }
+
+  /**
+   * Handles GET: 
/clusters/{clusterID}/services/{serviceID}/components/{componentID}/kerberos_identities
+   * Get all Kerberos identities for a service.
+   *
+   * @param headers http headers
+   * @param ui      uri info
+   * @return component collection resource representation
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getKerberosIdentities(String body, @Context HttpHeaders 
headers, @Context UriInfo ui, @QueryParam("format") String format) {
+    return getKerberosIdentity(body, headers, ui, null, format);
+  }
+
+  /**
+   * Create a kerberos identity resource instance.
+   *
+   * @param clusterName cluster name
+   * @param hostName    host name
+   * @param identityId  Kerberos identity id
+   * @return a component resource instance
+   */
+  ResourceInstance createResource(String clusterName, String hostName, String 
identityId) {
+    Map<Resource.Type, String> mapIds = new HashMap<Resource.Type, String>();
+    mapIds.put(Resource.Type.Cluster, clusterName);
+    mapIds.put(Resource.Type.Host, hostName);
+    mapIds.put(Resource.Type.HostKerberosIdentity, identityId);
+
+    return createResource(Resource.Type.HostKerberosIdentity, mapIds);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java
index aaf3007..c54d30f 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java
@@ -207,6 +207,17 @@ public class HostService extends BaseService {
   }
 
   /**
+   * Get the kerberos_identities sub-resource.
+   *
+   * @param hostName host id
+   * @return the host_components service
+   */
+  @Path("{hostName}/kerberos_identities")
+  public HostKerberosIdentityService 
getHostKerberosIdentityHandler(@PathParam("hostName") String hostName) {
+    return new HostKerberosIdentityService(m_clusterName, hostName);
+  }
+
+  /**
    * Get the alerts sub-resource.
    *
    * @param hostName host id

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
index 61afee2..4d469d7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ResultPostProcessorImpl.java
@@ -89,7 +89,7 @@ public class ResultPostProcessorImpl implements 
ResultPostProcessor {
       for (ResourceDefinition.PostProcessor processor : listProcessors) {
         processor.process(m_request, node, href);
       }
-      href = node.getProperty("href");
+      href = node.getStringProperty("href");
       int i = href.indexOf('?');
       if (i != -1) {
         try {
@@ -99,7 +99,7 @@ public class ResultPostProcessorImpl implements 
ResultPostProcessor {
         }
       }
     } else {
-      String isItemsCollection = node.getProperty("isCollection");
+      String isItemsCollection = node.getStringProperty("isCollection");
       if (node.getName() == null && "true".equals(isItemsCollection)) {
         node.setName("items");
         node.setProperty("href", href);

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/CsvSerializer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/CsvSerializer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/CsvSerializer.java
new file mode 100644
index 0000000..87751dc
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/CsvSerializer.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.services.serializers;
+
+import org.apache.ambari.server.api.services.Result;
+import org.apache.ambari.server.api.services.ResultStatus;
+import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * CSV serializer used to generate a CSV-formatted document from a result.
+ */
+public class CsvSerializer implements ResultSerializer {
+  /**
+   * Property name for the CsvSerializer-specific column map where the value 
of this property
+   * contains a map of resource property names to header descriptive names.
+   * <p/>
+   * If not specified, no header record will be serialized.
+   */
+  public static final String PROPERTY_COLUMN_MAP = "csv_column_map";
+
+  /**
+   * Property name for the CsvSerializer-specific column order where the value 
of this property
+   * contains a list of resource property names in the order to export.
+   * <p/>
+   * If not specified, the order will be taken from key order in the 
csv_column_map property (if
+   * available) or from the "natural" order of the properties in the resource.
+   */
+  public static final String PROPERTY_COLUMN_ORDER = "csv_column_order";
+
+  /**
+   * Serialize the result into a CSV-formatted text document.
+   * <p/>
+   * It is expected that the result set is a collection of flat resources - no 
sub-resources will be
+   * included in the output.  The root of the tree structure may have a column 
map (csv_column_map)
+   * and a column order (csv_column_order) property set to indicate the header 
record and ordering
+   * of the columns.
+   * <p/>
+   * The csv_column_map is a map of resource property names to header 
descriptive names.  If not
+   * specified, a header record will not be serialized.
+   * <p/>
+   * The csv_column_order is a list of resource property names declaring the 
order of the columns.
+   * If not specified, the order will be taken from the key order of 
csv_column_map or the "natural"
+   * ordering of the resource property names, both may be unpredictable.
+   *
+   * @param result internal result
+   * @return a String containing the CSV-formatted document
+   */
+  @Override
+  public Object serialize(Result result) {
+    if (result.getStatus().isErrorState()) {
+      return serializeError(result.getStatus());
+    } else {
+
+      try {
+        // A StringBuffer to store the CSV-formatted document while building 
it.  It may be
+        // necessary to use file-based storage if the data set is expected to 
be really large.
+        StringBuffer buffer = new StringBuffer();
+
+        TreeNode<Resource> root = result.getResultTree();
+
+        if (root != null) {
+          CSVPrinter csvPrinter = new CSVPrinter(buffer, CSVFormat.DEFAULT);
+
+          // TODO: recursively handle tree structure, for now only handle 
single level of detail
+          if ("true".equalsIgnoreCase(root.getStringProperty("isCollection"))) 
{
+            List<String> fieldNameOrder = processHeader(csvPrinter, root);
+
+            Collection<TreeNode<Resource>> children = root.getChildren();
+            if (children != null) {
+              // Iterate over the child nodes of the collection an add each as 
a new record in the
+              // CSV document.
+              for (TreeNode<Resource> child : children) {
+                processRecord(csvPrinter, child, fieldNameOrder);
+              }
+            }
+          }
+        }
+
+        return buffer.toString();
+      } catch (IOException e) {
+        //todo: exception handling.  Create ResultStatus 500 and call 
serializeError
+        throw new RuntimeException("Unable to serialize to csv: " + e, e);
+      }
+    }
+  }
+
+  @Override
+  public Object serializeError(ResultStatus error) {
+    try {
+      StringBuffer buffer = new StringBuffer();
+      CSVPrinter csvPrinter = new CSVPrinter(buffer, CSVFormat.DEFAULT);
+
+      csvPrinter.printRecord(Arrays.asList("status", "message"));
+      csvPrinter.printRecord(Arrays.asList(error.getStatus().getStatus(), 
error.getMessage()));
+
+      return buffer.toString();
+    } catch (IOException e) {
+      //todo: exception handling.  Create ResultStatus 500 and call 
serializeError
+      throw new RuntimeException("Unable to serialize to csv: " + e, e);
+    }
+  }
+
+  /**
+   * Generate a CSV record by processing the resource embedded in the 
specified node.  The order of
+   * the fields are to be set as specified.
+   *
+   * @param csvPrinter     the CSVPrinter used to create the record
+   * @param node           the relevant node in the collection
+   * @param fieldNameOrder a list of field names indicating order
+   * @throws IOException if an error occurs creating the CSV record
+   */
+  private void processRecord(CSVPrinter csvPrinter, TreeNode<Resource> node, 
List<String> fieldNameOrder)
+      throws IOException {
+
+    if (node != null) {
+      Resource recordResource = node.getObject();
+      if (recordResource != null) {
+        List<Object> values = new ArrayList<Object>();
+
+        if (fieldNameOrder != null) {
+          for (String fieldName : fieldNameOrder) {
+            values.add(recordResource.getPropertyValue(fieldName));
+          }
+        } else {
+          Map<String, Map<String, Object>> properties = 
recordResource.getPropertiesMap();
+          if (properties != null) {
+
+            for (Map.Entry<String, Map<String, Object>> outer : 
properties.entrySet()) {
+              Map<String, Object> innerProperties = outer.getValue();
+
+              if (innerProperties != null) {
+                for (Map.Entry<String, Object> inner : 
innerProperties.entrySet()) {
+                  values.add(inner.getValue());
+                }
+              }
+            }
+          }
+        }
+
+        if (!values.isEmpty()) {
+          csvPrinter.printRecord(values);
+        }
+      }
+    }
+  }
+
+  /**
+   * Optionally generate the CSV header record and establish the field order 
by processing the
+   * csv_column_map and csv_column_order node properties.
+   *
+   * @param csvPrinter the CSVPrinter used to create the record
+   * @param node       a node containing header and ordering information
+   * @return a list indicating the field order for the CSV records
+   * @throws IOException if an error occurs creating the CSV header
+   */
+  private List<String> processHeader(CSVPrinter csvPrinter, TreeNode<Resource> 
node) throws IOException {
+    Map<String, String> header;
+    List<String> fieldNameOrder;
+    Object object;
+
+    // Get the explicitly set header property for the current tree node. This 
may be null if no
+    // header needs to be written out. The header map is expected to be a map 
of field names to
+    // descriptive header values.
+    object = node.getProperty(PROPERTY_COLUMN_MAP);
+    if (object instanceof Map) {
+      header = (Map<String, String>) object;
+    } else {
+      header = null;
+    }
+
+    // Determine the field name order.  If explicitly set, use it, else grab 
it from the header map
+    // (if available).
+    object = node.getProperty(PROPERTY_COLUMN_ORDER);
+    if (object instanceof List) {
+      // Use the explicitly set ordering
+      fieldNameOrder = (List<String>) object;
+    } else if (header != null) {
+      // Use the ordering specified by the map.
+      fieldNameOrder = new ArrayList<String>(header.keySet());
+    } else {
+      fieldNameOrder = null;
+    }
+
+    if (header != null) {
+      // build the header record
+      List<String> headerNames = new ArrayList<String>();
+      for (String fieldName : fieldNameOrder) {
+        headerNames.add(header.get(fieldName));
+      }
+
+      // write out the header...
+      csvPrinter.printRecord(headerNames);
+    }
+
+    return fieldNameOrder;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
index 7f57f7f..c17f162 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/services/serializers/JsonSerializer.java
@@ -214,14 +214,14 @@ public class JsonSerializer implements ResultSerializer {
   }
 
   private void writeHref(TreeNode<Resource> node) throws IOException {
-    String hrefProp = node.getProperty("href");
+    String hrefProp = node.getStringProperty("href");
     if (hrefProp != null) {
       m_generator.writeStringField("href", hrefProp);
     }
   }
 
   private void writeItemCount(TreeNode<Resource> node) throws IOException {
-    String countProp = node.getProperty("count");
+    String countProp = node.getStringProperty("count");
     if (countProp != null) {
       m_generator.writeStringField("itemTotal", countProp);
       // Write once

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java 
b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
index 796d64f..60d365f 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNode.java
@@ -98,7 +98,7 @@ public interface TreeNode<T> {
    * @param name  the name of the property
    * @param value the value of the property
    */
-  public void setProperty(String name, String value);
+  public void setProperty(String name, Object value);
 
   /**
    * Get the specified node property.
@@ -106,7 +106,15 @@ public interface TreeNode<T> {
    * @param name property name
    * @return the requested property value or null
    */
-  public String getProperty(String name);
+  public Object getProperty(String name);
+
+  /**
+   * Get the specified node property as a String.
+   *
+   * @param name property name
+   * @return the requested property value (as a String) or null
+   */
+  public String getStringProperty(String name);
 
   /**
    * Remove a property from the node.

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
index 1739b88..c498ad7 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/api/util/TreeNodeImpl.java
@@ -50,7 +50,7 @@ public class TreeNodeImpl<T> implements TreeNode<T> {
   /**
    * properties
    */
-  private Map<String, String> m_mapNodeProps;
+  private Map<String, Object> m_mapNodeProps;
 
   /**
    * Constructor.
@@ -117,19 +117,25 @@ public class TreeNodeImpl<T> implements TreeNode<T> {
   }
 
   @Override
-  public void setProperty(String name, String value) {
+  public void setProperty(String name, Object value) {
     if (m_mapNodeProps == null) {
-      m_mapNodeProps = new LinkedHashMap<String, String>();
+      m_mapNodeProps = new LinkedHashMap<String, Object>();
     }
     m_mapNodeProps.put(name, value);
   }
 
   @Override
-  public String getProperty(String name) {
+  public Object getProperty(String name) {
     return m_mapNodeProps == null ? null : m_mapNodeProps.get(name);
   }
 
   @Override
+  public String getStringProperty(String name) {
+    Object value = getProperty(name);
+    return value == null ? null : value.toString();
+  }
+
+  @Override
   public void removeProperty(String name) {
     if (m_mapNodeProps != null) {
       m_mapNodeProps.remove(name);

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index d6da1eb..e2f6ccb 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -61,6 +61,7 @@ import 
org.apache.ambari.server.configuration.Configuration.ConnectionPoolType;
 import org.apache.ambari.server.configuration.Configuration.DatabaseType;
 import org.apache.ambari.server.controller.internal.ComponentResourceProvider;
 import 
org.apache.ambari.server.controller.internal.HostComponentResourceProvider;
+import 
org.apache.ambari.server.controller.internal.HostKerberosIdentityResourceProvider;
 import org.apache.ambari.server.controller.internal.HostResourceProvider;
 import org.apache.ambari.server.controller.internal.MemberResourceProvider;
 import 
org.apache.ambari.server.controller.internal.RepositoryVersionResourceProvider;
@@ -389,6 +390,7 @@ public class ControllerModule extends AbstractModule {
         .implement(ResourceProvider.class, Names.named("component"), 
ComponentResourceProvider.class)
         .implement(ResourceProvider.class, Names.named("member"), 
MemberResourceProvider.class)
         .implement(ResourceProvider.class, Names.named("repositoryVersion"), 
RepositoryVersionResourceProvider.class)
+        .implement(ResourceProvider.class, 
Names.named("hostKerberosIdentity"), HostKerberosIdentityResourceProvider.class)
         .build(ResourceProviderFactory.class));
 
     install(new FactoryModuleBuilder().implement(

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
index bdf94a5..b131c4c 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
@@ -1999,6 +1999,173 @@ public class KerberosHelper {
   }
 
   /**
+   * Returns the active identities for the named cluster.  Results are 
filtered by host, service,
+   * and/or component; and grouped by host.
+   * <p/>
+   * The cluster name is mandatory; however the active identities may be 
filtered by one or more of
+   * host, service, or component. A <code>null</code> value for any of these 
filters indicates no
+   * filter for that parameter.
+   * <p/>
+   * The return values are grouped by host and optionally <code>_HOST</code> 
in principals will be
+   * replaced with the relevant hostname if specified to do so.
+   *
+   * @param clusterName      the name of the relevant cluster (mandatory)
+   * @param hostName         the name of a host for which to find results, 
null indicates all hosts
+   * @param serviceName      the name of a service for which to find results, 
null indicates all
+   *                         services
+   * @param componentName    the name of a component for which to find 
results, null indicates all
+   *                         components
+   * @param replaceHostNames if true, _HOST in principals will be replace with 
the relevant host
+   *                         name
+   * @return a map of host names to kerberos identities
+   * @throws AmbariException if an error occurs processing the cluster's 
active identities
+   */
+  public Map<String, Collection<KerberosIdentityDescriptor>> 
getActiveIdentities(String clusterName,
+                                                                               
  String hostName,
+                                                                               
  String serviceName,
+                                                                               
  String componentName,
+                                                                               
  boolean replaceHostNames)
+      throws AmbariException {
+
+    if ((clusterName == null) || clusterName.isEmpty()) {
+      throw new IllegalArgumentException("Invalid argument, cluster name is 
required");
+    }
+
+    Cluster cluster = clusters.getCluster(clusterName);
+
+    if (cluster == null) {
+      throw new AmbariException(String.format("The cluster object for the 
cluster name %s is not available", clusterName));
+    }
+
+    Map<String, Collection<KerberosIdentityDescriptor>> activeIdentities = new 
HashMap<String, Collection<KerberosIdentityDescriptor>>();
+
+    if (isClusterKerberosEnabled(cluster)) {
+      Collection<String> hosts;
+
+      if (hostName == null) {
+        Map<String, Host> hostMap = clusters.getHostsForCluster(clusterName);
+        if (hostMap == null) {
+          hosts = null;
+        } else {
+          hosts = hostMap.keySet();
+        }
+      } else {
+        hosts = Collections.singleton(hostName);
+      }
+
+      if ((hosts != null) && !hosts.isEmpty()) {
+        KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster);
+
+        if (kerberosDescriptor != null) {
+          Map<String, String> kerberosDescriptorProperties = 
kerberosDescriptor.getProperties();
+
+          for (String hostname : hosts) {
+            Map<String, KerberosIdentityDescriptor> hostActiveIdentities = new 
HashMap<String, KerberosIdentityDescriptor>();
+            List<KerberosIdentityDescriptor> identities = 
getActiveIdentities(cluster, hostname, serviceName, componentName, 
kerberosDescriptor);
+
+            if (!identities.isEmpty()) {
+              // Calculate the current host-specific configurations. These 
will be used to replace
+              // variables within the Kerberos descriptor data
+              Map<String, Map<String, String>> configurations = 
calculateConfigurations(cluster, hostname, kerberosDescriptorProperties);
+
+              for (KerberosIdentityDescriptor identity : identities) {
+                KerberosPrincipalDescriptor principalDescriptor = 
identity.getPrincipalDescriptor();
+                String principal = null;
+
+                if (principalDescriptor != null) {
+                  principal = 
KerberosDescriptor.replaceVariables(principalDescriptor.getValue(), 
configurations);
+                }
+
+                if (principal != null) {
+                  if (replaceHostNames) {
+                    principal = principal.replace("_HOST", hostname);
+                  }
+
+                  if (!hostActiveIdentities.containsKey(principal)) {
+                    KerberosPrincipalDescriptor resolvedPrincipalDescriptor =
+                        new KerberosPrincipalDescriptor(principal,
+                            principalDescriptor.getType(),
+                            
KerberosDescriptor.replaceVariables(principalDescriptor.getConfiguration(), 
configurations),
+                            
KerberosDescriptor.replaceVariables(principalDescriptor.getLocalUsername(), 
configurations));
+
+                    KerberosKeytabDescriptor resolvedKeytabDescriptor;
+
+                    KerberosKeytabDescriptor keytabDescriptor = 
identity.getKeytabDescriptor();
+                    if (keytabDescriptor == null) {
+                      resolvedKeytabDescriptor = null;
+                    } else {
+                      resolvedKeytabDescriptor =
+                          new KerberosKeytabDescriptor(
+                              
KerberosDescriptor.replaceVariables(keytabDescriptor.getFile(), configurations),
+                              
KerberosDescriptor.replaceVariables(keytabDescriptor.getOwnerName(), 
configurations),
+                              
KerberosDescriptor.replaceVariables(keytabDescriptor.getOwnerAccess(), 
configurations),
+                              
KerberosDescriptor.replaceVariables(keytabDescriptor.getGroupName(), 
configurations),
+                              
KerberosDescriptor.replaceVariables(keytabDescriptor.getGroupAccess(), 
configurations),
+                              
KerberosDescriptor.replaceVariables(keytabDescriptor.getConfiguration(), 
configurations),
+                              keytabDescriptor.isCachable());
+                    }
+
+                    hostActiveIdentities.put(principal, new 
KerberosIdentityDescriptor(
+                        identity.getName(),
+                        resolvedPrincipalDescriptor,
+                        resolvedKeytabDescriptor));
+                  }
+                }
+              }
+            }
+
+            activeIdentities.put(hostname, hostActiveIdentities.values());
+          }
+        }
+      }
+    }
+
+    return activeIdentities;
+  }
+
+  private List<KerberosIdentityDescriptor> getActiveIdentities(Cluster cluster,
+                                                               String hostname,
+                                                               String 
serviceName,
+                                                               String 
componentName,
+                                                               
KerberosDescriptor kerberosDescriptor)
+      throws AmbariException {
+
+    List<KerberosIdentityDescriptor> identities = new 
ArrayList<KerberosIdentityDescriptor>();
+
+    List<ServiceComponentHost> serviceComponentHosts = 
cluster.getServiceComponentHosts(hostname);
+
+    if(serviceComponentHosts != null) {
+      for (ServiceComponentHost serviceComponentHost : serviceComponentHosts) {
+        String schServiceName = serviceComponentHost.getServiceName();
+        String schComponentName = 
serviceComponentHost.getServiceComponentName();
+
+        if (((serviceName == null) || serviceName.equals(schServiceName)) &&
+            ((componentName == null) || 
componentName.equals(schComponentName))) {
+
+          KerberosServiceDescriptor serviceDescriptor = 
kerberosDescriptor.getService(schServiceName);
+
+          if (serviceDescriptor != null) {
+            List<KerberosIdentityDescriptor> serviceIdentities = 
serviceDescriptor.getIdentities(true);
+            if (serviceIdentities != null) {
+              identities.addAll(serviceIdentities);
+            }
+
+            KerberosComponentDescriptor componentDescriptor = 
serviceDescriptor.getComponent(schComponentName);
+            if (componentDescriptor != null) {
+              List<KerberosIdentityDescriptor> componentIdentities = 
componentDescriptor.getIdentities(true);
+              if (componentIdentities != null) {
+                identities.addAll(componentIdentities);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return identities;
+  }
+
+  /**
    * A enumeration of the supported custom operations
    */
   public static enum SupportedCustomOperation {

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/controller/ResourceProviderFactory.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/ResourceProviderFactory.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/ResourceProviderFactory.java
index f7eb2d9..5d1143a 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/ResourceProviderFactory.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/ResourceProviderFactory.java
@@ -53,6 +53,9 @@ public interface ResourceProviderFactory {
       Map<Type, String> keyPropertyIds,
       AmbariManagementController managementController);
 
+  @Named("hostKerberosIdentity")
+  ResourceProvider 
getHostKerberosIdentityResourceProvider(AmbariManagementController 
managementController);
+
   @Named("repositoryVersion")
   ResourceProvider getRepositoryVersionResourceProvider();
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
index 210227e..9163656 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractControllerResourceProvider.java
@@ -167,6 +167,8 @@ public abstract class AbstractControllerResourceProvider 
extends AbstractResourc
         return new WidgetLayoutResourceProvider(managementController);
       case Widget:
         return new WidgetResourceProvider(managementController);
+      case HostKerberosIdentity:
+        return 
resourceProviderFactory.getHostKerberosIdentityResourceProvider(managementController);
 
       default:
         throw new IllegalArgumentException("Unknown type " + type);

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
new file mode 100644
index 0000000..c76ae6c
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
@@ -0,0 +1,243 @@
+/*
+ * 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.controller.internal;
+
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
+import org.apache.ambari.server.controller.spi.NoSuchResourceException;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+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.state.kerberos.KerberosIdentityDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
+
+import java.text.DecimalFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Read-only resource provider for Kerberos identity resources.
+ */
+public class HostKerberosIdentityResourceProvider extends 
ReadOnlyResourceProvider {
+
+  protected static final String KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID = 
"KerberosIdentity/cluster_name";
+  protected static final String KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID = 
"KerberosIdentity/host_name";
+  protected static final String KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID = 
"KerberosIdentity/description";
+  protected static final String KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID = 
"KerberosIdentity/principal_name";
+  protected static final String KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID = 
"KerberosIdentity/principal_type";
+  protected static final String 
KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID = 
"KerberosIdentity/principal_local_username";
+  protected static final String KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID 
= "KerberosIdentity/keytab_file_path";
+  protected static final String 
KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID = 
"KerberosIdentity/keytab_file_owner";
+  protected static final String 
KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID = 
"KerberosIdentity/keytab_file_owner_access";
+  protected static final String 
KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID = 
"KerberosIdentity/keytab_file_group";
+  protected static final String 
KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID = 
"KerberosIdentity/keytab_file_group_access";
+  protected static final String KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID 
= "KerberosIdentity/keytab_file_mode";
+  protected static final String 
KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID = 
"KerberosIdentity/keytab_file_installed";
+
+  protected static final Map<Resource.Type, String> PK_PROPERTY_MAP = 
Collections.unmodifiableMap(
+      new HashMap<Resource.Type, String>() {{
+        put(Resource.Type.Cluster, KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID);
+        put(Resource.Type.Host, KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID);
+        put(Resource.Type.HostKerberosIdentity, 
KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID);
+      }}
+  );
+
+  protected static final Set<String> PK_PROPERTY_IDS = 
Collections.unmodifiableSet(
+      new HashSet<String>(PK_PROPERTY_MAP.values())
+  );
+
+  protected static final Set<String> PROPERTY_IDS = 
Collections.unmodifiableSet(
+      new HashSet<String>() {{
+        add(KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID);
+        add(KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID);
+      }}
+  );
+
+  @Inject
+  private KerberosHelper kerberosHelper;
+
+  /**
+   * KerberosPrincipalHostDAO used to get Kerberos principal details
+   */
+  @Inject
+  private KerberosPrincipalHostDAO kerberosPrincipalHostDAO;
+
+  /**
+   * KerberosPrincipalDAO used to get Kerberos principal details
+   */
+  @Inject
+  private KerberosPrincipalDAO kerberosPrincipalDAO;
+
+  /**
+   * HostDAO used to translate host names to host ids
+   */
+  @Inject
+  private HostDAO hostDAO;
+
+  /**
+   * Create a  new resource provider for the given management controller.
+   *
+   * @param managementController the management controller
+   */
+  @AssistedInject
+  HostKerberosIdentityResourceProvider(@Assisted AmbariManagementController 
managementController) {
+    super(PROPERTY_IDS, PK_PROPERTY_MAP, managementController);
+  }
+
+
+  @Override
+  public Set<Resource> getResources(Request request, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, 
NoSuchResourceException, NoSuchParentResourceException {
+    return getResources(new GetResourcesCommand(getPropertyMaps(predicate), 
getRequestPropertyIds(request, predicate)));
+  }
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return PK_PROPERTY_IDS;
+  }
+
+  /**
+   * Inner class to implement the "get resource" command.
+   */
+  private class GetResourcesCommand implements Command<Set<Resource>> {
+    private final Set<Map<String, Object>> propertyMaps;
+    private final Set<String> requestPropertyIds;
+
+    public GetResourcesCommand(Set<Map<String, Object>> propertyMaps, 
Set<String> requestPropertyIds) {
+      this.propertyMaps = propertyMaps;
+      this.requestPropertyIds = requestPropertyIds;
+    }
+
+    @Override
+    public Set<Resource> invoke() throws AmbariException {
+      Set<Resource> resources = new HashSet<Resource>();
+
+      for (Map<String, Object> propertyMap : propertyMaps) {
+        String clusterName = (String) 
propertyMap.get(KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID);
+        String hostName = (String) 
propertyMap.get(KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID);
+
+        // Retrieve the active identities for the cluster filtered and grouped 
by hostname
+        Map<String, Collection<KerberosIdentityDescriptor>> hostDescriptors =
+            kerberosHelper.getActiveIdentities(clusterName, hostName, null, 
null, true);
+
+        if (hostDescriptors != null) {
+          for (Map.Entry<String, Collection<KerberosIdentityDescriptor>> entry 
: hostDescriptors.entrySet()) {
+            Collection<KerberosIdentityDescriptor> descriptors = 
entry.getValue();
+
+            if (descriptors != null) {
+              String currentHostName = entry.getKey();
+              HostEntity host = hostDAO.findByName(currentHostName);
+              Long hostId = (host == null) ? null : host.getHostId();
+
+              for (KerberosIdentityDescriptor descriptor : descriptors) {
+                KerberosPrincipalDescriptor principalDescriptor = 
descriptor.getPrincipalDescriptor();
+                if (principalDescriptor != null) {
+                  String principal = principalDescriptor.getValue();
+
+                  if ((principal != null) && !principal.isEmpty()) {
+                    Resource resource = new 
ResourceImpl(Resource.Type.HostKerberosIdentity);
+
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID, clusterName, requestPropertyIds);
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID, currentHostName, requestPropertyIds);
+
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID, principal, requestPropertyIds);
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID, principalDescriptor.getType(), 
requestPropertyIds);
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID, 
principalDescriptor.getLocalUsername(), requestPropertyIds);
+
+                    String installedStatus;
+                    if ((hostId != null) && 
kerberosPrincipalDAO.exists(principal)) {
+                      if (kerberosPrincipalHostDAO.exists(principal, hostId)) {
+                        installedStatus = "true";
+                      } else {
+                        installedStatus = "false";
+                      }
+                    } else {
+                      installedStatus = "unknown";
+                    }
+
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID, installedStatus, 
requestPropertyIds);
+
+                    KerberosKeytabDescriptor keytabDescriptor = 
descriptor.getKeytabDescriptor();
+                    if (keytabDescriptor != null) {
+                      String ownerAccess = keytabDescriptor.getOwnerAccess();
+                      String groupAccess = keytabDescriptor.getGroupAccess();
+                      int mode = 0;
+
+                      // Create the file access mode using *nix chmod values.
+                      if ("rw".equals(ownerAccess)) {
+                        mode += 600;
+                      } else if ("r".equals(ownerAccess)) {
+                        mode += 400;
+                      }
+
+                      if ("rw".equals(groupAccess)) {
+                        mode += 60;
+                      } else if ("r".equals(groupAccess)) {
+                        mode += 40;
+                      }
+
+                      setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID, keytabDescriptor.getFile(), 
requestPropertyIds);
+                      setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID, 
keytabDescriptor.getOwnerName(), requestPropertyIds);
+                      setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID, ownerAccess, 
requestPropertyIds);
+                      setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID, 
keytabDescriptor.getGroupName(), requestPropertyIds);
+                      setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID, groupAccess, 
requestPropertyIds);
+                      setResourceProperty(resource, 
KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID, new 
DecimalFormat("000").format(mode), requestPropertyIds);
+                    }
+
+                    setResourceProperty(resource, 
KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID, descriptor.getName(), 
requestPropertyIds);
+
+                    resources.add(resource);
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+
+      return resources;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
index 30bac9e..1b208fb 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
@@ -143,7 +143,8 @@ public interface Resource {
     Widget,
     WidgetLayout,
     ActiveWidgetLayout,
-    Theme;
+    Theme,
+    HostKerberosIdentity;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -248,6 +249,7 @@ public interface Resource {
     public static final Type Widget = InternalType.Widget.getType();
     public static final Type WidgetLayout = 
InternalType.WidgetLayout.getType();
     public static final Type ActiveWidgetLayout = 
InternalType.ActiveWidgetLayout.getType();
+    public static final Type HostKerberosIdentity = 
InternalType.HostKerberosIdentity.getType();
 
     /**
      * The type name.

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
index 2e5a27d..d31dd21 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java
@@ -87,6 +87,19 @@ public class KerberosIdentityDescriptor extends 
AbstractKerberosDescriptor {
 
   /**
    * Creates a new KerberosIdentityDescriptor
+   *
+   * @param name the name of this identity descriptor
+   * @param principal a KerberosPrincipalDescriptor
+   * @param keytab a KerberosKeytabDescriptor
+   */
+  public KerberosIdentityDescriptor(String name, KerberosPrincipalDescriptor 
principal, KerberosKeytabDescriptor keytab) {
+    setName(name);
+    setPrincipalDescriptor(principal);
+    setKeytabDescriptor(keytab);
+  }
+
+  /**
+   * Creates a new KerberosIdentityDescriptor
    * <p/>
    * See {@link 
org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor} for the JSON
    * Schema that may be used to generate this map.

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosKeytabDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosKeytabDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosKeytabDescriptor.java
index 79537d4..404efa2 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosKeytabDescriptor.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosKeytabDescriptor.java
@@ -156,6 +156,27 @@ public class KerberosKeytabDescriptor extends 
AbstractKerberosDescriptor {
 
   /**
    * Creates a new KerberosKeytabDescriptor
+   *
+   * @param file the path to the keytab file
+   * @param ownerName the local username of the file owner
+   * @param ownerAccess the file access privileges for the file owner ("r", 
"rw", "")
+   * @param groupName the local group name with privileges to access the file
+   * @param groupAccess the file access privileges for the group ("r", "rw", 
"")
+   * @param configuration the configuration used to store the principal name
+   * @param cachable true if the keytab may be cached by Ambari; otherwise 
false
+   */
+  public KerberosKeytabDescriptor(String file, String ownerName, String 
ownerAccess, String groupName,
+                                  String groupAccess, String configuration, 
boolean cachable) {
+    setName(file);
+    setOwnerName(ownerName);
+    setOwnerAccess(ownerAccess);
+    setGroupName(groupName);
+    setGroupAccess(groupAccess);
+    setConfiguration(configuration);
+    setCachable(cachable);
+  }
+  /**
+   * Creates a new KerberosKeytabDescriptor
    * <p/>
    * See {@link 
org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor} for the JSON
    * Schema that may be used to generate this map.

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosPrincipalDescriptor.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosPrincipalDescriptor.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosPrincipalDescriptor.java
index 2c0c90a..6e5ac5c 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosPrincipalDescriptor.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosPrincipalDescriptor.java
@@ -73,7 +73,7 @@ public class KerberosPrincipalDescriptor extends 
AbstractKerberosDescriptor {
    * <p/>
    * Expecting either "service" or "user"
    */
-  private KerberosPrincipalType type;
+  private KerberosPrincipalType type = KerberosPrincipalType.SERVICE;
 
   /**
    * A string declaring configuration type and property name indicating the 
property to be updated
@@ -87,7 +87,7 @@ public class KerberosPrincipalDescriptor extends 
AbstractKerberosDescriptor {
    * <p/>
    * Example: hdfs-site/dfs.namenode.kerberos.principal
    */
-  private String configuration;
+  private String configuration = null;
 
   /**
    * a String indicating the local username related to this principal, or null 
of no local mapping is
@@ -95,7 +95,24 @@ public class KerberosPrincipalDescriptor extends 
AbstractKerberosDescriptor {
    * <p/>
    * This value may be using in generating auth_to_local configuration 
settings.
    */
-  private String localUsername;
+  private String localUsername = null;
+
+  /**
+   * Creates a new KerberosPrincipalDescriptor
+   *
+   * @param principal the principal name
+   * @param type the principal type (user, service, etc...)
+   * @param configuration the configuration used to store the principal name
+   * @param localUsername the local username to map to the principal
+   */
+  public KerberosPrincipalDescriptor(String principal, KerberosPrincipalType 
type, String configuration, String localUsername) {
+    // The name for this KerberosPrincipalDescriptor is stored in the "value" 
entry in the map
+    // This is not automatically set by the super classes.
+    setName(principal);
+    setType((type == null) ? KerberosPrincipalType.SERVICE : type);
+    setConfiguration(configuration);
+    setLocalUsername(localUsername);
+  }
 
   /**
    * Creates a new KerberosPrincipalDescriptor
@@ -107,16 +124,11 @@ public class KerberosPrincipalDescriptor extends 
AbstractKerberosDescriptor {
    * @see org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor
    */
   public KerberosPrincipalDescriptor(Map<?, ?> data) {
-    // The name for this KerberosPrincipalDescriptor is stored in the "value" 
entry in the map
-    // This is not automatically set by the super classes.
-    setName(getStringValue(data, "value"));
-
-    String type = getStringValue(data, "type");
-    setType((type == null) ? KerberosPrincipalType.SERVICE : 
KerberosPrincipalType.valueOf(type.toUpperCase()));
-
-    setConfiguration(getStringValue(data, "configuration"));
-
-    setLocalUsername(getStringValue(data, "local_username"));
+    this(getStringValue(data, "value"),
+        getKerberosPrincipalTypeValue(data, "type"),
+        getStringValue(data, "configuration"),
+        getStringValue(data, "local_username")
+    );
   }
 
   /**
@@ -299,4 +311,24 @@ public class KerberosPrincipalDescriptor extends 
AbstractKerberosDescriptor {
       return false;
     }
   }
+
+  /**
+   * Translates a string value representing a principal type to a 
KerberosPrincipalType.
+   * <p/>
+   * If no value is supplied for the key or a translation cannot be made then 
KerberosPrincipalType.SERVICE
+   * is assumed.
+   *
+   * @param map a Map containing the relevant data
+   * @param key a String declaring the item to retrieve
+   * @return a KerberosPrincipalType
+   * @throws IllegalArgumentException if the principal type value is not one 
of the expected types.
+   */
+  private static KerberosPrincipalType getKerberosPrincipalTypeValue(Map<?, ?> 
map, String key) {
+    String type = getStringValue(map, key);
+    if ((type == null) || type.isEmpty()) {
+      return KerberosPrincipalType.SERVICE;
+    } else {
+      return KerberosPrincipalType.valueOf(type.toUpperCase());
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRendererTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRendererTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRendererTest.java
index d33adcd..96abb8c 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRendererTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/ClusterBlueprintRendererTest.java
@@ -193,7 +193,7 @@ public class ClusterBlueprintRendererTest {
     Result blueprintResult = renderer.finalizeResult(result);
 
     TreeNode<Resource> blueprintTree = blueprintResult.getResultTree();
-    assertNull(blueprintTree.getProperty("isCollection"));
+    assertNull(blueprintTree.getStringProperty("isCollection"));
     assertEquals(1, blueprintTree.getChildren().size());
 
     TreeNode<Resource> blueprintNode = 
blueprintTree.getChildren().iterator().next();
@@ -272,7 +272,7 @@ public class ClusterBlueprintRendererTest {
     Result blueprintResult = renderer.finalizeResult(result);
 
     TreeNode<Resource> blueprintTree = blueprintResult.getResultTree();
-    assertNull(blueprintTree.getProperty("isCollection"));
+    assertNull(blueprintTree.getStringProperty("isCollection"));
     assertEquals(1, blueprintTree.getChildren().size());
 
     TreeNode<Resource> blueprintNode = 
blueprintTree.getChildren().iterator().next();

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
index 37bf33c..9b9b614 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/api/query/render/MinimalRendererTest.java
@@ -348,7 +348,7 @@ public class MinimalRendererTest {
     renderer.finalizeProperties(createPropertyTree(), false);
 
     TreeNode<Resource> resultTree = 
renderer.finalizeResult(result).getResultTree();
-    assertNull(resultTree.getProperty("isCollection"));
+    assertNull(resultTree.getStringProperty("isCollection"));
     assertEquals(1, resultTree.getChildren().size());
 
     TreeNode<Resource> clusterNode = 
resultTree.getChildren().iterator().next();
@@ -414,7 +414,7 @@ public class MinimalRendererTest {
     renderer.finalizeProperties(createPropertyTreeWithSubProps(), false);
 
     TreeNode<Resource> resultTree = 
renderer.finalizeResult(result).getResultTree();
-    assertNull(resultTree.getProperty("isCollection"));
+    assertNull(resultTree.getStringProperty("isCollection"));
     assertEquals(1, resultTree.getChildren().size());
 
     TreeNode<Resource> clusterNode = 
resultTree.getChildren().iterator().next();

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BaseResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BaseResourceDefinitionTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BaseResourceDefinitionTest.java
index 3f64d9a..f174cb6 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BaseResourceDefinitionTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/BaseResourceDefinitionTest.java
@@ -106,7 +106,7 @@ public class BaseResourceDefinitionTest {
     
     processor.process(null, serviceNode, 
"http://c6401.ambari.apache.org:8080/api/v1/clusters/c1/services";);
 
-    String href = serviceNode.getProperty("href");
+    String href = serviceNode.getStringProperty("href");
 
     
Assert.assertEquals("http://c6401.ambari.apache.org:8080/api/v1/clusters/c1/services/Service1";,
 href);
 
@@ -121,7 +121,7 @@ public class BaseResourceDefinitionTest {
 
     processor.process(null, configGroupNode, 
"http://c6401.ambari.apache.org:8080/api/v1/clusters/c1/config_groups";);
 
-    href = configGroupNode.getProperty("href");
+    href = configGroupNode.getStringProperty("href");
 
     
Assert.assertEquals("http://c6401.ambari.apache.org:8080/api/v1/clusters/c1/config_groups/2";,
 href);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinitionTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinitionTest.java
new file mode 100644
index 0000000..603e00e
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostKerberosIdentityResourceDefinitionTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.api.resources;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Set;
+
+/**
+ * HostKerberosIdentityResourceDefinition tests.
+ */
+public class HostKerberosIdentityResourceDefinitionTest {
+  @Test
+  public void testGetPluralName() throws Exception {
+    final HostKerberosIdentityResourceDefinition resourceDefinition = new 
HostKerberosIdentityResourceDefinition();
+    Assert.assertEquals("kerberos_identities", 
resourceDefinition.getPluralName());
+  }
+
+  @Test
+  public void testGetSingularName() throws Exception {
+    final HostKerberosIdentityResourceDefinition resourceDefinition = new 
HostKerberosIdentityResourceDefinition();
+    Assert.assertEquals("kerberos_identity", 
resourceDefinition.getSingularName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() throws Exception {
+    final HostKerberosIdentityResourceDefinition resourceDefinition = new 
HostKerberosIdentityResourceDefinition();
+    final Set<SubResourceDefinition> subResourceDefinitions = 
resourceDefinition.getSubResourceDefinitions ();
+    Assert.assertEquals(0, subResourceDefinitions.size());
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostResourceDefinitionTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostResourceDefinitionTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostResourceDefinitionTest.java
index b3851eb..3d3fe11 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostResourceDefinitionTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/HostResourceDefinitionTest.java
@@ -46,10 +46,11 @@ public class HostResourceDefinitionTest {
     final ResourceDefinition resource = new HostResourceDefinition();
     Set<SubResourceDefinition> subResources = 
resource.getSubResourceDefinitions();
 
-    assertEquals(3, subResources.size());
+    assertEquals(4, subResources.size());
     assertTrue(includesType(subResources, Resource.Type.HostComponent));
     assertTrue(includesType(subResources, Resource.Type.Alert));
     assertTrue(includesType(subResources, Resource.Type.HostStackVersion));
+    assertTrue(includesType(subResources, Resource.Type.HostKerberosIdentity));
   }
 
   private boolean includesType(Set<SubResourceDefinition> resources, 
Resource.Type type) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/66e42cba/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
index 0daffce..2daa273 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImplTest.java
@@ -47,4 +47,14 @@ public class ResourceInstanceFactoryImplTest {
     assertEquals("artifacts", resourceDefinition.getPluralName());
     assertEquals(Resource.Type.Artifact, resourceDefinition.getType());
   }
+
+  @Test
+  public void testGetHostKerberosIdentityDefinition() {
+    ResourceDefinition resourceDefinition = 
ResourceInstanceFactoryImpl.getResourceDefinition(
+        Resource.Type.HostKerberosIdentity, null);
+
+    assertEquals("kerberos_identity", resourceDefinition.getSingularName());
+    assertEquals("kerberos_identities", resourceDefinition.getPluralName());
+    assertEquals(Resource.Type.HostKerberosIdentity, 
resourceDefinition.getType());
+  }
 }

Reply via email to