You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by sm...@apache.org on 2017/02/24 16:42:34 UTC

ambari git commit: AMBARI-20034. USER to GROUP mapping (hdfs_user -> hadoop_group) should be stack driven (Madhuvanthi Radhakrishnan via smohanty)

Repository: ambari
Updated Branches:
  refs/heads/branch-2.5 7702c4714 -> b856c5f37


AMBARI-20034. USER to GROUP mapping (hdfs_user -> hadoop_group) should be stack driven (Madhuvanthi Radhakrishnan via smohanty)


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

Branch: refs/heads/branch-2.5
Commit: b856c5f37c0573d4231b1976ad1523425947e2f8
Parents: 7702c47
Author: Sumit Mohanty <sm...@hortonworks.com>
Authored: Fri Feb 24 08:42:18 2017 -0800
Committer: Sumit Mohanty <sm...@hortonworks.com>
Committed: Fri Feb 24 08:42:18 2017 -0800

----------------------------------------------------------------------
 .../ambari/server/agent/ExecutionCommand.java   |   1 +
 .../AmbariCustomCommandExecutionHelper.java     |   7 +
 .../AmbariManagementControllerImpl.java         |   7 +
 .../internal/ClientConfigResourceProvider.java  |   7 +
 .../ambari/server/state/ConfigHelper.java       | 168 +++++++++++++++++++
 .../ambari/server/state/PropertyInfo.java       |   4 +
 .../apache/ambari/server/state/ServiceInfo.java |   5 +
 .../ambari/server/state/UserGroupInfo.java      |  50 ++++++
 .../server/state/ValueAttributesInfo.java       |  15 ++
 .../src/main/resources/configuration-schema.xsd |  17 ++
 .../2.0.6/hooks/before-ANY/scripts/params.py    |   9 +-
 .../server/api/services/AmbariMetaInfoTest.java |   2 +-
 .../AmbariCustomCommandExecutionHelperTest.java |  66 +++++++-
 .../AmbariManagementControllerTest.java         |  23 ++-
 .../ClientConfigResourceProviderTest.java       |  54 +++++-
 .../ambari/server/stack/StackManagerTest.java   |   2 +-
 .../python/stacks/2.0.6/configs/default.json    |   1 +
 .../services/HDFS/configuration/hadoop-env.xml  |  53 ++++++
 18 files changed, 468 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java
index 5c4f08e..c80ebe6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/ExecutionCommand.java
@@ -429,6 +429,7 @@ public class ExecutionCommand extends AgentCommand {
     String COMPONENT_CATEGORY = "component_category";
     String USER_LIST = "user_list";
     String GROUP_LIST = "group_list";
+    String USER_GROUPS = "user_groups";
     String NOT_MANAGED_HDFS_PATH_LIST = "not_managed_hdfs_path_list";
     String VERSION = "version";
     String REFRESH_TOPOLOGY = "refresh_topology";

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
index b601893..867ebff 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelper.java
@@ -43,6 +43,7 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT_TY
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_PACKAGE_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.STACK_NAME;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.STACK_VERSION;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_GROUPS;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_LIST;
 
 import java.text.MessageFormat;
@@ -413,6 +414,12 @@ public class AmbariCustomCommandExecutionHelper {
       String userList = gson.toJson(userSet);
       hostLevelParams.put(USER_LIST, userList);
 
+      //Create a user_group mapping and send it as part of the hostLevelParams
+      Map<String, Set<String>> userGroupsMap = configHelper.createUserGroupsMap(
+        stackId, cluster, desiredConfigs);
+      String userGroups = gson.toJson(userGroupsMap);
+      hostLevelParams.put(USER_GROUPS, userGroups);
+
       Set<String> groupSet = configHelper.getPropertyValuesWithPropertyType(stackId, PropertyType.GROUP, cluster, desiredConfigs);
       String groupList = gson.toJson(groupSet);
       hostLevelParams.put(GROUP_LIST, groupList);

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 3b88ac9..c25ab97 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -39,6 +39,7 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SCRIPT_TYPE;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_PACKAGE_FOLDER;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_REPO_INFO;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_GROUPS;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_LIST;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.VERSION;
 
@@ -2404,6 +2405,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     String userList = gson.toJson(userSet);
     hostParams.put(USER_LIST, userList);
 
+    //Create a user_group mapping and send it as part of the hostLevelParams
+    Map<String, Set<String>> userGroupsMap = configHelper.createUserGroupsMap(
+      cluster, clusterDesiredConfigs, servicesMap, stackProperties);
+    String userGroups = gson.toJson(userGroupsMap);
+    hostParams.put(USER_GROUPS, userGroups);
+
     Set<String> groupSet = configHelper.getPropertyValuesWithPropertyType(PropertyType.GROUP, cluster, clusterDesiredConfigs, servicesMap, stackProperties);
     String groupList = gson.toJson(groupSet);
     hostParams.put(GROUP_LIST, groupList);

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProvider.java
index 8a35c98..42e9b34 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProvider.java
@@ -35,6 +35,7 @@ import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.PACKAGE_L
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.SERVICE_REPO_INFO;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.STACK_NAME;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.STACK_VERSION;
+import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_GROUPS;
 import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.USER_LIST;
 
 import java.io.BufferedInputStream;
@@ -396,6 +397,12 @@ public class ClientConfigResourceProvider extends AbstractControllerResourceProv
         String userList = gson.toJson(userSet);
         hostLevelParams.put(USER_LIST, userList);
 
+        //Create a user_group mapping and send it as part of the hostLevelParams
+        Map<String, Set<String>> userGroupsMap = configHelper.createUserGroupsMap(
+          stackId, cluster, desiredClusterConfigs);
+        String userGroups = gson.toJson(userGroupsMap);
+        hostLevelParams.put(USER_GROUPS,userGroups);
+
         Set<String> groupSet = configHelper.getPropertyValuesWithPropertyType(stackId, PropertyType.GROUP, cluster, desiredClusterConfigs);
         String groupList = gson.toJson(groupSet);
         hostLevelParams.put(GROUP_LIST, groupList);

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/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 978ecd7..13114dd 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
@@ -545,6 +545,156 @@ public class ConfigHelper {
     return result;
   }
 
+  /***
+   * Fetch user to group mapping from the cluster configs. UserGroupEntries contain information regarding the group that the user is associated to.
+   * @param stackId
+   * @param cluster
+   * @param desiredConfigs
+   * @return
+   * @throws AmbariException
+   */
+  public Map<String, Set<String>> createUserGroupsMap(StackId stackId,
+                                                      Cluster cluster, Map<String, DesiredConfig> desiredConfigs) throws AmbariException {
+    StackInfo stack = ambariMetaInfo.getStack(stackId.getStackName(), stackId.getStackVersion());
+    Map<String, ServiceInfo> servicesMap = ambariMetaInfo.getServices(stack.getName(), stack.getVersion());
+    Set<PropertyInfo> stackProperties = ambariMetaInfo.getStackProperties(stack.getName(), stack.getVersion());
+    return createUserGroupsMap(cluster, desiredConfigs, servicesMap, stackProperties);
+  }
+
+  /***
+   * Fetch user to group mapping from the cluster configs. UserGroupEntries contain information regarding the group that the user is associated to.
+   * @param cluster
+   * @param desiredConfigs
+   * @param servicesMap
+   * @param stackProperties
+   * @return
+   * @throws AmbariException
+   */
+  public Map<String, Set<String>> createUserGroupsMap(
+    Cluster cluster, Map<String, DesiredConfig> desiredConfigs,
+    Map<String, ServiceInfo> servicesMap, Set<PropertyInfo> stackProperties) throws AmbariException {
+
+    Map<String, Set<String>> userGroupsMap = new HashMap<>();
+    Map<PropertyInfo, String> userProperties = getPropertiesWithPropertyType(
+      PropertyType.USER, cluster, desiredConfigs, servicesMap, stackProperties);
+    Map<PropertyInfo, String> groupProperties = getPropertiesWithPropertyType(
+      PropertyType.GROUP, cluster, desiredConfigs, servicesMap, stackProperties);
+
+    if(userProperties != null && groupProperties != null) {
+      for(Map.Entry<PropertyInfo, String> userProperty : userProperties.entrySet()) {
+        PropertyInfo userPropertyInfo = userProperty.getKey();
+        String userPropertyValue = userProperty.getValue();
+        if(userPropertyInfo.getPropertyValueAttributes() != null
+          && userPropertyInfo.getPropertyValueAttributes().getUserGroupEntries() != null) {
+          Set<String> groupPropertyValues = new HashSet<>();
+          Collection<UserGroupInfo> userGroupEntries = userPropertyInfo.getPropertyValueAttributes().getUserGroupEntries();
+          for (UserGroupInfo userGroupInfo : userGroupEntries) {
+            boolean found = false;
+            for(Map.Entry<PropertyInfo, String> groupProperty : groupProperties.entrySet()) {
+              PropertyInfo groupPropertyInfo = groupProperty.getKey();
+              String groupPropertyValue = groupProperty.getValue();
+              if(StringUtils.equals(userGroupInfo.getType(),
+                ConfigHelper.fileNameToConfigType(groupPropertyInfo.getFilename()))
+                && StringUtils.equals(userGroupInfo.getName(), groupPropertyInfo.getName())) {
+                groupPropertyValues.add(groupPropertyValue);
+                found = true;
+              }
+            }
+            if(!found) {
+             //Log error if the user-group mapping is not found
+              LOG.error("User group mapping property {" + userGroupInfo.getType() + "/" + userGroupInfo.getName() + "} is missing for user property {" + ConfigHelper.fileNameToConfigType(userPropertyInfo.getFilename()) + "/" + userPropertyInfo.getName() + "} (username = " + userPropertyInfo.getValue() +")");
+            }
+          }
+          userGroupsMap.put(userPropertyValue, groupPropertyValues);
+        }
+      }
+    }
+    return userGroupsMap;
+  }
+
+  /***
+   * Fetch all the properties of a given PropertyType. For eg: Fetch all cluster configs that are of type "user"
+   * @param stackId
+   * @param propertyType
+   * @param cluster
+   * @param desiredConfigs
+   * @return
+   * @throws AmbariException
+   */
+  public Map<PropertyInfo, String> getPropertiesWithPropertyType(StackId stackId, PropertyType propertyType,
+                                                                 Cluster cluster, Map<String, DesiredConfig> desiredConfigs) throws AmbariException {
+    StackInfo stack = ambariMetaInfo.getStack(stackId.getStackName(), stackId.getStackVersion());
+    Map<String, ServiceInfo> servicesMap = ambariMetaInfo.getServices(stack.getName(), stack.getVersion());
+    Set<PropertyInfo> stackProperties = ambariMetaInfo.getStackProperties(stack.getName(), stack.getVersion());
+
+    return getPropertiesWithPropertyType(propertyType, cluster, desiredConfigs, servicesMap, stackProperties);
+  }
+
+  /***
+   * Fetch all the properties of a given PropertyType. For eg: Fetch all cluster configs that are of type "user"
+   * @param propertyType
+   * @param cluster
+   * @param desiredConfigs
+   * @param servicesMap
+   * @param stackProperties
+   * @return
+   */
+  public Map<PropertyInfo, String> getPropertiesWithPropertyType(PropertyType propertyType, Cluster cluster,
+                                                                 Map<String, DesiredConfig> desiredConfigs, Map<String, ServiceInfo> servicesMap,
+                                                                 Set<PropertyInfo> stackProperties) throws AmbariException {
+    Map<String, Config> actualConfigs = new HashMap<>();
+    Map<PropertyInfo, String> result = new HashMap<>();
+
+    for (Map.Entry<String, DesiredConfig> desiredConfigEntry : desiredConfigs.entrySet()) {
+      String configType = desiredConfigEntry.getKey();
+      DesiredConfig desiredConfig = desiredConfigEntry.getValue();
+      actualConfigs.put(configType, cluster.getConfig(configType, desiredConfig.getTag()));
+    }
+
+    for (Service service : cluster.getServices().values()) {
+      Set<PropertyInfo> serviceProperties = new HashSet<PropertyInfo>(servicesMap.get(service.getName()).getProperties());
+      for (PropertyInfo serviceProperty : serviceProperties) {
+        if (serviceProperty.getPropertyTypes().contains(propertyType)) {
+          String stackPropertyConfigType = fileNameToConfigType(serviceProperty.getFilename());
+          try {
+            String property = actualConfigs.get(stackPropertyConfigType).getProperties().get(serviceProperty.getName());
+            if (null == property){
+              LOG.error(String.format("Unable to obtain property values for %s with property attribute %s. "
+                  + "The property does not exist in version %s of %s configuration.",
+                serviceProperty.getName(),
+                propertyType,
+                desiredConfigs.get(stackPropertyConfigType),
+                stackPropertyConfigType
+              ));
+            } else {
+              result.put(serviceProperty, property);
+            }
+          } catch (Exception ignored) {
+          }
+        }
+      }
+    }
+
+
+    for (PropertyInfo stackProperty : stackProperties) {
+      if (stackProperty.getPropertyTypes().contains(propertyType)) {
+        String stackPropertyConfigType = fileNameToConfigType(stackProperty.getFilename());
+        result.put(stackProperty, actualConfigs.get(stackPropertyConfigType).getProperties().get(stackProperty.getName()));
+      }
+    }
+
+    return result;
+  }
+
+  /***
+   * Fetch all the property values of a given PropertyType. For eg: Fetch all cluster configs that are of type "user"
+   * @param stackId
+   * @param propertyType
+   * @param cluster
+   * @param desiredConfigs
+   * @return
+   * @throws AmbariException
+   */
   public Set<String> getPropertyValuesWithPropertyType(StackId stackId, PropertyType propertyType,
                                                        Cluster cluster, Map<String,
                                                        DesiredConfig> desiredConfigs) throws AmbariException {
@@ -555,6 +705,16 @@ public class ConfigHelper {
     return getPropertyValuesWithPropertyType(propertyType, cluster, desiredConfigs, servicesMap, stackProperties);
   }
 
+  /***
+   * Fetch all the property values of a given PropertyType. For eg: Fetch all cluster configs that are of type "user"
+   * @param propertyType
+   * @param cluster
+   * @param desiredConfigs
+   * @param servicesMap
+   * @param stackProperties
+   * @return
+   * @throws AmbariException
+   */
   public Set<String> getPropertyValuesWithPropertyType(PropertyType propertyType,
                                                        Cluster cluster, Map<String, DesiredConfig> desiredConfigs,
                                                        Map<String, ServiceInfo> servicesMap,
@@ -592,6 +752,14 @@ public class ConfigHelper {
     return result;
   }
 
+  /***
+   * Fetch all the config values of a given PropertyType. For eg: Fetch all stack configs that are of type "user"
+   * @param cluster
+   * @param configType
+   * @param propertyName
+   * @return
+   * @throws AmbariException
+   */
   public String getPropertyValueFromStackDefinitions(Cluster cluster, String configType, String propertyName) throws AmbariException {
     StackId stackId = cluster.getCurrentStackVersion();
     StackInfo stack = ambariMetaInfo.getStack(stackId.getStackName(),

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/state/PropertyInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/PropertyInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/PropertyInfo.java
index 34c2941..2ad92fd 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/PropertyInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/PropertyInfo.java
@@ -190,6 +190,10 @@ public class PropertyInfo {
     return dependsOnProperties;
   }
 
+  public void setPropertyValueAttributes(ValueAttributesInfo propertyValueAttributes) {
+    this.propertyValueAttributes = propertyValueAttributes;
+  }
+
   public Set<PropertyDependencyInfo> getDependedByProperties() {
     return dependedByProperties;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/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 56fcd74..a38af29 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
@@ -384,10 +384,15 @@ public String getVersion() {
     return properties;
   }
 
+  public void setProperties(List properties) {
+    this.properties = properties;
+  }
+
   public List<ComponentInfo> getComponents() {
     if (components == null) components = new ArrayList<ComponentInfo>();
     return components;
   }
+
   /**
    * Finds ComponentInfo by component name
    * @param componentName  name of the component

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/state/UserGroupInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/UserGroupInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/UserGroupInfo.java
new file mode 100644
index 0000000..a8c4322
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/UserGroupInfo.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+public class UserGroupInfo {
+
+  private String type;
+  private String name;
+
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/java/org/apache/ambari/server/state/ValueAttributesInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ValueAttributesInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ValueAttributesInfo.java
index 30a1533..149505a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ValueAttributesInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ValueAttributesInfo.java
@@ -86,6 +86,10 @@ public class ValueAttributesInfo {
   @JsonProperty("property-file-type")
   private String propertyFileType;
 
+  @XmlElementWrapper(name = "user-groups")
+  @XmlElements(@XmlElement(name = "property"))
+  private Collection<UserGroupInfo> userGroupEntries;
+
   @XmlElement(name = "keystore")
   private boolean keyStore;
 
@@ -133,6 +137,14 @@ public class ValueAttributesInfo {
     this.entries = entries;
   }
 
+  public Collection<UserGroupInfo> getUserGroupEntries() {
+    return userGroupEntries;
+  }
+
+  public void setUserGroupEntries(Collection<UserGroupInfo> userGroupEntries) {
+    this.userGroupEntries = userGroupEntries;
+  }
+
   public String getHidden() {
     return hidden;
   }
@@ -313,6 +325,7 @@ public class ValueAttributesInfo {
     if (unit != null ? !unit.equals(that.unit) : that.unit != null) return false;
     if (delete != null ? !delete.equals(that.delete) : that.delete != null) return false;
     if (incrementStep != null ? !incrementStep.equals(that.incrementStep) : that.incrementStep != null) return false;
+    if (userGroupEntries != null ? !userGroupEntries.equals(that.userGroupEntries) : that.userGroupEntries != null) return false;
 
     return true;
   }
@@ -339,6 +352,7 @@ public class ValueAttributesInfo {
     result = 31 * result + (showPropertyName != null ? showPropertyName.hashCode() : 0);
     result = 31 * result + (uiOnlyProperty != null ? uiOnlyProperty.hashCode() : 0);
     result = 31 * result + (copy != null ? copy.hashCode() : 0);
+    result = 31 * result + (userGroupEntries != null ? userGroupEntries.hashCode() : 0);
     return result;
   }
 
@@ -364,6 +378,7 @@ public class ValueAttributesInfo {
       ", propertyFileName='" + propertyFileName + '\'' +
       ", propertyFileType='" + propertyFileType + '\'' +
       ", copy='" + copy + '\'' +
+      ", userGroupEntries='" + userGroupEntries + '\'' +
       '}';
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/resources/configuration-schema.xsd
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/configuration-schema.xsd b/ambari-server/src/main/resources/configuration-schema.xsd
index 1019dd9..18063cb 100644
--- a/ambari-server/src/main/resources/configuration-schema.xsd
+++ b/ambari-server/src/main/resources/configuration-schema.xsd
@@ -107,9 +107,17 @@
       <xs:element name="selection-cardinality" type="xs:string" minOccurs="0"/>
       <xs:element name="property-file-name" type="xs:string" minOccurs="0"/>
       <xs:element name="property-file-type" type="xs:string" minOccurs="0"/>
+    <xs:element name="user-groups" minOccurs="0">
+      <xs:complexType>
+        <xs:sequence>
+          <xs:element name="property" type="userGroupInfo" minOccurs="1"/>
+        </xs:sequence>
+      </xs:complexType>
+    </xs:element>
     </xs:all>
   </xs:complexType>
 
+
   <xs:complexType name="valueEntryInfo">
     <xs:all>
       <xs:element name="value" type="xs:string" minOccurs="0"/>
@@ -118,6 +126,15 @@
     </xs:all>
   </xs:complexType>
 
+  <xs:complexType name="userGroupInfo">
+    <xs:sequence>
+      <xs:choice minOccurs="0" maxOccurs="unbounded">
+        <xs:element name="name" type="xs:string" minOccurs="0"/>
+        <xs:element name="type" type="xs:string" minOccurs="0"/>
+      </xs:choice>
+    </xs:sequence>
+  </xs:complexType>
+
   <xs:complexType name="propertyDependencyInfo">
     <xs:sequence>
       <xs:choice minOccurs="0" maxOccurs="unbounded">

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/main/resources/stacks/HDP/2.0.6/hooks/before-ANY/scripts/params.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.0.6/hooks/before-ANY/scripts/params.py b/ambari-server/src/main/resources/stacks/HDP/2.0.6/hooks/before-ANY/scripts/params.py
index a748b33..d1eeaa5 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.0.6/hooks/before-ANY/scripts/params.py
+++ b/ambari-server/src/main/resources/stacks/HDP/2.0.6/hooks/before-ANY/scripts/params.py
@@ -20,6 +20,7 @@ limitations under the License.
 import collections
 import re
 import os
+import ast
 
 import ambari_simplejson as json # simplejson is much faster comparing to Python 2.6 json module and has the same functions set.
 
@@ -245,7 +246,13 @@ if has_ranger_admin:
   user_to_groups_dict[ranger_user] = [ranger_group]
 if has_zeppelin_master:
   user_to_groups_dict[zeppelin_user] = [zeppelin_group, user_group]
-
+#Append new user-group mapping to the dict
+try:
+  user_group_map = ast.literal_eval(config['hostLevelParams']['user_group'])
+  for key in user_group_map.iterkeys():
+    user_to_groups_dict[key] = user_group_map[key]
+except ValueError:
+  print('User Group mapping (user_group) is missing in the hostLevelParams')
 user_to_gid_dict = collections.defaultdict(lambda:user_group)
 
 user_list = json.loads(config['hostLevelParams']['user_list'])

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java b/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
index 19df802..334ab01 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
@@ -130,7 +130,7 @@ public class AmbariMetaInfoTest {
   private static final String NON_EXT_VALUE = "XXX";
 
   private static final int REPOS_CNT = 3;
-  private static final int PROPERTIES_CNT = 62;
+  private static final int PROPERTIES_CNT = 64;
   private static final int OS_CNT = 4;
 
   private static TestAmbariMetaInfo metaInfo = null;

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
index 63a9e9c..84fbba3 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariCustomCommandExecutionHelperTest.java
@@ -51,15 +51,21 @@ import org.apache.ambari.server.security.TestAuthenticationFactory;
 import org.apache.ambari.server.security.authorization.AuthorizationException;
 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.Host;
 import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.SecurityType;
 import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.state.UserGroupInfo;
+import org.apache.ambari.server.state.ValueAttributesInfo;
 import org.apache.ambari.server.topology.TopologyManager;
 import org.apache.ambari.server.utils.StageUtils;
 import org.easymock.Capture;
@@ -91,6 +97,9 @@ public class AmbariCustomCommandExecutionHelperTest {
   @Mock(type = MockType.NICE)
   private HostRoleCommand hostRoleCommand;
 
+  @Mock(type = MockType.NICE)
+  private ConfigHelper configHelper;
+
   private Injector injector;
   private Clusters clusters;
   private AmbariManagementController ambariManagementController;
@@ -100,7 +109,7 @@ public class AmbariCustomCommandExecutionHelperTest {
 
   @Before
   public void setup() throws Exception {
-    EasyMock.reset(actionManager, hostRoleCommand);
+    EasyMock.reset(actionManager, hostRoleCommand, configHelper);
 
     InMemoryDefaultTestModule module = new InMemoryDefaultTestModule(){
       @Override
@@ -109,6 +118,7 @@ public class AmbariCustomCommandExecutionHelperTest {
           OVERRIDDEN_SERVICE_CHECK_TIMEOUT_VALUE);
         super.configure();
         bind(ActionManager.class).toInstance(actionManager);
+        bind(ConfigHelper.class).toInstance(configHelper);
       }
     };
 
@@ -132,6 +142,41 @@ public class AmbariCustomCommandExecutionHelperTest {
     EasyMock.expect(actionManager.getNextRequestId()).andReturn(1L).anyTimes();
     EasyMock.expect(actionManager.getRequestTasks(1L)).andReturn(Collections.singletonList(hostRoleCommand));
 
+    StackInfo stackInfo = new StackInfo();
+    stackInfo.setName("HDP");
+    stackInfo.setVersion("2.0.6");
+    StackId stackId = new StackId(stackInfo);
+    Map<String, DesiredConfig> desiredConfigMap = new HashMap<String, DesiredConfig>();
+    Map<PropertyInfo, String> userProperties = new HashMap<>();
+    Map<PropertyInfo, String> groupProperties = new HashMap<>();
+    PropertyInfo userProperty = new PropertyInfo();
+    userProperty.setFilename("zookeeper-env.xml");
+    userProperty.setName("zookeeper-user");
+    userProperty.setValue("zookeeperUser");
+    PropertyInfo groupProperty = new PropertyInfo();
+    groupProperty.setFilename("zookeeper-env.xml");
+    groupProperty.setName("zookeeper-group");
+    groupProperty.setValue("zookeeperGroup");
+    ValueAttributesInfo valueAttributesInfo = new ValueAttributesInfo();
+    valueAttributesInfo.setType("user");
+    Set<UserGroupInfo> userGroupEntries = new HashSet<>();
+    UserGroupInfo userGroupInfo = new UserGroupInfo();
+    userGroupInfo.setType("zookeeper-env");
+    userGroupInfo.setName("zookeeper-group");
+    userGroupEntries.add(userGroupInfo);
+    valueAttributesInfo.setUserGroupEntries(userGroupEntries);
+    userProperty.setPropertyValueAttributes(valueAttributesInfo);
+    userProperties.put(userProperty, "zookeeperUser");
+    groupProperties.put(groupProperty, "zookeeperGroup");
+    Map<String, Set<String>> userGroupsMap = new HashMap<>();
+    userGroupsMap.put("zookeeperUser", new HashSet<String>(Arrays.asList("zookeeperGroup")));
+    Cluster cluster = clusters.getCluster("c1");
+    EasyMock.expect(configHelper.getPropertiesWithPropertyType(
+      stackId, PropertyInfo.PropertyType.USER, cluster, desiredConfigMap)).andReturn(userProperties).anyTimes();
+    EasyMock.expect(configHelper.getPropertiesWithPropertyType(
+      stackId, PropertyInfo.PropertyType.GROUP, cluster, desiredConfigMap)).andReturn(groupProperties).anyTimes();
+    EasyMock.expect(configHelper.createUserGroupsMap(stackId, cluster, desiredConfigMap)).andReturn(userGroupsMap).anyTimes();
+
     actionManager.sendActions(EasyMock.capture(requestCapture), EasyMock.anyObject(ExecuteActionRequest.class));
     EasyMock.expectLastCall();
 
@@ -160,7 +205,7 @@ public class AmbariCustomCommandExecutionHelperTest {
         }, false);
     actionRequest.getResourceFilters().add(new RequestResourceFilter("YARN", "RESOURCEMANAGER", Collections.singletonList("c1-c6401")));
 
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
 
     ambariManagementController.createAction(actionRequest, requestProperties);
 
@@ -176,6 +221,9 @@ public class AmbariCustomCommandExecutionHelperTest {
     Assert.assertEquals(1, commands.size());
 
     ExecutionCommand command = commands.get(0).getExecutionCommand();
+    Assert.assertNotNull(command.getHostLevelParams());
+    Assert.assertTrue(command.getHostLevelParams().containsKey(ExecutionCommand.KeyNames.USER_GROUP));
+    Assert.assertEquals("{\"zookeeperUser\":[\"zookeeperGroup\"]}", command.getHostLevelParams().get(ExecutionCommand.KeyNames.USER_GROUP));
     Assert.assertEquals(true, command.getForceRefreshConfigTagsBeforeExecution());
   }
 
@@ -205,7 +253,7 @@ public class AmbariCustomCommandExecutionHelperTest {
         },
        false);
 
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
 
     ambariManagementController.createAction(actionRequest, requestProperties);
 
@@ -243,7 +291,7 @@ public class AmbariCustomCommandExecutionHelperTest {
           }
         }, false);
 
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
 
     ambariManagementController.createAction(actionRequest, requestProperties);
 
@@ -283,7 +331,7 @@ public class AmbariCustomCommandExecutionHelperTest {
           }
         }, false);
 
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
 
     ambariManagementController.createAction(actionRequest, requestProperties);
 
@@ -332,7 +380,7 @@ public class AmbariCustomCommandExecutionHelperTest {
           }
         }, false);
 
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
     ambariManagementController.createAction(actionRequest, requestProperties);
     Assert.fail(
         "Expected an exception since there are no hosts which can run the ZK service check");
@@ -375,7 +423,7 @@ public class AmbariCustomCommandExecutionHelperTest {
           }
         }, false);
 
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
     ambariManagementController.createAction(actionRequest, requestProperties);
     Assert.fail("Expected an exception since there are no hosts which can run the ZK service check");
   }
@@ -424,7 +472,7 @@ public class AmbariCustomCommandExecutionHelperTest {
 
     HashSet<String> localComponents = new HashSet<>();
     EasyMock.expect(execCmd.getLocalComponents()).andReturn(localComponents).anyTimes();
-    EasyMock.replay(stage, execCmdWrapper, execCmd);
+    EasyMock.replay(configHelper,stage, execCmdWrapper, execCmd);
 
     ambariCustomCommandExecutionHelper.addExecutionCommandsToStage(actionExecutionContext, stage, new HashMap<String, String>());
     Map<String, String> configMap = timeOutCapture.getValues().get(0);
@@ -506,7 +554,7 @@ public class AmbariCustomCommandExecutionHelperTest {
               }
             }, false);
     actionRequest.getResourceFilters().add(new RequestResourceFilter("YARN", "RESOURCEMANAGER", Collections.singletonList("c1-c6401")));
-    EasyMock.replay(hostRoleCommand, actionManager);
+    EasyMock.replay(hostRoleCommand, actionManager, configHelper);
 
     ambariManagementController.createAction(actionRequest, requestProperties);
     StackId stackId = clusters.getCluster("c1").getDesiredStackVersion();

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
index c40ff64..3a53388 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
@@ -1246,11 +1246,18 @@ public class AmbariManagementControllerTest {
     Map<String, String> configs = new HashMap<String, String>();
     configs.put("a", "b");
 
-    ConfigurationRequest cr1,cr2;
+    Map<String, String> hadoopEnvConfigs = new HashMap<>();
+    hadoopEnvConfigs.put("hdfs_user", "myhdfsuser");
+    hadoopEnvConfigs.put("hdfs_group", "myhdfsgroup");
+
+    ConfigurationRequest cr1,cr2, cr3;
+
     cr1 = new ConfigurationRequest(cluster1, "core-site","version1",
                                    configs, null);
     cr2 = new ConfigurationRequest(cluster1, "hdfs-site","version1",
                                    configs, null);
+    cr3 = new ConfigurationRequest(cluster1, "hadoop-env","version1",
+      hadoopEnvConfigs, null);
 
     ClusterRequest crReq = new ClusterRequest(cluster.getClusterId(), cluster1, null, null);
     crReq.setDesiredConfig(Collections.singletonList(cr1));
@@ -1258,6 +1265,9 @@ public class AmbariManagementControllerTest {
     crReq = new ClusterRequest(cluster.getClusterId(), cluster1, null, null);
     crReq.setDesiredConfig(Collections.singletonList(cr2));
     controller.updateClusters(Collections.singleton(crReq), null);
+    crReq = new ClusterRequest(cluster.getClusterId(), cluster1, null, null);
+    crReq.setDesiredConfig(Collections.singletonList(cr3));
+    controller.updateClusters(Collections.singleton(crReq), null);
 
 
 
@@ -1271,11 +1281,13 @@ public class AmbariManagementControllerTest {
     assertEquals(cluster1, ec.getClusterName());
     Map<String, Map<String, String>> configurations = ec.getConfigurations();
     assertNotNull(configurations);
-    assertEquals(2, configurations.size());
+    assertEquals(3, configurations.size());
     assertTrue(configurations.containsKey("hdfs-site"));
     assertTrue(configurations.containsKey("core-site"));
+    assertTrue(configurations.containsKey("hadoop-env"));
     assertTrue(ec.getConfigurationAttributes().containsKey("hdfs-site"));
     assertTrue(ec.getConfigurationAttributes().containsKey("core-site"));
+    assertTrue(ec.getConfigurationAttributes().containsKey("hadoop-env"));
     assertTrue(ec.getCommandParams().containsKey("max_duration_for_retries"));
     assertEquals("0", ec.getCommandParams().get("max_duration_for_retries"));
     assertTrue(ec.getCommandParams().containsKey("command_retry_enabled"));
@@ -1291,6 +1303,13 @@ public class AmbariManagementControllerTest {
     assertNotNull(ec.getCommandParams());
     assertTrue(ec.getCommandParams().containsKey("custom_folder"));
     assertEquals("dashboards", ec.getCommandParams().get("custom_folder"));
+    assertNotNull(ec.getHostLevelParams());
+    assertTrue(ec.getHostLevelParams().containsKey(ExecutionCommand.KeyNames.USER_LIST));
+    assertEquals("[\"myhdfsuser\"]", ec.getHostLevelParams().get(ExecutionCommand.KeyNames.USER_LIST));
+    assertTrue(ec.getHostLevelParams().containsKey(ExecutionCommand.KeyNames.GROUP_LIST));
+    assertEquals("[\"myhdfsgroup\"]", ec.getHostLevelParams().get(ExecutionCommand.KeyNames.GROUP_LIST));
+    assertTrue(ec.getHostLevelParams().containsKey(ExecutionCommand.KeyNames.USER_GROUP));
+    assertEquals("{\"myhdfsuser\":[\"myhdfsgroup\"]}", ec.getHostLevelParams().get(ExecutionCommand.KeyNames.USER_GROUP));
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProviderTest.java
index 8fad94e..84678e6 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClientConfigResourceProviderTest.java
@@ -26,6 +26,7 @@ import static org.easymock.EasyMock.expectLastCall;
 import static org.easymock.EasyMock.replay;
 import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.powermock.api.mockito.PowerMockito.whenNew;
 
 import java.io.ByteArrayInputStream;
@@ -72,7 +73,10 @@ import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.ServiceOsSpecific;
 import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.UserGroupInfo;
+import org.apache.ambari.server.state.ValueAttributesInfo;
 import org.apache.ambari.server.utils.StageUtils;
+import org.apache.commons.io.FileUtils;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -189,7 +193,9 @@ public class ClientConfigResourceProviderTest {
     Configuration configuration = PowerMock.createStrictMockAndExpectNew(Configuration.class);
     Map<String, String> configMap = createNiceMock(Map.class);
 
-    File mockFile = PowerMock.createNiceMock(File.class);
+    File newFile = File.createTempFile("config",".json",new File("/tmp/"));
+    newFile.deleteOnExit();
+
     Runtime runtime = createMock(Runtime.class);
     Process process = createNiceMock(Process.class);
 
@@ -320,14 +326,42 @@ public class ClientConfigResourceProviderTest {
     expect(serviceInfo.getOsSpecifics()).andReturn(new HashMap<String, ServiceOsSpecific>()).anyTimes();
     Set<String> userSet = new HashSet<String>();
     userSet.add("hdfs");
-    expect(configHelper.getPropertyValuesWithPropertyType(stackId, PropertyInfo.PropertyType.USER, cluster, desiredConfigMap)).andReturn(userSet);
-    PowerMock.expectNew(File.class, new Class<?>[]{String.class}, anyObject(String.class)).andReturn(mockFile).anyTimes();
-    PowerMock.createNiceMockAndExpectNew(PrintWriter.class, anyObject());
-    expect(mockFile.getParent()).andReturn("");
-    PowerMock.mockStatic(Runtime.class);
-    expect(mockFile.exists()).andReturn(true);
-    String commandLine = "ambari-python-wrap /tmp/stacks/S1/V1/PIG/package/null generate_configs null " +
-            "/tmp/stacks/S1/V1/PIG/package /var/lib/ambari-server/tmp/structured-out.json " +
+    expect(configHelper.getPropertyValuesWithPropertyType(
+      stackId, PropertyInfo.PropertyType.USER, cluster, desiredConfigMap)).andReturn(userSet);
+    Map<PropertyInfo, String> userProperties = new HashMap<>();
+    Map<PropertyInfo, String> groupProperties = new HashMap<>();
+    PropertyInfo userProperty = new PropertyInfo();
+    userProperty.setFilename("hadoop-env.xml");
+    userProperty.setName("hdfs-user");
+    userProperty.setValue("hdfsUser");
+
+
+    PropertyInfo groupProperty = new PropertyInfo();
+    groupProperty.setFilename("hadoop-env.xml");
+    groupProperty.setName("hdfs-group");
+    groupProperty.setValue("hdfsGroup");
+    ValueAttributesInfo valueAttributesInfo = new ValueAttributesInfo();
+    valueAttributesInfo.setType("user");
+    Set<UserGroupInfo> userGroupEntries = new HashSet<>();
+    UserGroupInfo userGroupInfo = new UserGroupInfo();
+    userGroupInfo.setType("hadoop-env");
+    userGroupInfo.setName("hdfs-group");
+    userGroupEntries.add(userGroupInfo);
+    valueAttributesInfo.setUserGroupEntries(userGroupEntries);
+    userProperty.setPropertyValueAttributes(valueAttributesInfo);
+    userProperties.put(userProperty, "hdfsUser");
+    groupProperties.put(groupProperty, "hdfsGroup");
+    Map<String, Set<String>> userGroupsMap = new HashMap<>();
+    userGroupsMap.put("hdfsUser", new HashSet<String>(Arrays.asList("hdfsGroup")));
+    expect(configHelper.getPropertiesWithPropertyType(
+      stackId, PropertyInfo.PropertyType.USER, cluster, desiredConfigMap)).andReturn(userProperties).anyTimes();
+    expect(configHelper.getPropertiesWithPropertyType(
+      stackId, PropertyInfo.PropertyType.GROUP, cluster, desiredConfigMap)).andReturn(groupProperties).anyTimes();
+    expect(configHelper.createUserGroupsMap(stackId, cluster, desiredConfigMap)).andReturn(userGroupsMap).anyTimes();
+
+    PowerMock.expectNew(File.class, new Class<?>[]{String.class}, anyObject(String.class)).andReturn(newFile).anyTimes();
+    String commandLine = "ambari-python-wrap /tmp/stacks/S1/V1/PIG/package/null generate_configs "+newFile  +
+      " /tmp/stacks/S1/V1/PIG/package /var/lib/ambari-server/tmp/structured-out.json " +
             "INFO /var/lib/ambari-server/tmp";
 
     if (System.getProperty("os.name").contains("Windows")) {
@@ -364,6 +398,8 @@ public class ClientConfigResourceProviderTest {
 
     Set<Resource> resources = provider.getResources(request, predicate);
     assertFalse(resources.isEmpty());
+    String str = FileUtils.readFileToString(newFile);
+    assertTrue(str.contains("\"user_groups\":\"{\\\"hdfsUser\\\":[\\\"hdfsGroup\\\"]}"));
 
     // verify
     verify(managementController, clusters, cluster, ambariMetaInfo, stackId, componentInfo,commandScriptDefinition,

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/java/org/apache/ambari/server/stack/StackManagerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/StackManagerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/StackManagerTest.java
index 87a1fc7..0234d79 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/stack/StackManagerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/StackManagerTest.java
@@ -194,7 +194,7 @@ public class StackManagerTest {
     List<ComponentInfo> components = hdfsService.getComponents();
     assertEquals(6, components.size());
     List<PropertyInfo> properties = hdfsService.getProperties();
-    assertEquals(62, properties.size());
+    assertEquals(64, properties.size());
 
     // test a couple of the properties for filename
     boolean hdfsPropFound = false;

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/python/stacks/2.0.6/configs/default.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/stacks/2.0.6/configs/default.json b/ambari-server/src/test/python/stacks/2.0.6/configs/default.json
index 2a27eca..fa7419f 100644
--- a/ambari-server/src/test/python/stacks/2.0.6/configs/default.json
+++ b/ambari-server/src/test/python/stacks/2.0.6/configs/default.json
@@ -20,6 +20,7 @@
         "java_home": "/usr/jdk64/jdk1.7.0_45",
         "java_version": "8",
         "db_name": "ambari",
+        "user_group": "{\"sample\":[\"sample\",\"users\"]}",
         "group_list": "[\"hadoop\",\"nobody\",\"users\"]",
         "user_list": "[\"hive\",\"oozie\",\"nobody\",\"ambari-qa\",\"flume\",\"hdfs\",\"storm\",\"mapred\",\"hbase\",\"tez\",\"zookeeper\",\"falcon\",\"sqoop\",\"yarn\",\"hcat\"]",
         "custom_mysql_jdbc_name" : "mysql-connector-java.jar",

http://git-wip-us.apache.org/repos/asf/ambari/blob/b856c5f3/ambari-server/src/test/resources/stacks/HDP/0.1/services/HDFS/configuration/hadoop-env.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/resources/stacks/HDP/0.1/services/HDFS/configuration/hadoop-env.xml b/ambari-server/src/test/resources/stacks/HDP/0.1/services/HDFS/configuration/hadoop-env.xml
new file mode 100644
index 0000000..4e5ff1a
--- /dev/null
+++ b/ambari-server/src/test/resources/stacks/HDP/0.1/services/HDFS/configuration/hadoop-env.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
+<!--
+/**
+ * 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.
+ */
+-->
+<configuration supports_adding_forbidden="true">
+    <property>
+        <name>hdfs_user</name>
+        <display-name>HDFS User</display-name>
+        <value>hdfs</value>
+        <property-type>USER</property-type>
+        <description>User to run HDFS as</description>
+        <value-attributes>
+            <type>user</type>
+            <overridable>false</overridable>
+            <user-groups>
+                <property>
+                    <type>hadoop-env</type>
+                    <name>hdfs_group</name>
+                </property>
+            </user-groups>
+        </value-attributes>
+        <on-ambari-upgrade add="true"/>
+    </property>
+    <property>
+        <name>hdfs_group</name>
+        <display-name>HDFS User Group</display-name>
+        <value>hdfs_group</value>
+        <property-type>GROUP</property-type>
+        <description>HDFS user group.</description>
+        <value-attributes>
+            <type>user</type>
+            <overridable>false</overridable>
+        </value-attributes>
+        <on-ambari-upgrade add="true"/>
+    </property>
+</configuration>
\ No newline at end of file