Browse Source

Merge branch 'trunk' into branch-alerts-dev

Conflicts:
	ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
Jonathan Hurley 10 years ago
parent
commit
7f86e75c0c
68 changed files with 4408 additions and 530 deletions
  1. 0 1
      ambari-admin/src/main/resources/ui/admin-web/app/index.html
  2. 1 21
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/NavbarCtrl.js
  3. 0 60
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ldap.js
  4. 1 9
      ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
  5. 0 9
      ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
  6. 4 0
      ambari-server/sbin/ambari-server
  7. 57 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinition.java
  8. 4 0
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java
  9. 144 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/LdapSyncEventService.java
  10. 9 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java
  11. 27 4
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
  12. 40 13
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java
  13. 2 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java
  14. 438 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java
  15. 4 0
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java
  16. 3 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java
  17. 284 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntity.java
  18. 143 0
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java
  19. 14 3
      ambari-server/src/main/java/org/apache/ambari/server/proxy/ProxyService.java
  20. 10 7
      ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
  21. 216 52
      ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java
  22. 39 0
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java
  23. 32 27
      ambari-server/src/main/java/org/apache/ambari/server/state/stack/LatestRepoCallable.java
  24. 165 50
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java
  25. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog161.java
  26. 4 23
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
  27. 167 0
      ambari-server/src/main/java/org/apache/ambari/server/view/HttpImpersonatorImpl.java
  28. 63 0
      ambari-server/src/main/java/org/apache/ambari/server/view/ImpersonatorSettingImpl.java
  29. 16 2
      ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java
  30. 5 21
      ambari-server/src/main/java/org/apache/ambari/server/view/ViewProviderModule.java
  31. 21 1
      ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
  32. 74 0
      ambari-server/src/main/python/ambari-server.py
  33. 2 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/package/scripts/application_timeline_server.py
  34. 1 0
      ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/package/scripts/status_params.py
  35. 2 2
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/scripts/params.py
  36. 2 20
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/scripts/slider.py
  37. 2 6
      ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/templates/slider-env.sh.j2
  38. 49 0
      ambari-server/src/test/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinitionTest.java
  39. 139 66
      ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java
  40. 97 0
      ambari-server/src/test/java/org/apache/ambari/server/api/services/LdapSyncEventServiceTest.java
  41. 114 0
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProviderTest.java
  42. 166 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java
  43. 95 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java
  44. 14 0
      ambari-server/src/test/java/org/apache/ambari/server/proxy/ProxyServiceTest.java
  45. 1271 32
      ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java
  46. 6 4
      ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java
  47. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog161Test.java
  48. 6 29
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog170Test.java
  49. 128 0
      ambari-server/src/test/java/org/apache/ambari/server/view/HttpImpersonatorImplTest.java
  50. 20 0
      ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
  51. 4 25
      ambari-server/src/test/python/stacks/2.2/SLIDER/test_slider_client.py
  52. 53 0
      ambari-views/src/main/java/org/apache/ambari/view/HttpImpersonator.java
  53. 46 0
      ambari-views/src/main/java/org/apache/ambari/view/ImpersonatorSetting.java
  54. 15 0
      ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java
  55. 0 22
      ambari-web/app/utils/ajax/ajax.js
  56. 15 0
      contrib/views/jobs/pom.xml
  57. 68 0
      contrib/views/jobs/src/main/java/org/apache/ambari/view/jobs/ProxyServlet.java
  58. 37 0
      contrib/views/jobs/src/main/resources/WEB-INF/web.xml
  59. 8 2
      contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js
  60. 19 6
      contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js
  61. 12 3
      contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/jobs.js
  62. 8 0
      contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js
  63. 1 1
      contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js
  64. 9 0
      contrib/views/slider/src/main/resources/ui/app/mappers/slider_apps_mapper.js
  65. 5 0
      contrib/views/slider/src/main/resources/ui/app/models/slider_app.js
  66. 3 3
      contrib/views/slider/src/main/resources/ui/app/styles/application.less
  67. 1 1
      contrib/views/slider/src/main/resources/ui/app/templates/slider_app/summary.hbs
  68. 1 1
      contrib/views/slider/src/main/resources/ui/app/templates/slider_apps.hbs

+ 0 - 1
ambari-admin/src/main/resources/ui/admin-web/app/index.html

@@ -134,7 +134,6 @@
     <script src="scripts/services/User.js"></script>
     <script src="scripts/services/Group.js"></script>
     <script src="scripts/services/View.js"></script>
-    <script src="scripts/services/ldap.js"></script>
     <script src="scripts/services/Cluster.js"></script>
     <script src="scripts/services/uiAlert.js"></script>
     <script src="scripts/services/PermissionLoader.js"></script>

+ 1 - 21
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/NavbarCtrl.js

@@ -18,7 +18,7 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('NavbarCtrl',['$scope', 'Cluster', '$location', 'uiAlert', 'ROUTES', 'LDAP', 'ConfirmationModal', '$rootScope', function($scope, Cluster, $location, uiAlert, ROUTES, LDAP, ConfirmationModal, $rootScope) {
+.controller('NavbarCtrl',['$scope', 'Cluster', '$location', 'uiAlert', 'ROUTES', 'ConfirmationModal', '$rootScope', function($scope, Cluster, $location, uiAlert, ROUTES, ConfirmationModal, $rootScope) {
   $scope.cluster = null;
   $scope.editCluster = {
     name        : '',
@@ -73,24 +73,4 @@ angular.module('ambariAdminConsole')
   	var r = new RegExp( route.url.replace(/(:\w+)/, '\\w+'));
   	return r.test($location.path());
   };
-
-  $scope.isLDAPConfigured = false;
-  $scope.ldapData = {};
-  LDAP.get().then(function(data) {
-    $scope.ldapData = data.data;
-    $scope.isLDAPConfigured = data.data['LDAP']['configured'];
-  });
-
-  $scope.syncLDAP = function() {
-    ConfirmationModal.show('Sync LDAP', 'Are you sure you want to sync LDAP?').then(function() {
-      LDAP.sync($scope.ldapData['LDAP'].groups, $scope.ldapData['LDAP'].users).then(function() {
-        uiAlert.success('LDAP synced successful');
-        $rootScope.$evalAsync(function() {
-          $rootScope.LDAPSynced = true;
-        });
-      }).catch(function(data) {
-        uiAlert.danger(data.data.status, data.data.message);
-      });
-    });
-  };
 }]);

+ 0 - 60
ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ldap.js

@@ -1,60 +0,0 @@
-/**
- * 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.
- */
-'use strict';
-
-angular.module('ambariAdminConsole')
-.factory('LDAP', ['$http', '$q', 'Settings', function($http, $q, Settings) {
-
-
-  return {
-    get: function() {
-      return $http({
-        method: 'GET',
-        url: '/api/v1/controllers/ldap'
-      });
-    },
-    sync: function(groupsList, usersList) {
-      groupsList = Array.isArray(groupsList) ? groupsList : [];
-      usersList = Array.isArray(usersList) ? usersList : [];
-      return $http({
-        method: 'PUT',
-        url: Settings.baseUrl + '/controllers/ldap',
-        data:[{
-          LDAP:{
-            "synced_groups": groupsList.join(','),
-            "synced_users": usersList.join(',')
-          }
-        }]
-      });
-    },
-    syncResource: function(resourceType, items) {
-      var items = items.map(function(item) {
-        var name = 'LDAP/synced_' + resourceType;
-        var obj = {};
-        obj['LDAP/synced_' + resourceType] = item;
-        return obj;
-      });
-      
-      return $http({
-        method: 'POST',
-        url: '/api/v1/controllers/ldap',
-        data: items
-      });
-    }
-  };
-}]);

+ 1 - 9
ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css

@@ -345,11 +345,6 @@
   -o-transform: translateY(2px);
   transform: translateY(2px);
 }
-.btn.disabled.syncldapbtn{
-  pointer-events: auto;
-  background-color: #e6e6e6;
-  cursor: not-allowed;
-}
 .btn.deleteuser-btn.disabled, .btn.deleteuser-btn[disabled], .btn.btn-delete-instance.disabled{
   pointer-events: auto;
   cursor: not-allowed;
@@ -466,9 +461,6 @@
   font-size: 13px;
   color: #428bca;
 }
-.left-navbar .panel-body #LDAP-button {
-  padding: 5px;
-}
 .left-navbar .panel-body hr{
   margin-top: 5px;
   margin-bottom: 5px;
@@ -1099,4 +1091,4 @@ button.btn.btn-xs{
 
 accordion .panel-group .panel{
   overflow: visible;
-}
+}

+ 0 - 9
ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html

@@ -85,15 +85,6 @@
         <li ng-class="{active: isActive('users.list')}"><link-to route="users.list" class="userslist-link">Users</link-to></li>
         <li ng-class="{active: isActive('groups.list')}"><link-to route="groups.list" class="groupslist-link">Groups</link-to></li>
       </ul>
-      <hr>
-      <div id="LDAP-button" ng-switch="isLDAPConfigured">
-        <a ng-switch-when="true" href class="btn btn-primary btn-block syncldapbtn" ng-click="syncLDAP()">
-          <span class="glyphicon glyphicon-transfer pulldown2"></span> Sync LDAP
-        </a>
-        <a ng-switch-default href class="btn btn-default btn-block syncldapbtn disabled" tooltip="LDAP is not configured. To configure LDAP, run ambari-server setup-ldap from the command line.">
-          <span class="glyphicon glyphicon-transfer pulldown2"></span> Sync LDAP
-        </a>
-      </div>
         
     </div>
   </div>

+ 4 - 0
ambari-server/sbin/ambari-server

@@ -115,6 +115,10 @@ case "$1" in
         echo -e "Setting up LDAP properties..."
         $PYTHON /usr/sbin/ambari-server.py $@
         ;;
+  sync-ldap)
+        echo -e "Syncing with LDAP..."
+        $PYTHON /usr/sbin/ambari-server.py $@
+        ;;
   setup-security)
         echo -e "Security setup options..."
         $PYTHON /usr/sbin/ambari-server.py $@

+ 57 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinition.java

@@ -0,0 +1,57 @@
+/**
+ * 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.controller.spi.Resource;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * LDAP sync event resource definition.
+ */
+public class LdapSyncEventResourceDefinition extends BaseResourceDefinition {
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an event resource definition.
+   */
+  public LdapSyncEventResourceDefinition() {
+    super(Resource.Type.LdapSyncEvent);
+  }
+
+
+  // ----- ResourceDefinition ------------------------------------------------
+
+  @Override
+  public String getPluralName() {
+    return "ldap_sync_events";
+  }
+
+  @Override
+  public String getSingularName() {
+    return "ldap_sync_event";
+  }
+
+  @Override
+  public Set<SubResourceDefinition> getSubResourceDefinitions() {
+    return Collections.emptySet();
+  }
+}

+ 4 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceInstanceFactoryImpl.java

@@ -292,6 +292,10 @@ public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {
         resourceDefinition = new ClientConfigResourceDefinition();
         break;
 
+      case LdapSyncEvent:
+        resourceDefinition = new LdapSyncEventResourceDefinition();
+        break;
+
       default:
         throw new IllegalArgumentException("Unsupported resource type: " + type);
     }

+ 144 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/LdapSyncEventService.java

@@ -0,0 +1,144 @@
+/**
+ * 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.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.Collections;
+
+/**
+ * Service responsible for ldap sync event resource requests.
+ */
+@Path("/ldap_sync_events/")
+public class LdapSyncEventService extends BaseService {
+  /**
+   * Handles: GET /ldap_sync_events/{eventId}
+   * Get a specific view.
+   *
+   * @param headers   http headers
+   * @param ui        uri info
+   * @param eventId   event id
+   *
+   * @return view instance representation
+   */
+  @GET
+  @Path("{eventId}")
+  @Produces("text/plain")
+  public Response getEvent(@Context HttpHeaders headers, @Context UriInfo ui,
+                          @PathParam("eventId") String eventId) {
+
+    return handleRequest(headers, null, ui, Request.Type.GET, createEventResource(eventId));
+  }
+
+  /**
+   * Handles: GET  /ldap_sync_events
+   * Get all events.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   *
+   * @return view collection resource representation
+   */
+  @GET
+  @Produces("text/plain")
+  public Response getEvents(@Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, null, ui, Request.Type.GET, createEventResource(null));
+  }
+
+  /**
+   * Handles: POST /ldap_sync_events/
+   * Create an event.
+   *
+   * @param headers    http headers
+   * @param ui         uri info
+   *
+   * @return information regarding the created view
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createEvent(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST, createEventResource(null));
+  }
+
+  /**
+   * Handles: PUT /ldap_sync_events/{eventId}
+   * Update a specific event.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   * @param eventId  event id
+   *
+   * @return information regarding the updated event
+   */
+  @PUT
+  @Path("{eventId}")
+  @Produces("text/plain")
+  public Response updateEvent(String body, @Context HttpHeaders headers, @Context UriInfo ui,
+                             @PathParam("eventId") String eventId) {
+
+    return handleRequest(headers, body, ui, Request.Type.PUT, createEventResource(eventId));
+  }
+
+  /**
+   * Handles: DELETE /ldap_sync_events/{eventId}
+   * Delete a specific view.
+   *
+   * @param headers  http headers
+   * @param ui       uri info
+   * @param eventId  event id
+   *
+   * @return information regarding the deleted event
+   */
+  @DELETE
+  @Path("{eventId}")
+  @Produces("text/plain")
+  public Response deleteEvent(@Context HttpHeaders headers, @Context UriInfo ui,
+                             @PathParam("eventId") String eventId) {
+
+    return handleRequest(headers, null, ui, Request.Type.DELETE, createEventResource(eventId));
+  }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Create a view resource.
+   *
+   * @param eventId the event id
+   *
+   * @return an event resource instance
+   */
+  private ResourceInstance createEventResource(String eventId) {
+    return createResource(Resource.Type.LdapSyncEvent,
+        Collections.singletonMap(Resource.Type.LdapSyncEvent, eventId));
+  }
+}

+ 9 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementController.java

@@ -24,6 +24,7 @@ import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.internal.RequestStageContainer;
 import org.apache.ambari.server.metadata.RoleCommandOrder;
 import org.apache.ambari.server.scheduler.ExecutionScheduleManager;
+import org.apache.ambari.server.security.ldap.LdapBatchDto;
 import org.apache.ambari.server.security.ldap.LdapSyncDto;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
@@ -705,7 +706,14 @@ public interface AmbariManagementController {
    * @param groups groups to be synchronized
    * @throws AmbariException if synchronization data was invalid
    */
-  public void synchronizeLdapUsersAndGroups(Set<String> users, Set<String> groups) throws AmbariException;
+  public LdapBatchDto synchronizeLdapUsersAndGroups(Set<String> users, Set<String> groups) throws AmbariException;
+
+  /**
+   * Checks if LDAP sync process is running.
+   *
+   * @return true if LDAP sync is in progress
+   */
+  public boolean isLdapSyncInProgress();
 
   /**
    * Get configurations which are specific for a cluster (!not a service).

+ 27 - 4
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java

@@ -227,6 +227,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   final private String serverDB;
   final private String mysqljdbcUrl;
 
+  private boolean ldapSyncInProgress;
+
   private Cache<ClusterRequest, ClusterResponse> clusterUpdateCache =
       CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
 
@@ -3675,9 +3677,30 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
   }
 
   @Override
-  public synchronized void synchronizeLdapUsersAndGroups(Set<String> users,
-      Set<String> groups) throws AmbariException {
-    final LdapBatchDto batchInfo = ldapDataPopulator.synchronizeLdapUsersAndGroups(users, groups);
-    this.users.processLdapSync(batchInfo);
+  public boolean isLdapSyncInProgress() {
+    return ldapSyncInProgress;
+  }
+
+  @Override
+  public synchronized LdapBatchDto synchronizeLdapUsersAndGroups(Set<String> users, Set<String> groups)
+      throws AmbariException {
+    ldapSyncInProgress = true;
+    try {
+      final LdapBatchDto batchInfo = new LdapBatchDto();
+      if (users != null) {
+        ldapDataPopulator.synchronizeLdapUsers(users, batchInfo);
+      } else {
+        ldapDataPopulator.synchronizeAllLdapUsers(batchInfo);
+      }
+      if (groups != null) {
+        ldapDataPopulator.synchronizeLdapGroups(groups, batchInfo);
+      } else {
+        ldapDataPopulator.synchronizeAllLdapGroups(batchInfo);
+      }
+      this.users.processLdapSync(batchInfo);
+      return batchInfo;
+    } finally {
+      ldapSyncInProgress = false;
+    }
   }
 }

+ 40 - 13
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ControllerResourceProvider.java

@@ -39,6 +39,7 @@ import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.security.ldap.LdapBatchDto;
 import org.apache.ambari.server.security.ldap.LdapGroupDto;
 import org.apache.ambari.server.security.ldap.LdapSyncDto;
 import org.apache.ambari.server.security.ldap.LdapUserDto;
@@ -58,6 +59,8 @@ class ControllerResourceProvider extends AbstractControllerResourceProvider {
   protected static final String CONTROLLER_LDAP_SYNCED_USERS_PROPERTY_ID  = PropertyHelper.getPropertyId("LDAP", "synced_users");
   protected static final String CONTROLLER_LDAP_SYNCED_GROUPS_PROPERTY_ID = PropertyHelper.getPropertyId("LDAP", "synced_groups");
 
+  protected static final String ALL_ENTRIES = "*";
+
   private static Set<String> pkPropertyIds = new HashSet<String>(
       Arrays.asList(new String[] { CONTROLLER_NAME_PROPERTY_ID }));
 
@@ -190,39 +193,63 @@ class ControllerResourceProvider extends AbstractControllerResourceProvider {
     }
 
     // one request per each controller
+    Set<Resource> resources = new HashSet<Resource>();
     for (final ControllerRequest controllerRequest: requests) {
-      modifyResources(new Command<Void>() {
+      Resource resource = modifyResources(new Command<Resource>() {
         @Override
-        public Void invoke() throws AmbariException {
+        public Resource invoke() throws AmbariException {
+          Resource resource = null;
           switch (ControllerType.getByName(controllerRequest.getName())) {
           case LDAP:
-            Set<String> users = new HashSet<String>();
+            resource = new ResourceImpl(Resource.Type.Controller);
+            Set<String> users = null;
             if (controllerRequest.getPropertyMap().containsKey(CONTROLLER_LDAP_SYNCED_USERS_PROPERTY_ID)) {
               final String userCsv = (String) controllerRequest.getPropertyMap().get(CONTROLLER_LDAP_SYNCED_USERS_PROPERTY_ID);
-              for (String user: userCsv.split(",")) {
-                if (StringUtils.isNotEmpty(user)) {
-                  users.add(user.toLowerCase());
+              if (!userCsv.trim().equals(ALL_ENTRIES)) {
+                users = new HashSet<String>();
+                for (String user: userCsv.split(",")) {
+                  if (StringUtils.isNotEmpty(user)) {
+                    users.add(user.toLowerCase());
+                  }
                 }
               }
             }
-            Set<String> groups = new HashSet<String>();
+            Set<String> groups = null;
             if (controllerRequest.getPropertyMap().containsKey(CONTROLLER_LDAP_SYNCED_GROUPS_PROPERTY_ID)) {
               final String groupCsv = (String) controllerRequest.getPropertyMap().get(CONTROLLER_LDAP_SYNCED_GROUPS_PROPERTY_ID);
-              for (String group: groupCsv.split(",")) {
-                if (StringUtils.isNotEmpty(group)) {
-                  groups.add(group.toLowerCase());
+              if (!groupCsv.trim().equals(ALL_ENTRIES)) {
+                groups = new HashSet<String>();
+                for (String group : groupCsv.split(",")) {
+                  if (StringUtils.isNotEmpty(group)) {
+                    groups.add(group.toLowerCase());
+                  }
                 }
               }
             }
-            getManagementController().synchronizeLdapUsersAndGroups(users, groups);
+            if (!getManagementController().isLdapSyncInProgress()) {
+              LdapBatchDto syncInfo = getManagementController().synchronizeLdapUsersAndGroups(users, groups);
+              resource.setProperty("Sync/status", "successful");
+              resource.setProperty("Sync/summary/Users/created", syncInfo.getUsersToBeCreated().size());
+              resource.setProperty("Sync/summary/Users/updated", syncInfo.getUsersToBecomeLdap().size());
+              resource.setProperty("Sync/summary/Users/removed", syncInfo.getUsersToBeRemoved().size());
+              resource.setProperty("Sync/summary/Groups/created", syncInfo.getGroupsToBeCreated().size());
+              resource.setProperty("Sync/summary/Groups/updated", syncInfo.getGroupsToBecomeLdap().size());
+              resource.setProperty("Sync/summary/Groups/removed", syncInfo.getGroupsToBeRemoved().size());
+              resource.setProperty("Sync/summary/Memberships/created", syncInfo.getMembershipToAdd().size());
+              resource.setProperty("Sync/summary/Memberships/removed", syncInfo.getMembershipToRemove().size());
+            } else {
+              resource.setProperty("Sync/status", "not started");
+              resource.setProperty("Sync/summary", "Another sync is already running");
+            }
             break;
           }
-          return null;
+          return resource;
         }
       });
+      resources.add(resource);
     }
 
-    return getRequestStatus(null);
+    return getRequestStatus(null, resources);
   }
 
   @Override

+ 2 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/DefaultProviderModule.java

@@ -79,6 +79,8 @@ public class DefaultProviderModule extends AbstractProviderModule {
         return new ViewPermissionResourceProvider();
       case ClusterPrivilege:
         return new ClusterPrivilegeResourceProvider();
+      case LdapSyncEvent:
+        return new LdapSyncEventResourceProvider();
       default:
         return AbstractControllerResourceProvider.getResourceProvider(type, propertyIds,
             keyPropertyIds, managementController);

+ 438 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProvider.java

@@ -0,0 +1,438 @@
+/**
+ * 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 org.apache.ambari.server.AmbariException;
+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.RequestStatus;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.orm.entities.LdapSyncEventEntity;
+import org.apache.ambari.server.orm.entities.LdapSyncSpecEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Resource provider for ldap sync events.
+ */
+public class LdapSyncEventResourceProvider extends AbstractResourceProvider {
+
+  /**
+   * Thread pool
+   */
+  private static ExecutorService executorService;
+
+  // TODO : Do we need to expose the thread pool params in configuration?
+  private final static int  THREAD_POOL_CORE_SIZE = 2;
+  private final static int  THREAD_POOL_MAX_SIZE  = 5;
+  private final static long THREAD_POOL_TIMEOUT   = 1000L;
+
+  /**
+   * Event property id constants.
+   */
+  public static final String EVENT_ID_PROPERTY_ID            = "Event/id";
+  public static final String EVENT_STATUS_PROPERTY_ID        = "Event/status";
+  public static final String EVENT_STATUS_DETAIL_PROPERTY_ID = "Event/status_detail";
+  public static final String EVENT_START_TIME_PROPERTY_ID    = "Event/sync_start_time";
+  public static final String EVENT_END_TIME_PROPERTY_ID      = "Event/sync_end_time";
+  public static final String USERS_FETCHED_PROPERTY_ID       = "Event/results/users_fetched";
+  public static final String USERS_CREATED_PROPERTY_ID       = "Event/results/users_created";
+  public static final String USERS_UPDATED_PROPERTY_ID       = "Event/results/users_updated";
+  public static final String USERS_REMOVED_PROPERTY_ID       = "Event/results/users_removed";
+  public static final String GROUPS_FETCHED_PROPERTY_ID      = "Event/results/groups_fetched";
+  public static final String GROUPS_CREATED_PROPERTY_ID      = "Event/results/groups_created";
+  public static final String GROUPS_UPDATED_PROPERTY_ID      = "Event/results/groups_updated";
+  public static final String GROUPS_REMOVED_PROPERTY_ID      = "Event/results/groups_removed";
+  public static final String MEMBERSHIPS_FETCHED_PROPERTY_ID = "Event/results/memberships_fetched";
+  public static final String MEMBERSHIPS_CREATED_PROPERTY_ID = "Event/results/memberships_created";
+  public static final String MEMBERSHIPS_UPDATED_PROPERTY_ID = "Event/results/memberships_updated";
+  public static final String EVENT_SPECS_PROPERTY_ID         = "Event/specs";
+
+  /**
+   * The key property ids for a event resource.
+   */
+  private static Map<Resource.Type, String> keyPropertyIds = new HashMap<Resource.Type, String>();
+  static {
+    keyPropertyIds.put(Resource.Type.LdapSyncEvent, EVENT_ID_PROPERTY_ID);
+  }
+
+  /**
+   * The property ids for a event resource.
+   */
+  private static Set<String> propertyIds = new HashSet<String>();
+
+  static {
+    propertyIds.add(EVENT_ID_PROPERTY_ID);
+    propertyIds.add(EVENT_STATUS_PROPERTY_ID);
+    propertyIds.add(EVENT_STATUS_DETAIL_PROPERTY_ID);
+    propertyIds.add(EVENT_START_TIME_PROPERTY_ID);
+    propertyIds.add(EVENT_END_TIME_PROPERTY_ID);
+    propertyIds.add(USERS_FETCHED_PROPERTY_ID);
+    propertyIds.add(USERS_CREATED_PROPERTY_ID);
+    propertyIds.add(USERS_UPDATED_PROPERTY_ID);
+    propertyIds.add(USERS_REMOVED_PROPERTY_ID);
+    propertyIds.add(GROUPS_FETCHED_PROPERTY_ID);
+    propertyIds.add(GROUPS_CREATED_PROPERTY_ID);
+    propertyIds.add(GROUPS_UPDATED_PROPERTY_ID);
+    propertyIds.add(GROUPS_REMOVED_PROPERTY_ID);
+    propertyIds.add(MEMBERSHIPS_FETCHED_PROPERTY_ID);
+    propertyIds.add(MEMBERSHIPS_CREATED_PROPERTY_ID);
+    propertyIds.add(MEMBERSHIPS_UPDATED_PROPERTY_ID);
+    propertyIds.add(EVENT_SPECS_PROPERTY_ID);
+  }
+
+  /**
+   * Spec property keys.
+   */
+  private static final String PRINCIPAL_TYPE_SPEC_KEY = "principal_type";
+  private static final String SYNC_TYPE_SPEC_KEY      = "sync_type";
+  private static final String NAMES_SPEC_KEY          = "names";
+
+  /**
+   * Map of all sync events.
+   */
+  private final Map<Long, LdapSyncEventEntity> events = new ConcurrentSkipListMap<Long, LdapSyncEventEntity>();
+
+  /**
+   * The queue of events to be processed.
+   */
+  private final Queue<LdapSyncEventEntity> eventQueue = new LinkedList<LdapSyncEventEntity>();
+
+  /**
+   * Indicates whether or not the events are currently being processed.
+   */
+  private volatile boolean processingEvents = false;
+
+  /**
+   * The next event id.
+   */
+  private AtomicLong nextEventId = new AtomicLong(1L);
+
+  /**
+   * The logger.
+   */
+  protected final static Logger LOG = LoggerFactory.getLogger(LdapSyncEventResourceProvider.class);
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct a event resource provider.
+   */
+  public LdapSyncEventResourceProvider() {
+    super(propertyIds, keyPropertyIds);
+  }
+
+
+  // ----- ResourceProvider --------------------------------------------------
+
+  @Override
+  public RequestStatus createResources(Request event)
+      throws SystemException, UnsupportedPropertyException,
+      ResourceAlreadyExistsException, NoSuchParentResourceException {
+    Set<LdapSyncEventEntity> newEvents = new HashSet<LdapSyncEventEntity>();
+
+    for (Map<String, Object> properties : event.getProperties()) {
+      newEvents.add(createResources(getCreateCommand(properties)));
+    }
+    notifyCreate(Resource.Type.ViewInstance, event);
+
+    Set<Resource> associatedResources = new HashSet<Resource>();
+    for (LdapSyncEventEntity eventEntity : newEvents) {
+      Resource resource = new ResourceImpl(Resource.Type.LdapSyncEvent);
+      resource.setProperty(EVENT_ID_PROPERTY_ID, eventEntity.getId());
+      associatedResources.add(resource);
+      synchronized (eventQueue) {
+        eventQueue.offer(eventEntity);
+      }
+    }
+
+    ensureEventProcessor();
+
+    return getRequestStatus(null, associatedResources);
+  }
+
+  @Override
+  public Set<Resource> getResources(Request event, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+
+    Set<Resource> resources    = new HashSet<Resource>();
+    Set<String>   requestedIds = getRequestPropertyIds(event, predicate);
+
+    for (LdapSyncEventEntity eventEntity : events.values()) {
+      resources.add(toResource(eventEntity, requestedIds));
+    }
+    return resources;
+  }
+
+  @Override
+  public RequestStatus updateResources(Request event, Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    throw new UnsupportedOperationException("Not supported.");
+  }
+
+  @Override
+  public RequestStatus deleteResources(Predicate predicate)
+      throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
+    modifyResources(getDeleteCommand(predicate));
+    notifyDelete(Resource.Type.ViewInstance, predicate);
+    return getRequestStatus(null);
+  }
+
+  @Override
+  public Map<Resource.Type, String> getKeyPropertyIds() {
+    return keyPropertyIds;
+  }
+
+
+  // ----- AbstractResourceProvider ------------------------------------------
+
+  @Override
+  protected Set<String> getPKPropertyIds() {
+    return new HashSet<String>(keyPropertyIds.values());
+  }
+
+
+  // ----- helper methods ----------------------------------------------------
+
+  /**
+   * Ensure that sync events are being processed.
+   */
+  protected void ensureEventProcessor() {
+
+    if (!processingEvents) {
+      synchronized (eventQueue) {
+        if (!processingEvents) {
+          processingEvents = true;
+          getExecutorService().submit(new Runnable() {
+            @Override
+            public void run() {
+              processSyncEvents();
+            }
+          });
+        }
+      }
+    }
+  }
+
+  // create a resource from the given event entity
+  private Resource toResource(LdapSyncEventEntity eventEntity, Set<String> requestedIds) {
+    Resource resource = new ResourceImpl(Resource.Type.LdapSyncEvent);
+
+    setResourceProperty(resource, EVENT_ID_PROPERTY_ID, eventEntity.getId(), requestedIds);
+    setResourceProperty(resource, EVENT_STATUS_PROPERTY_ID, eventEntity.getStatus().toString().toUpperCase(), requestedIds);
+    setResourceProperty(resource, EVENT_STATUS_DETAIL_PROPERTY_ID, eventEntity.getStatusDetail(), requestedIds);
+    setResourceProperty(resource, USERS_FETCHED_PROPERTY_ID, eventEntity.getUsersFetched(), requestedIds);
+    setResourceProperty(resource, USERS_CREATED_PROPERTY_ID, eventEntity.getUsersCreated(), requestedIds);
+    setResourceProperty(resource, USERS_UPDATED_PROPERTY_ID, eventEntity.getUsersUpdated(), requestedIds);
+    setResourceProperty(resource, USERS_REMOVED_PROPERTY_ID, eventEntity.getUsersRemoved(), requestedIds);
+    setResourceProperty(resource, GROUPS_FETCHED_PROPERTY_ID, eventEntity.getGroupsFetched(), requestedIds);
+    setResourceProperty(resource, GROUPS_CREATED_PROPERTY_ID, eventEntity.getGroupsCreated(), requestedIds);
+    setResourceProperty(resource, GROUPS_UPDATED_PROPERTY_ID, eventEntity.getGroupsUpdated(), requestedIds);
+    setResourceProperty(resource, GROUPS_REMOVED_PROPERTY_ID, eventEntity.getGroupsRemoved(), requestedIds);
+    setResourceProperty(resource, MEMBERSHIPS_FETCHED_PROPERTY_ID, eventEntity.getMembershipsFetched(), requestedIds);
+    setResourceProperty(resource, MEMBERSHIPS_CREATED_PROPERTY_ID, eventEntity.getMembershipsCreated(), requestedIds);
+    setResourceProperty(resource, MEMBERSHIPS_UPDATED_PROPERTY_ID, eventEntity.getMembershipsUpdated(), requestedIds);
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    List<LdapSyncSpecEntity> specList = eventEntity.getSpecs();
+
+    for (LdapSyncSpecEntity spec : specList) {
+
+      Map<String, String> specMap = new HashMap<String, String>();
+
+      specMap.put(PRINCIPAL_TYPE_SPEC_KEY, spec.getPrincipalType().toString().toLowerCase());
+      specMap.put(SYNC_TYPE_SPEC_KEY, spec.getSyncType().toString().toLowerCase());
+
+      List<String> names = spec.getPrincipalNames();
+
+      if (!names.isEmpty()) {
+        specMap.put(NAMES_SPEC_KEY, names.toString().replace("[", "").replace("]", "").replace(", ", ","));
+      }
+      specs.add(specMap);
+    }
+    setResourceProperty(resource, EVENT_SPECS_PROPERTY_ID, specs, requestedIds);
+
+    setResourceProperty(resource, EVENT_START_TIME_PROPERTY_ID, eventEntity.getStartTime(), requestedIds);
+    setResourceProperty(resource, EVENT_END_TIME_PROPERTY_ID, eventEntity.getEndTime(), requestedIds);
+
+    return resource;
+  }
+
+  // create a event entity from the given set of properties
+  private LdapSyncEventEntity toEntity(Map<String, Object> properties) {
+    LdapSyncEventEntity      entity   = new LdapSyncEventEntity(getNextEventId());
+    List<LdapSyncSpecEntity> specList = new LinkedList<LdapSyncSpecEntity>();
+
+    Set<Map<String, String>> specs = (Set<Map<String, String>>) properties.get(EVENT_SPECS_PROPERTY_ID);
+
+    for (Map<String, String> specMap : specs) {
+
+      LdapSyncSpecEntity.SyncType      syncType      = null;
+      LdapSyncSpecEntity.PrincipalType principalType = null;
+
+      List<String> principalNames = Collections.emptyList();
+
+      for (Map.Entry<String, String> entry : specMap.entrySet()) {
+        String key = entry.getKey();
+        if (key.equalsIgnoreCase(PRINCIPAL_TYPE_SPEC_KEY)) {
+          principalType = LdapSyncSpecEntity.PrincipalType.valueOfIgnoreCase(entry.getValue());
+
+        } else if (key.equalsIgnoreCase(SYNC_TYPE_SPEC_KEY)) {
+          syncType = LdapSyncSpecEntity.SyncType.valueOfIgnoreCase(entry.getValue());
+
+        } else if (key.equalsIgnoreCase(NAMES_SPEC_KEY)) {
+          String names = entry.getValue();
+          principalNames = Arrays.asList(names.split("\\s*,\\s*"));
+        } else {
+          throw new IllegalArgumentException("Unknown spec key " + key + ".");
+        }
+      }
+
+      if (syncType == null || principalType == null) {
+        throw new IllegalArgumentException("LDAP event spec must include both sync-type and principal-type.");
+      }
+
+      LdapSyncSpecEntity spec = new LdapSyncSpecEntity(principalType, syncType, principalNames);
+      specList.add(spec);
+    }
+    entity.setSpecs(specList);
+
+    return entity;
+  }
+
+  // get the next event id
+  private long getNextEventId() {
+    return nextEventId.getAndIncrement();
+  }
+
+  // Create a create command with all properties set
+  private Command<LdapSyncEventEntity> getCreateCommand(final Map<String, Object> properties) {
+    return new Command<LdapSyncEventEntity>() {
+      @Override
+      public LdapSyncEventEntity invoke() throws AmbariException {
+
+        LdapSyncEventEntity eventEntity = toEntity(properties);
+
+        events.put(eventEntity.getId(), eventEntity);
+
+        return eventEntity;
+      }
+    };
+  }
+
+  // Create a delete command with the given predicate
+  private Command<Void> getDeleteCommand(final Predicate predicate) {
+    return new Command<Void>() {
+      @Override
+      public Void invoke() throws AmbariException {
+        Set<String>  requestedIds = getRequestPropertyIds(PropertyHelper.getReadRequest(), predicate);
+
+        Set<LdapSyncEventEntity> entities = new HashSet<LdapSyncEventEntity>();
+
+        for (LdapSyncEventEntity entity : events.values()){
+              Resource resource = toResource(entity, requestedIds);
+              if (predicate == null || predicate.evaluate(resource)) {
+                entities.add(entity);
+              }
+        }
+        for (LdapSyncEventEntity entity : entities) {
+          events.remove(entity.getId());
+        }
+        return null;
+      }
+    };
+  }
+
+  // Get the ldap sync thread pool
+  private static synchronized ExecutorService getExecutorService() {
+    if (executorService == null) {
+      LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
+
+      ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+          THREAD_POOL_CORE_SIZE,
+          THREAD_POOL_MAX_SIZE,
+          THREAD_POOL_TIMEOUT,
+          TimeUnit.MILLISECONDS,
+          queue);
+
+      threadPoolExecutor.allowCoreThreadTimeOut(true);
+      executorService = threadPoolExecutor;
+    }
+    return executorService;
+  }
+
+  // Process any queued up sync events
+  private void processSyncEvents() {
+
+    while (processingEvents) {
+      LdapSyncEventEntity event;
+      synchronized (eventQueue) {
+        event = eventQueue.poll();
+        if (event == null) {
+          processingEvents = false;
+          return;
+        }
+      }
+
+      event.setStatus(LdapSyncEventEntity.Status.Running);
+      event.setStatusDetail("Running LDAP sync.");
+      event.setStartTime(System.currentTimeMillis());
+
+      try {
+
+        // TODO : call back end code to perform LDAP sync
+
+        // TODO : update event resource with results of sync
+
+        event.setEndTime(System.currentTimeMillis());
+        event.setStatus(LdapSyncEventEntity.Status.Complete);
+        event.setStatusDetail("Completed LDAP sync.");
+      } catch (Exception e) {
+        String msg = "Caught exception running LDAP sync. ";
+        event.setStatus(LdapSyncEventEntity.Status.Error);
+        event.setStatusDetail(msg + e.getMessage());
+        LOG.error(msg, e);
+      }
+    }
+  }
+}

+ 4 - 0
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/URLStreamProvider.java

@@ -218,4 +218,8 @@ public class URLStreamProvider implements StreamProvider {
  
     return connection;
   }
+
+  public AppCookieManager getAppCookieManager() {
+    return appCookieManager;
+  }
 }

+ 3 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/spi/Resource.java

@@ -124,7 +124,8 @@ public interface Resource {
     ViewPermission,
     Controller,
     ClientConfig,
-    StackLevelConfiguration;
+    StackLevelConfiguration,
+    LdapSyncEvent;
 
     /**
      * Get the {@link Type} that corresponds to this InternalType.
@@ -210,6 +211,7 @@ public interface Resource {
     public static final Type Controller = InternalType.Controller.getType();
     public static final Type ClientConfig = InternalType.ClientConfig.getType();
     public static final Type StackLevelConfiguration = InternalType.StackLevelConfiguration.getType();
+    public static final Type LdapSyncEvent = InternalType.LdapSyncEvent.getType();
 
     /**
      * The type name.

+ 284 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntity.java

@@ -0,0 +1,284 @@
+/**
+ * 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.orm.entities;
+
+import java.util.List;
+
+/**
+ * LDAP sync event entity.
+ */
+public class LdapSyncEventEntity {
+
+  /**
+   * The unique id.
+   */
+  private final long id;
+
+  /**
+   * The status of this event.
+   */
+  private Status status;
+
+  /**
+   * The detail status message for this event.
+   */
+  private String statusDetail;
+
+  /**
+   * Sync event times.
+   */
+  private long startTime;
+  private long endTime;
+
+  /**
+   * Results of sync event.
+   */
+  private Integer usersFetched;
+  private Integer usersCreated;
+  private Integer usersUpdated;
+  private Integer usersRemoved;
+  private Integer groupsFetched;
+  private Integer groupsCreated;
+  private Integer groupsUpdated;
+  private Integer groupsRemoved;
+  private Integer membershipsFetched;
+  private Integer membershipsCreated;
+  private Integer membershipsUpdated;
+
+  /**
+   * The specifications that define the sync event.
+   */
+  private List<LdapSyncSpecEntity> specs;
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an LdapSyncEventEntity.
+   *
+   * @param id  the unique id
+   */
+  public LdapSyncEventEntity(long id) {
+    this.id = id;
+    this.status = Status.Pending;
+  }
+
+
+  // ----- LdapSyncEventEntity -----------------------------------------------
+
+  /**
+   * Get the id.
+   *
+   * @return the id
+   */
+  public long getId() {
+    return id;
+  }
+
+  /**
+   * Get the event status.
+   *
+   * @return the status
+   */
+  public Status getStatus() {
+    return status;
+  }
+
+  /**
+   * Set the event status.
+   *
+   * @param status  the status
+   */
+  public void setStatus(Status status) {
+    this.status = status;
+  }
+
+  /**
+   * Get the event status detail message.
+   *
+   * @return the event detail
+   */
+  public String getStatusDetail() {
+    return statusDetail;
+  }
+
+  /**
+   * Set the event status detail message.
+   *
+   * @param statusDetail  the event status detail message
+   */
+  public void setStatusDetail(String statusDetail) {
+    this.statusDetail = statusDetail;
+  }
+
+  /**
+   * Get the event specifications.
+   *
+   * @return the event specs
+   */
+  public List<LdapSyncSpecEntity> getSpecs() {
+    return specs;
+  }
+
+  /**
+   * Set the event specifications.
+   *
+   * @param specs  the event specifications
+   */
+  public void setSpecs(List<LdapSyncSpecEntity> specs) {
+    this.specs = specs;
+  }
+
+  /**
+   * Get the time that the sync started.
+   *
+   * @return the sync start time (millis)
+   */
+  public long getStartTime() {
+    return startTime;
+  }
+
+  /**
+   * Set the sync start time.
+   *
+   * @param startTime  the sync start time (millis)
+   */
+  public void setStartTime(long startTime) {
+    this.startTime = startTime;
+  }
+
+  /**
+   * Get the time the the sync event ended.
+   *
+   * @return  the end time (millis)
+   */
+  public long getEndTime() {
+    return endTime;
+  }
+
+  /**
+   * Set the sync end time.
+   *
+   * @param endTime  the end time (millis)
+   */
+  public void setEndTime(long endTime) {
+    this.endTime = endTime;
+  }
+
+  public Integer getUsersFetched() {
+    return usersFetched;
+  }
+
+  public void setUsersFetched(Integer usersFetched) {
+    this.usersFetched = usersFetched;
+  }
+
+  public Integer getUsersCreated() {
+    return usersCreated;
+  }
+
+  public void setUsersCreated(Integer usersCreated) {
+    this.usersCreated = usersCreated;
+  }
+
+  public Integer getUsersUpdated() {
+    return usersUpdated;
+  }
+
+  public void setUsersUpdated(Integer usersUpdated) {
+    this.usersUpdated = usersUpdated;
+  }
+
+  public Integer getUsersRemoved() {
+    return usersRemoved;
+  }
+
+  public void setUsersRemoved(Integer usersRemoved) {
+    this.usersRemoved = usersRemoved;
+  }
+
+  public Integer getGroupsFetched() {
+    return groupsFetched;
+  }
+
+  public void setGroupsFetched(Integer groupsFetched) {
+    this.groupsFetched = groupsFetched;
+  }
+
+  public Integer getGroupsCreated() {
+    return groupsCreated;
+  }
+
+  public void setGroupsCreated(Integer groupsCreated) {
+    this.groupsCreated = groupsCreated;
+  }
+
+  public Integer getGroupsUpdated() {
+    return groupsUpdated;
+  }
+
+  public void setGroupsUpdated(Integer groupsUpdated) {
+    this.groupsUpdated = groupsUpdated;
+  }
+
+  public Integer getGroupsRemoved() {
+    return groupsRemoved;
+  }
+
+  public void setGroupsRemoved(Integer groupsRemoved) {
+    this.groupsRemoved = groupsRemoved;
+  }
+
+  public Integer getMembershipsFetched() {
+    return membershipsFetched;
+  }
+
+  public void setMembershipsFetched(Integer membershipsFetched) {
+    this.membershipsFetched = membershipsFetched;
+  }
+
+  public Integer getMembershipsCreated() {
+    return membershipsCreated;
+  }
+
+  public void setMembershipsCreated(Integer membershipsCreated) {
+    this.membershipsCreated = membershipsCreated;
+  }
+
+  public Integer getMembershipsUpdated() {
+    return membershipsUpdated;
+  }
+
+  public void setMembershipsUpdated(Integer membershipsUpdated) {
+    this.membershipsUpdated = membershipsUpdated;
+  }
+
+
+  // ----- enum : Status -----------------------------------------------------
+
+  /**
+   * LDAP sync event status
+   */
+  public enum Status {
+    Pending,
+    Running,
+    Error,
+    Complete
+  }
+}

+ 143 - 0
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntity.java

@@ -0,0 +1,143 @@
+/**
+ * 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.orm.entities;
+
+import java.util.List;
+
+/**
+ * LDAP sync specification entity.
+ */
+public class LdapSyncSpecEntity {
+
+  /**
+   * The principal type.
+   */
+  private PrincipalType principalType;
+
+  /**
+   * The sync type.
+   */
+  private SyncType syncType;
+
+  /**
+   * The list of principal names.
+   */
+  private List<String> principalNames;
+
+
+  // ----- Constructors ------------------------------------------------------
+
+  /**
+   * Construct an LdapSyncSpecEntity.
+   *
+   * @param principalType   the principal type
+   * @param syncType        the sync type
+   * @param principalNames  the list of principal names.
+   */
+  public LdapSyncSpecEntity(PrincipalType principalType, SyncType syncType, List<String> principalNames) {
+    this.principalType = principalType;
+    this.syncType = syncType;
+    this.principalNames = principalNames;
+
+    if (syncType == SyncType.Specific) {
+      if (principalNames.isEmpty()) {
+        throw new IllegalArgumentException("Missing principal names for " + syncType + " sync-type.");
+      }
+    } else {
+      if (!principalNames.isEmpty()) {
+        throw new IllegalArgumentException("Principal names should not be specified for " + syncType + " sync-type.");
+      }
+    }
+  }
+
+
+  // ----- LdapSyncSpecEntity ------------------------------------------------
+
+  /**
+   * Get the principal type.
+   *
+   * @return the principal type
+   */
+  public PrincipalType getPrincipalType() {
+    return principalType;
+  }
+
+  /**
+   * Get the sync type.
+   *
+   * @return the sync type
+   */
+  public SyncType getSyncType() {
+    return syncType;
+  }
+
+  /**
+   * Get a list of principal names.
+   *
+   * @return the list of principal names.
+   */
+  public List<String> getPrincipalNames() {
+    return principalNames;
+  }
+
+
+  // ----- enum : PrincipalType ----------------------------------------------
+
+  /**
+   * LDAP sync principal type.
+   */
+  public enum PrincipalType {
+    Users,
+    Groups;
+
+    /**
+     * Get the enum value for the given principal type name string, ignoring case.
+     *
+     * @param type  the principal type name
+     *
+     * @return the enum value for the given type name
+     */
+    public static PrincipalType valueOfIgnoreCase(String type) {
+      return valueOf(Character.toUpperCase(type.charAt(0)) + type.substring(1).toLowerCase());
+    }
+  }
+
+
+  // ----- enum : SyncType ---------------------------------------------------
+
+  /**
+   * LDAP sync type.
+   */
+  public enum SyncType {
+    All,
+    Existing,
+    Specific;
+
+    /**
+     * Get the enum value for the given sync type name string, ignoring case.
+     *
+     * @param type  the sync type name
+     *
+     * @return the enum value for the given type name
+     */
+    public static SyncType valueOfIgnoreCase(String type) {
+      return valueOf(Character.toUpperCase(type.charAt(0)) + type.substring(1).toLowerCase());
+    }
+  }
+}

+ 14 - 3
ambari-server/src/main/java/org/apache/ambari/server/proxy/ProxyService.java

@@ -20,6 +20,7 @@ package org.apache.ambari.server.proxy;
 
 import com.google.gson.Gson;
 import org.apache.ambari.server.controller.internal.URLStreamProvider;
+import org.apache.ambari.server.view.ImpersonatorSettingImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -36,6 +37,7 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -48,9 +50,9 @@ import java.util.HashMap;
 @Path("/")
 public class ProxyService {
 
-  private static final int URL_CONNECT_TIMEOUT = 20000;
-  private static final int URL_READ_TIMEOUT = 15000;
-  private static final int HTTP_ERROR_RANGE_START = Response.Status.BAD_REQUEST.getStatusCode();
+  public static final int URL_CONNECT_TIMEOUT = 20000;
+  public static final int URL_READ_TIMEOUT = 15000;
+  public static final int HTTP_ERROR_RANGE_START = Response.Status.BAD_REQUEST.getStatusCode();
 
   private static final String REQUEST_TYPE_GET = "GET";
   private static final String REQUEST_TYPE_POST = "POST";
@@ -59,6 +61,7 @@ public class ProxyService {
   private static final String QUERY_PARAMETER_URL = "url=";
   private static final String AMBARI_PROXY_PREFIX = "AmbariProxy-";
   private static final String ERROR_PROCESSING_URL = "Error occurred during processing URL ";
+  private static final String INVALID_PARAM_IN_URL = "Invalid query params found in URL ";
 
   private final static Logger LOG = LoggerFactory.getLogger(ProxyService.class);
 
@@ -90,6 +93,14 @@ public class ProxyService {
     String query = ui.getRequestUri().getQuery();
     if (query != null && query.indexOf(QUERY_PARAMETER_URL) != -1) {
       String url = query.replaceFirst(QUERY_PARAMETER_URL, "");
+
+      MultivaluedMap<String, String> m = ui.getQueryParameters();
+      if (m.containsKey(ImpersonatorSettingImpl.DEFAULT_DO_AS_PARAM)) {              // Case doesn't matter
+        LOG.error(INVALID_PARAM_IN_URL + url);
+        return Response.status(Response.Status.BAD_REQUEST.getStatusCode()).type(MediaType.TEXT_PLAIN).
+            entity(INVALID_PARAM_IN_URL).build();
+      }
+      
       try {
         HttpURLConnection connection = urlStreamProvider.processURL(url, requestType, body, getHeaderParamsToForward(headers));
         int responseCode = connection.getResponseCode();

+ 10 - 7
ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java

@@ -638,13 +638,6 @@ public class Users {
     userDAO.create(usersToCreate);
     groupDAO.create(groupsToCreate);
 
-    // remove membership
-    final Set<MemberEntity> membersToRemove = new HashSet<MemberEntity>();
-    for (LdapUserGroupMemberDto member: batchInfo.getMembershipToRemove()) {
-      membersToRemove.add(memberDAO.findByUserAndGroup(member.getUserName(), member.getGroupName()));
-    }
-    memberDAO.remove(membersToRemove);
-
     // create membership
     final Set<MemberEntity> membersToCreate = new HashSet<MemberEntity>();
     final Set<GroupEntity> groupsToUpdate = new HashSet<GroupEntity>();
@@ -660,6 +653,16 @@ public class Users {
     memberDAO.create(membersToCreate);
     groupDAO.merge(groupsToUpdate); // needed for Derby DB as it doesn't fetch newly added members automatically
 
+    // remove membership
+    final Set<MemberEntity> membersToRemove = new HashSet<MemberEntity>();
+    for (LdapUserGroupMemberDto member: batchInfo.getMembershipToRemove()) {
+      MemberEntity memberEntity = memberDAO.findByUserAndGroup(member.getUserName(), member.getGroupName());
+      if (memberEntity != null) {
+        membersToRemove.add(memberEntity);
+      }
+    }
+    memberDAO.remove(membersToRemove);
+
     // clear cached entities
     entityManagerProvider.get().getEntityManagerFactory().getCache().evictAll();
   }

+ 216 - 52
ambari-server/src/main/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulator.java

@@ -42,7 +42,11 @@ import org.springframework.ldap.core.ContextMapper;
 import org.springframework.ldap.core.DirContextAdapter;
 import org.springframework.ldap.core.LdapTemplate;
 import org.springframework.ldap.core.support.LdapContextSource;
+import org.springframework.ldap.filter.AndFilter;
 import org.springframework.ldap.filter.EqualsFilter;
+import org.springframework.ldap.filter.Filter;
+import org.springframework.ldap.filter.LikeFilter;
+import org.springframework.ldap.filter.OrFilter;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
 import com.google.inject.Inject;
@@ -80,6 +84,7 @@ public class AmbariLdapDataPopulator {
   public AmbariLdapDataPopulator(Configuration configuration, Users users) {
     this.configuration = configuration;
     this.users = users;
+    this.ldapServerProperties = configuration.getLdapServerProperties();
   }
 
   /**
@@ -143,69 +148,136 @@ public class AmbariLdapDataPopulator {
   }
 
   /**
-   * Performs synchronization of given sets of usernames and groupnames.
+   * Performs synchronization of all groups.
    *
-   * @param users set of users to synchronize
-   * @param groups set of groups to synchronize
    * @throws AmbariException if synchronization failed for any reason
    */
-  public LdapBatchDto synchronizeLdapUsersAndGroups(Set<String> users,
-      Set<String> groups) throws AmbariException {
-    final LdapBatchDto batchInfo = new LdapBatchDto();
+  public LdapBatchDto synchronizeAllLdapGroups(LdapBatchDto batchInfo) throws AmbariException {
 
-    // validate request
-    final Set<LdapUserDto> externalUsers = getExternalLdapUserInfo();
-    final Map<String, LdapUserDto> externalUsersMap = new HashMap<String, LdapUserDto>();
-    for (LdapUserDto user: externalUsers) {
-      externalUsersMap.put(user.getUserName(), user);
+    Set<LdapGroupDto> externalLdapGroupInfo = getExternalLdapGroupInfo();
+
+    final Map<String, Group> internalGroupsMap = getInternalGroups();
+    final Map<String, User> internalUsersMap = getInternalUsers();
+
+    for (LdapGroupDto groupDto : externalLdapGroupInfo) {
+      String groupName = groupDto.getGroupName();
+      if (internalGroupsMap.containsKey(groupName)) {
+        final Group group = internalGroupsMap.get(groupName);
+        if (!group.isLdapGroup()) {
+          batchInfo.getGroupsToBecomeLdap().add(groupName);
+        }
+        internalGroupsMap.remove(groupName);
+      } else {
+        batchInfo.getGroupsToBeCreated().add(groupName);
+      }
+      refreshGroupMembers(batchInfo, groupDto, internalUsersMap);
     }
-    for (String user : users) {
-      if (!externalUsersMap.containsKey(user)) {
-        throw new AmbariException("Couldn't sync LDAP user " + user
-            + ", it doesn't exist");
+    for (Entry<String, Group> internalGroup : internalGroupsMap.entrySet()) {
+      if (internalGroup.getValue().isLdapGroup()) {
+        batchInfo.getGroupsToBeRemoved().add(internalGroup.getValue().getGroupName());
       }
     }
-    final Set<LdapGroupDto> externalGroups = getExternalLdapGroupInfo();
-    final Map<String, LdapGroupDto> externalGroupsMap = new HashMap<String, LdapGroupDto>();
-    for (LdapGroupDto group: externalGroups) {
-      externalGroupsMap.put(group.getGroupName(), group);
+
+    return batchInfo;
+  }
+
+  /**
+   * Performs synchronization of given sets of all users.
+   *
+   * @throws AmbariException if synchronization failed for any reason
+   */
+  public LdapBatchDto synchronizeAllLdapUsers(LdapBatchDto batchInfo) throws AmbariException {
+
+    Set<LdapUserDto> externalLdapUserInfo = getExternalLdapUserInfo();
+    Map<String, User> internalUsersMap = getInternalUsers();
+
+    for (LdapUserDto userDto : externalLdapUserInfo) {
+      String userName = userDto.getUserName();
+      if (internalUsersMap.containsKey(userName)) {
+        final User user = internalUsersMap.get(userName);
+        if (user != null && !user.isLdapUser()) {
+          batchInfo.getUsersToBecomeLdap().add(userName);
+        }
+        internalUsersMap.remove(userName);
+      } else {
+        batchInfo.getUsersToBeCreated().add(userName);
+      }
+    }
+    for (Entry<String, User> internalUser : internalUsersMap.entrySet()) {
+      if (internalUser.getValue().isLdapUser()) {
+        batchInfo.getUsersToBeRemoved().add(internalUser.getValue().getUserName());
+      }
     }
+
+    return batchInfo;
+  }
+
+  /**
+   * Performs synchronization of given set of groupnames.
+   *
+   * @param groups set of groups to synchronize
+   * @throws AmbariException if synchronization failed for any reason
+   */
+  public LdapBatchDto synchronizeLdapGroups(Set<String> groups, LdapBatchDto batchInfo) throws AmbariException {
+
+    final Set<LdapGroupDto> specifiedGroups = new HashSet<LdapGroupDto>();
     for (String group : groups) {
-      if (!externalGroupsMap.containsKey(group)) {
+      Set<LdapGroupDto> groupDtos = getLdapGroups(group);
+      if (groupDtos.isEmpty()) {
         throw new AmbariException("Couldn't sync LDAP group " + group
             + ", it doesn't exist");
       }
+      specifiedGroups.addAll(groupDtos);
     }
 
     final Map<String, Group> internalGroupsMap = getInternalGroups();
     final Map<String, User> internalUsersMap = getInternalUsers();
 
-    // processing groups
-    for (String groupName : groups) {
+    for (LdapGroupDto groupDto : specifiedGroups) {
+      String groupName = groupDto.getGroupName();
       if (internalGroupsMap.containsKey(groupName)) {
         final Group group = internalGroupsMap.get(groupName);
         if (!group.isLdapGroup()) {
           batchInfo.getGroupsToBecomeLdap().add(groupName);
         }
+        internalGroupsMap.remove(groupName);
       } else {
         batchInfo.getGroupsToBeCreated().add(groupName);
       }
-      refreshGroupMembers(batchInfo, externalGroupsMap.get(groupName), internalUsersMap, externalUsers);
-      internalGroupsMap.remove(groupName);
+      refreshGroupMembers(batchInfo, groupDto, internalUsersMap);
     }
-    for (Entry<String, Group> internalGroup : internalGroupsMap.entrySet()) {
-      if (internalGroup.getValue().isLdapGroup()) {
-        batchInfo.getGroupsToBeRemoved().add(internalGroup.getValue().getGroupName());
+
+    return batchInfo;
+  }
+
+  /**
+   * Performs synchronization of given set of usernames.
+   *
+   * @param users set of users to synchronize
+   * @throws AmbariException if synchronization failed for any reason
+   */
+  public LdapBatchDto synchronizeLdapUsers(Set<String> users, LdapBatchDto batchInfo) throws AmbariException {
+
+    final Set<LdapUserDto> specifiedUsers = new HashSet<LdapUserDto>();
+
+    for (String user : users) {
+      Set<LdapUserDto> userDtos = getLdapUsers(user);
+      if (userDtos.isEmpty()) {
+        throw new AmbariException("Couldn't sync LDAP user " + user
+            + ", it doesn't exist");
       }
+      specifiedUsers.addAll(userDtos);
     }
 
-    // processing users
-    for (String userName : users) {
+    final Map<String, User> internalUsersMap = getInternalUsers();
+    for (LdapUserDto userDto : specifiedUsers) {
+      String userName = userDto.getUserName();
       if (internalUsersMap.containsKey(userName)) {
         final User user = internalUsersMap.get(userName);
         if (user != null && !user.isLdapUser()) {
           batchInfo.getUsersToBecomeLdap().add(userName);
         }
+        internalUsersMap.remove(userName);
       } else {
         batchInfo.getUsersToBeCreated().add(userName);
       }
@@ -214,33 +286,75 @@ public class AmbariLdapDataPopulator {
     return batchInfo;
   }
 
+  /**
+   * Performs synchronization of existent users and groups.
+   *
+   * @throws AmbariException if synchronization failed for any reason
+   */
+  public LdapBatchDto synchronizeExistingLdapGroups(LdapBatchDto batchInfo) throws AmbariException {
+    final Map<String, Group> internalGroupsMap = getInternalGroups();
+    final Map<String, User> internalUsersMap = getInternalUsers();
+
+    for (Group group : internalGroupsMap.values()) {
+      if (group.isLdapGroup()) {
+        Set<LdapGroupDto> groupDtos = getLdapGroups(group.getGroupName());
+        if (groupDtos.isEmpty()) {
+          batchInfo.getGroupsToBeRemoved().add(group.getGroupName());
+        } else {
+          LdapGroupDto groupDto = groupDtos.iterator().next();
+          refreshGroupMembers(batchInfo, groupDto, internalUsersMap);
+        }
+      }
+    }
+
+    return batchInfo;
+  }
+
+  /**
+   * Performs synchronization of existent users and groups.
+   *
+   * @throws AmbariException if synchronization failed for any reason
+   */
+  public LdapBatchDto synchronizeExistingLdapUsers(LdapBatchDto batchInfo) throws AmbariException {
+    final Map<String, User> internalUsersMap = getInternalUsers();
+
+    for (User user : internalUsersMap.values()) {
+      if (user.isLdapUser()) {
+        Set<LdapUserDto> userDtos = getLdapUsers(user.getUserName());
+        if (userDtos.isEmpty()) {
+          batchInfo.getUsersToBeRemoved().add(user.getUserName());
+        }
+      }
+    }
+
+    return batchInfo;
+  }
+
   /**
    * Check group members of the synced group: add missing ones and remove the ones absent in external LDAP.
    *
-   * @param groupName group name
+   * @param batchInfo batch update object
+   * @param group ldap group
    * @param internalUsers map of internal users
-   * @param externalUsers set of external users
    * @throws AmbariException if group refresh failed
    */
-  protected void refreshGroupMembers(LdapBatchDto batchInfo, LdapGroupDto group, Map<String, User> internalUsers, Set<LdapUserDto> externalUsers) throws AmbariException {
-    final Set<String> externalMembers = new HashSet<String>();
+  protected void refreshGroupMembers(LdapBatchDto batchInfo, LdapGroupDto group, Map<String, User> internalUsers) throws AmbariException {
+    Set<String> externalMembers = new HashSet<String>();
     for (String memberAttribute: group.getMemberAttributes()) {
-      for (LdapUserDto externalUser: externalUsers) {
-        // memberAttribute may be either DN or UID, check both
-        if (externalUser.getDn().equals(memberAttribute) || externalUser.getUid().equals(memberAttribute)) {
-          externalMembers.add(externalUser.getUserName());
-          break;
-        }
+      LdapUserDto groupMember = getLdapUserByMemberAttr(memberAttribute);
+      if (groupMember != null) {
+        externalMembers.add(groupMember.getUserName());
       }
     }
-    final Map<String, User> internalMembers = getInternalMembers(group.getGroupName());
+    String groupName = group.getGroupName();
+    final Map<String, User> internalMembers = getInternalMembers(groupName);
     for (String externalMember: externalMembers) {
       if (internalUsers.containsKey(externalMember)) {
         final User user = internalUsers.get(externalMember);
         if (user == null) {
           // user is fresh and is already added to batch info
           if (!internalMembers.containsKey(externalMember)) {
-            batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(group.getGroupName(), externalMember));
+            batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(groupName, externalMember));
           }
           continue;
         }
@@ -248,21 +362,47 @@ public class AmbariLdapDataPopulator {
           batchInfo.getUsersToBecomeLdap().add(externalMember);
         }
         if (!internalMembers.containsKey(externalMember)) {
-          batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(group.getGroupName(), externalMember));
+          batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(groupName, externalMember));
         }
         internalMembers.remove(externalMember);
       } else {
         batchInfo.getUsersToBeCreated().add(externalMember);
-        batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(group.getGroupName(), externalMember));
-        internalUsers.put(externalMember, null);
+        batchInfo.getMembershipToAdd().add(new LdapUserGroupMemberDto(groupName, externalMember));
       }
     }
     for (Entry<String, User> userToBeUnsynced: internalMembers.entrySet()) {
       final User user = userToBeUnsynced.getValue();
-      batchInfo.getMembershipToRemove().add(new LdapUserGroupMemberDto(group.getGroupName(), user.getUserName()));
+      batchInfo.getMembershipToRemove().add(new LdapUserGroupMemberDto(groupName, user.getUserName()));
     }
   }
 
+  protected Set<LdapGroupDto> getLdapGroups(String groupName) {
+    Filter groupObjectFilter = new EqualsFilter("objectClass",
+        ldapServerProperties.getGroupObjectClass());
+    Filter groupNameFilter = new LikeFilter(ldapServerProperties.getGroupNamingAttr(), groupName);
+    Set<LdapGroupDto> filteredLdapGroups = getFilteredLdapGroups(groupObjectFilter, groupNameFilter);
+    return filteredLdapGroups;
+  }
+
+  protected Set<LdapUserDto> getLdapUsers(String username) {
+    Filter userObjectFilter = new EqualsFilter("objectClass", ldapServerProperties.getUserObjectClass());
+    Filter userNameFilter = new LikeFilter(ldapServerProperties.getUsernameAttribute(), username);
+    Set<LdapUserDto> filteredLdapUsers = getFilteredLdapUsers(userObjectFilter, userNameFilter);
+    return filteredLdapUsers;
+  }
+
+  protected LdapUserDto getLdapUserByMemberAttr(String memberAttribute) {
+    // memberAttribute may be either DN or UID, check both
+    Filter userObjectFilter = new EqualsFilter("objectClass", ldapServerProperties.getUserObjectClass());
+    Filter dnFilter = new EqualsFilter("dn", memberAttribute);
+    Filter uidFilter = new EqualsFilter("uid", memberAttribute);
+    OrFilter orFilter = new OrFilter();
+    orFilter.or(dnFilter);
+    orFilter.or(uidFilter);
+    Set<LdapUserDto> filteredLdapUsers = getFilteredLdapUsers(userObjectFilter, orFilter);
+    return (filteredLdapUsers.isEmpty()) ? null : filteredLdapUsers.iterator().next();
+  }
+
   /**
    * Removes synced users which are not present in any of group.
    *
@@ -285,12 +425,24 @@ public class AmbariLdapDataPopulator {
    * @return set of info about LDAP groups
    */
   protected Set<LdapGroupDto> getExternalLdapGroupInfo() {
+    EqualsFilter groupObjectFilter = new EqualsFilter("objectClass",
+        ldapServerProperties.getGroupObjectClass());
+    return getFilteredLdapGroups(groupObjectFilter);
+  }
+
+  private Set<LdapGroupDto> getFilteredLdapGroups(Filter...filters) {
+    AndFilter andFilter = new AndFilter();
+    for (Filter filter : filters) {
+      andFilter.and(filter);
+    }
+    return getFilteredLdapGroups(andFilter);
+  }
+
+  private Set<LdapGroupDto> getFilteredLdapGroups(Filter filter) {
     final Set<LdapGroupDto> groups = new HashSet<LdapGroupDto>();
     final LdapTemplate ldapTemplate = loadLdapTemplate();
-    final EqualsFilter equalsFilter = new EqualsFilter("objectClass",
-        ldapServerProperties.getGroupObjectClass());
     String baseDn = ldapServerProperties.getBaseDN();
-    ldapTemplate.search(baseDn, equalsFilter.encode(), new ContextMapper() {
+    ldapTemplate.search(baseDn, filter.encode(), new ContextMapper() {
 
       @Override
       public Object mapFromContext(Object ctx) {
@@ -320,12 +472,24 @@ public class AmbariLdapDataPopulator {
    * @return set of info about LDAP users
    */
   protected Set<LdapUserDto> getExternalLdapUserInfo() {
+    EqualsFilter userObjectFilter = new EqualsFilter("objectClass",
+        ldapServerProperties.getUserObjectClass());
+    return getFilteredLdapUsers(userObjectFilter);
+  }
+
+  private Set<LdapUserDto> getFilteredLdapUsers(Filter...filters) {
+    AndFilter andFilter = new AndFilter();
+    for (Filter filter : filters) {
+      andFilter.and(filter);
+    }
+    return getFilteredLdapUsers(andFilter);
+  }
+
+  private Set<LdapUserDto> getFilteredLdapUsers(Filter filter) {
     final Set<LdapUserDto> users = new HashSet<LdapUserDto>();
     final LdapTemplate ldapTemplate = loadLdapTemplate();
-    final EqualsFilter equalsFilter = new EqualsFilter("objectClass",
-        ldapServerProperties.getUserObjectClass());
     String baseDn = ldapServerProperties.getBaseDN();
-    ldapTemplate.search(baseDn, equalsFilter.encode(), new ContextMapper() {
+    ldapTemplate.search(baseDn, filter.encode(), new ContextMapper() {
 
       @Override
       public Object mapFromContext(Object ctx) {
@@ -337,11 +501,11 @@ public class AmbariLdapDataPopulator {
           user.setUserName(usernameAttribute.toLowerCase());
           user.setUid(uidAttribute.toLowerCase());
           user.setDn(adapter.getNameInNamespace().toLowerCase());
+          users.add(user);
         } else {
           LOG.warn("Ignoring LDAP user " + adapter.getNameInNamespace() + " as it doesn't have required" +
               " attributes uid and " + ldapServerProperties.getUsernameAttribute());
         }
-        users.add(user);
         return null;
       }
     });

+ 39 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/ConfigHelper.java

@@ -474,6 +474,8 @@ public class ConfigHelper {
     
     for(ServiceInfo serviceInfo:stack.getServices()) {     
       Set<PropertyInfo> stackProperties = ambariMetaInfo.getProperties(stack.getName(), stack.getVersion(), serviceInfo.getName());
+      Set<PropertyInfo> stackLevelProperties = ambariMetaInfo.getStackProperties(stack.getName(), stack.getVersion());
+      stackProperties.addAll(stackLevelProperties);
       
       for (PropertyInfo stackProperty : stackProperties) {
         String stackPropertyConfigType = fileNameToConfigType(stackProperty.getFilename());
@@ -488,6 +490,43 @@ public class ConfigHelper {
     return null;
   }
   
+  public ServiceInfo getPropertyOwnerService(Cluster cluster, String configType, String propertyName) throws AmbariException {
+    StackId stackId = cluster.getCurrentStackVersion();
+    StackInfo stack = ambariMetaInfo.getStackInfo(stackId.getStackName(),
+        stackId.getStackVersion());
+    
+    for(ServiceInfo serviceInfo:stack.getServices()) {     
+      Set<PropertyInfo> stackProperties = ambariMetaInfo.getProperties(stack.getName(), stack.getVersion(), serviceInfo.getName());
+      
+      for (PropertyInfo stackProperty : stackProperties) {
+        String stackPropertyConfigType = fileNameToConfigType(stackProperty.getFilename());
+        
+        if(stackProperty.getName().equals(propertyName) && stackPropertyConfigType.equals(configType)) {
+          return serviceInfo;
+        }
+      }
+      
+    }
+    
+    return null;
+  }
+  
+  public Set<PropertyInfo> getServiceProperties(Cluster cluster, String serviceName) throws AmbariException {
+    StackId stackId = cluster.getCurrentStackVersion();
+    StackInfo stack = ambariMetaInfo.getStackInfo(stackId.getStackName(),
+        stackId.getStackVersion());
+    
+    return ambariMetaInfo.getProperties(stack.getName(), stack.getVersion(), serviceName);
+  }
+  
+  public Set<PropertyInfo> getStackProperties(Cluster cluster) throws AmbariException {
+    StackId stackId = cluster.getCurrentStackVersion();
+    StackInfo stack = ambariMetaInfo.getStackInfo(stackId.getStackName(),
+        stackId.getStackVersion());
+    
+    return ambariMetaInfo.getStackProperties(stack.getName(), stack.getVersion());
+  }
+  
   public void createConfigType(Cluster cluster, AmbariManagementController ambariManagementController, 
       String configType, Map<String, String> properties, String authName) throws AmbariException {
     String tag;

+ 32 - 27
ambari-server/src/main/java/org/apache/ambari/server/state/stack/LatestRepoCallable.java

@@ -41,13 +41,13 @@ import com.google.gson.reflect.TypeToken;
 public class LatestRepoCallable implements Callable<Void> {
   private static final int LOOKUP_CONNECTION_TIMEOUT = 2000;
   private static final int LOOKUP_READ_TIMEOUT = 1000;
-  
+
   private final static Logger LOG = LoggerFactory.getLogger(LatestRepoCallable.class);
-  
+
   private String sourceUri = null;
   private File stackRepoFolder = null;
   private StackInfo stack = null;
-  
+
   public LatestRepoCallable(String latestSourceUri, File stackRepoFolder, StackInfo stack) {
     this.sourceUri = latestSourceUri;
     this.stackRepoFolder = stackRepoFolder;
@@ -56,19 +56,19 @@ public class LatestRepoCallable implements Callable<Void> {
 
   @Override
   public Void call() throws Exception {
-    
+
     Type type = new TypeToken<Map<String, Map<String, Object>>>(){}.getType();
     Gson gson = new Gson();
-    
+
     Map<String, Map<String, Object>> latestUrlMap = null;
-    
+
     try {
       if (sourceUri.startsWith("http")) {
-        
+
         URLStreamProvider streamProvider = new URLStreamProvider(
             LOOKUP_CONNECTION_TIMEOUT, LOOKUP_READ_TIMEOUT,
             null, null, null);
-        
+
         LOG.info("Loading latest URL info from " + sourceUri);
         latestUrlMap = gson.fromJson(new InputStreamReader(
             streamProvider.readFrom(sourceUri)), type);
@@ -79,7 +79,7 @@ public class LatestRepoCallable implements Callable<Void> {
         } else {
           jsonFile = new File(sourceUri);
         }
-        
+
         if (jsonFile.exists()) {
           LOG.info("Loading latest URL info from " + jsonFile);
           latestUrlMap = gson.fromJson(new FileReader(jsonFile), type);
@@ -89,71 +89,76 @@ public class LatestRepoCallable implements Callable<Void> {
       LOG.error("Could not load the URI " + sourceUri + " (" + e.getMessage() + ")");
       throw e;
     }
-    
-    
+
+
     if (null != latestUrlMap) {
       for (RepositoryInfo ri : stack.getRepositories()) {
         if (latestUrlMap.containsKey(ri.getRepoId())) {
           Map<String, Object> valueMap = latestUrlMap.get(ri.getRepoId());
           if (valueMap.containsKey("latest")) {
-            
+
             @SuppressWarnings("unchecked")
             Map<String, String> osMap = (Map<String, String>) valueMap.get("latest");
-            
+
             String baseUrl = resolveOsUrl(ri.getOsType(), osMap);
             if (null != baseUrl) {
               // !!! in the case where <name>.repo is defined with the base url, strip that off.
               // Agents do the reverse action (take the base url, and append <name>.repo)
-              
+
               String repo_file_format;
               if(ri.getOsType().equals("ubuntu12")) {
                 repo_file_format = "list";
               } else {
                 repo_file_format = "repo";
               }
-              
+
               String repoFileName = stack.getName().toLowerCase() + "." + repo_file_format;
-              int idx = baseUrl.toLowerCase().indexOf(repoFileName); 
-              
+              int idx = baseUrl.toLowerCase().indexOf(repoFileName);
+
               if (-1 != idx && baseUrl.toLowerCase().endsWith(repoFileName)) {
                 baseUrl = baseUrl.substring(0, idx);
               }
-              
+
               if ('/' == baseUrl.charAt(baseUrl.length()-1)) {
                 baseUrl = baseUrl.substring(0, baseUrl.length()-1);
               }
-              
+
               ri.setLatestBaseUrl(baseUrl);
+              if (ri.getBaseUrl() != null
+                  && ri.getBaseUrl().equals(ri.getDefaultBaseUrl())) {
+                // Override baseUrl with the latestBaseUrl.
+                ri.setBaseUrl(baseUrl);
+              }
             }
           }
         }
       }
     }
-    
+
     return null;
   }
-  
+
   /**
    * Resolves a base url given that certain OS types can be used interchangeably.
    * @param os the target os to find
-   * @param osMap the map of os-to-baseurl 
+   * @param osMap the map of os-to-baseurl
    * @return the url for an os.
    */
   private String resolveOsUrl(String os, Map<String, String> osMap) {
-    
+
     // !!! look for the OS directly
     if (osMap.containsKey(os))
       return osMap.get(os);
-    
+
     // !!! os not found, find and return the first compatible one
     Set<String> possibleTypes = OsFamily.findTypes(os);
-    
+
     for (String type : possibleTypes) {
       if (osMap.containsKey(type))
         return osMap.get(type);
     }
-    
+
     return null;
   }
-  
+
 }

+ 165 - 50
ambari-server/src/main/java/org/apache/ambari/server/upgrade/AbstractUpgradeCatalog.java

@@ -22,6 +22,7 @@ import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
 import com.google.inject.persist.Transactional;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.AmbariManagementController;
@@ -32,16 +33,26 @@ import org.apache.ambari.server.orm.entities.MetainfoEntity;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.Service;
+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.utils.VersionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.persistence.EntityManager;
+
 import java.sql.SQLException;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
 
 public abstract class AbstractUpgradeCatalog implements UpgradeCatalog {
   @Inject
@@ -172,6 +183,158 @@ public abstract class AbstractUpgradeCatalog implements UpgradeCatalog {
       dbAccessor.executeQuery(String.format("ALTER ROLE %s SET search_path to '%s';", dbUser, schemaName));
     }
   }
+  
+  public void addNewConfigurationsFromXml() throws AmbariException {
+    ConfigHelper configHelper = injector.getInstance(ConfigHelper.class);
+    AmbariManagementController controller = injector.getInstance(AmbariManagementController.class);
+    
+    Clusters clusters = controller.getClusters();
+    if (clusters == null) {
+      return;
+    }
+    Map<String, Cluster> clusterMap = clusters.getClusters();
+
+    if (clusterMap != null && !clusterMap.isEmpty()) {
+      for (Cluster cluster : clusterMap.values()) {
+        Map<String, Set<String>> newProperties = new HashMap<String, Set<String>>();
+        
+        Set<PropertyInfo> stackProperties = configHelper.getStackProperties(cluster);
+        for(String serviceName: cluster.getServices().keySet()) {
+          Set<PropertyInfo> properties = configHelper.getServiceProperties(cluster, serviceName);
+          
+          if(properties == null) {
+            continue;
+          }
+          properties.addAll(stackProperties);
+          
+          for(PropertyInfo property:properties) {
+            String configType = ConfigHelper.fileNameToConfigType(property.getFilename());
+            Config clusterConfigs = cluster.getDesiredConfigByType(configType);
+            if(clusterConfigs == null || !clusterConfigs.getProperties().containsKey(property.getName())) {
+              LOG.info("Config " + property.getName() + " from " + configType + " from xml configurations" +
+                  " is not found on the cluster. Adding it...");
+              
+              if(!newProperties.containsKey(configType)) {
+                newProperties.put(configType, new HashSet<String>());
+              }
+              newProperties.get(configType).add(property.getName());
+            }
+          }
+        }
+        
+        
+        
+        for (Entry<String, Set<String>> newProperty : newProperties.entrySet()) {
+          updateConfigurationPropertiesWithValuesFromXml(newProperty.getKey(), newProperty.getValue(), false, true);
+        }
+      }
+    }
+  }
+  
+  /**
+   * Create a new cluster scoped configuration with the new properties added
+   * with the values from the coresponding xml files.
+   * 
+   * If xml owner service is not in the cluster, the configuration won't be added.
+   * 
+   * @param configType Configuration type. (hdfs-site, etc.)
+   * @param properties Set property names.
+   */
+  protected void updateConfigurationPropertiesWithValuesFromXml(String configType,
+      Set<String> propertyNames, boolean updateIfExists, boolean createNewConfigType) throws AmbariException {
+    ConfigHelper configHelper = injector.getInstance(ConfigHelper.class);
+    AmbariManagementController controller = injector.getInstance(AmbariManagementController.class);
+    
+    Clusters clusters = controller.getClusters();
+    if (clusters == null) {
+      return;
+    }
+    Map<String, Cluster> clusterMap = clusters.getClusters();
+
+    if (clusterMap != null && !clusterMap.isEmpty()) {
+      for (Cluster cluster : clusterMap.values()) {
+        Map<String, String> properties = new HashMap<String, String>();
+        
+        for(String propertyName:propertyNames) {
+          String propertyValue = configHelper.getPropertyValueFromStackDefenitions(cluster, configType, propertyName);
+          
+          if(propertyValue == null) {
+            LOG.info("Config " + propertyName + " from " + configType + " is not found in xml definitions." +
+                "Skipping configuration property update");
+            continue;
+          }
+          
+          ServiceInfo propertyService = configHelper.getPropertyOwnerService(cluster, configType, propertyName);
+          if(propertyService != null && !cluster.getServices().containsKey(propertyService.getName())) {
+            LOG.info("Config " + propertyName + " from " + configType + " with value = " + propertyValue + " " +
+                "Is not added due to service " + propertyService.getName() + " is not in the cluster.");
+            continue;
+          }
+          
+          properties.put(propertyName, propertyValue);
+        }
+        
+        updateConfigurationPropertiesForCluster(cluster, configType,
+            properties, updateIfExists, createNewConfigType);
+      }
+    }
+  }
+  
+  protected void updateConfigurationPropertiesForCluster(Cluster cluster, String configType,
+      Map<String, String> properties, boolean updateIfExists, boolean createNewConfigType) throws AmbariException {
+    AmbariManagementController controller = injector.getInstance(AmbariManagementController.class);
+    String newTag = "version" + System.currentTimeMillis();
+    
+    if (properties != null) {
+      Map<String, Config> all = cluster.getConfigsByType(configType);
+      if (all == null || !all.containsKey(newTag) || properties.size() > 0) {
+        Map<String, String> oldConfigProperties;
+        Config oldConfig = cluster.getDesiredConfigByType(configType);
+        
+        if (oldConfig == null && !createNewConfigType) {
+          LOG.info("Config " + configType + " not found. Assuming service not installed. " +
+              "Skipping configuration properties update");
+          return;
+        } else if (oldConfig == null) {
+          oldConfigProperties = new HashMap<String, String>();
+          newTag = "version1";
+        } else {
+          oldConfigProperties = oldConfig.getProperties();
+        }
+
+        Map<String, String> mergedProperties =
+          mergeProperties(oldConfigProperties, properties, updateIfExists);
+
+        if (!Maps.difference(oldConfigProperties, mergedProperties).areEqual()) {
+          LOG.info("Applying configuration with tag '{}' to " +
+            "cluster '{}'", newTag, cluster.getClusterName());
+
+          ConfigurationRequest cr = new ConfigurationRequest();
+          cr.setClusterName(cluster.getClusterName());
+          cr.setVersionTag(newTag);
+          cr.setType(configType);
+          cr.setProperties(mergedProperties);
+          controller.createConfiguration(cr);
+
+          Config baseConfig = cluster.getConfig(cr.getType(), cr.getVersionTag());
+          if (baseConfig != null) {
+            String authName = "ambari-upgrade";
+
+            if (cluster.addDesiredConfig(authName, Collections.singleton(baseConfig)) != null) {
+              String oldConfigString = (oldConfig != null) ? " from='" + oldConfig.getTag() + "'" : "";
+              LOG.info("cluster '" + cluster.getClusterName() + "' "
+                + "changed by: '" + authName + "'; "
+                + "type='" + baseConfig.getType() + "' "
+                + "tag='" + baseConfig.getTag() + "'"
+                + oldConfigString);
+            }
+          }
+        } else {
+          LOG.info("No changes detected to config " + configType + ". Skipping configuration properties update");
+        }
+      }
+    }
+  }
 
   /**
    * Create a new cluster scoped configuration with the new properties added
@@ -183,7 +346,6 @@ public abstract class AbstractUpgradeCatalog implements UpgradeCatalog {
         Map<String, String> properties, boolean updateIfExists, boolean createNewConfigType) throws
     AmbariException {
     AmbariManagementController controller = injector.getInstance(AmbariManagementController.class);
-    String newTag = "version" + System.currentTimeMillis();
 
     Clusters clusters = controller.getClusters();
     if (clusters == null) {
@@ -193,55 +355,8 @@ public abstract class AbstractUpgradeCatalog implements UpgradeCatalog {
 
     if (clusterMap != null && !clusterMap.isEmpty()) {
       for (Cluster cluster : clusterMap.values()) {
-        if (properties != null) {
-          Map<String, Config> all = cluster.getConfigsByType(configType);
-          if (all == null || !all.containsKey(newTag) || properties.size() > 0) {
-            Map<String, String> oldConfigProperties;
-            Config oldConfig = cluster.getDesiredConfigByType(configType);
-            
-            if (oldConfig == null && !createNewConfigType) {
-              LOG.info("Config " + configType + " not found. Assuming service not installed. " +
-                  "Skipping configuration properties update");
-              return;
-            } else if (oldConfig == null) {
-              oldConfigProperties = new HashMap<String, String>();
-              newTag = "version1";
-            } else {
-              oldConfigProperties = oldConfig.getProperties();
-            }
-
-            Map<String, String> mergedProperties =
-              mergeProperties(oldConfigProperties, properties, updateIfExists);
-
-            if (!Maps.difference(oldConfigProperties, mergedProperties).areEqual()) {
-              LOG.info("Applying configuration with tag '{}' to " +
-                "cluster '{}'", newTag, cluster.getClusterName());
-
-              ConfigurationRequest cr = new ConfigurationRequest();
-              cr.setClusterName(cluster.getClusterName());
-              cr.setVersionTag(newTag);
-              cr.setType(configType);
-              cr.setProperties(mergedProperties);
-              controller.createConfiguration(cr);
-
-              Config baseConfig = cluster.getConfig(cr.getType(), cr.getVersionTag());
-              if (baseConfig != null) {
-                String authName = "ambari-upgrade";
-
-                if (cluster.addDesiredConfig(authName, Collections.singleton(baseConfig)) != null) {
-                  String oldConfigString = (oldConfig != null) ? " from='" + oldConfig.getTag() + "'" : "";
-                  LOG.info("cluster '" + cluster.getClusterName() + "' "
-                    + "changed by: '" + authName + "'; "
-                    + "type='" + baseConfig.getType() + "' "
-                    + "tag='" + baseConfig.getTag() + "'"
-                    + oldConfigString);
-                }
-              }
-            } else {
-              LOG.info("No changes detected to config " + configType + ". Skipping configuration properties update");
-            }
-          }
-        }
+        updateConfigurationPropertiesForCluster(cluster, configType,
+            properties, updateIfExists, createNewConfigType);
       }
     }
   }

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog161.java

@@ -311,7 +311,7 @@ public class UpgradeCatalog161 extends AbstractUpgradeCatalog {
             "reducer=1000000000\npig.exec.reducers.max=999\n\n# Temporary location to store the intermediate " +
             "data.\npig.temp.dir=/tmp/\n\n# Threshold for merging FRJoin fragment files\npig.files.concatenation." +
             "threshold=100\npig.optimistic.files.concatenation=false;\n\npig.disable.counter=false\n\n" +
-            "hcat.bin=/usr/bin/hcat"), true, true);
+            "hcat.bin=/usr/bin/hcat"), true, false);
   }
 
   @Override

+ 4 - 23
ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java

@@ -634,11 +634,11 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
 
     moveGlobalsToEnv();
     addEnvContentFields();
-    addMissingConfigs();
     renamePigProperties();
     upgradePermissionModel();
     addJobsViewPermissions();
     moveConfigGroupsGlobalToEnv();
+    addMissingConfigs();
   }
 
   public void moveHcatalogIntoHiveService() throws AmbariException {
@@ -994,25 +994,7 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
   }
 
   protected void addMissingConfigs() throws AmbariException {
-    updateConfigurationProperties("hbase-env",
-        Collections.singletonMap("hbase_regionserver_xmn_max", "512"), false,
-        false);
-
-    updateConfigurationProperties("hbase-env",
-        Collections.singletonMap("hbase_regionserver_xmn_ratio", "0.2"), false,
-        false);
-
-    updateConfigurationProperties("yarn-env",
-        Collections.singletonMap("min_user_id", "1000"), false,
-        false);
-
-    updateConfigurationProperties("sqoop-env", Collections.singletonMap("sqoop_user", "sqoop"), false, false);
-
-    updateConfigurationProperties("hadoop-env",
-            Collections.singletonMap("hadoop_root_logger", "INFO,RFA"), false,
-            false);
-
-    updateConfigurationProperties("oozie-env", Collections.singletonMap("oozie_admin_port", "11001"), false, false);
+    addNewConfigurationsFromXml();
   }
 
   /**
@@ -1066,8 +1048,7 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
             continue;
           }
 
-          String value = configHelper.getPropertyValueFromStackDefenitions(cluster, configType, CONTENT_FIELD_NAME);
-          updateConfigurationProperties(configType, Collections.singletonMap(CONTENT_FIELD_NAME, value), true, true);
+          updateConfigurationPropertiesWithValuesFromXml(configType, Collections.singleton(CONTENT_FIELD_NAME), false, true);
         }
       }
     }
@@ -1130,7 +1111,7 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
         }
 
         for (Entry<String, Map<String, String>> newProperty : newProperties.entrySet()) {
-          updateConfigurationProperties(newProperty.getKey(), newProperty.getValue(), true, true);
+          updateConfigurationProperties(newProperty.getKey(), newProperty.getValue(), false, true);
         }
 
         // if have some custom properties, for own services etc., leave that as it was

+ 167 - 0
ambari-server/src/main/java/org/apache/ambari/server/view/HttpImpersonatorImpl.java

@@ -0,0 +1,167 @@
+/**
+ * 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.view;
+
+
+import org.apache.ambari.server.controller.internal.URLStreamProvider;
+import org.apache.ambari.server.proxy.ProxyService;
+import org.apache.ambari.view.ImpersonatorSetting;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.HttpImpersonator;
+import org.apache.ambari.server.controller.internal.AppCookieManager;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+
+/**
+ * Class for Ambari to impersonate users over HTTP request.
+ * This is handy for Views like Jobs that needs to query ATS via HTTP Get requests and
+ * impersonate the currently logged on user.
+ * Or a file browser view that needs to use WebHDFS with the credentials of the current user.
+ */
+public class HttpImpersonatorImpl implements HttpImpersonator {
+  private ViewContext context;
+  private AppCookieManager appCookieManager;
+  private FactoryHelper helper;
+
+  /**
+   * Helper class that is mocked during unit testing.
+   */
+  static class FactoryHelper{
+    BufferedReader makeBR(InputStreamReader in){
+      return new BufferedReader(in);
+    }
+  }
+
+  public HttpImpersonatorImpl(ViewContext c, AppCookieManager appCookieManager) {
+    this.context = c;
+    this.appCookieManager = appCookieManager;
+    this.helper = new FactoryHelper();
+  }
+
+  public HttpImpersonatorImpl(ViewContext c, AppCookieManager appCookieManager, FactoryHelper h) {
+    this.context = c;
+    this.appCookieManager = appCookieManager;
+    this.helper = h;
+  }
+
+  public ViewContext getContext() {
+    return this.context;
+  }
+
+  public String getUsername() {
+    return getContext().getUsername();
+  }
+
+  /**
+   * @param conn HTTP connection that will be modified and returned
+   * @param type HTTP Request type: GET, PUT, POST, DELETE, etc.
+   * @return HTTP Connection object with the "doAs" query param set to the currently logged on user.
+   */
+  @Override
+  public HttpURLConnection doAs(HttpURLConnection conn, String type)  {
+    String username = getUsername();
+    return doAs(conn, type, username, ImpersonatorSettingImpl.DEFAULT_DO_AS_PARAM);
+  }
+
+  /**
+   * @param conn HTTP connection that will be modified and returned
+   * @param type HTTP Request type: GET, PUT, POST, DELETE, etc.
+   * @param username Username to impersonate
+   * @param doAsParamName Query param, typically "doAs"
+   * @return HTTP Connection object with the doAs query param set to the provider username.
+   */
+  @Override
+  public HttpURLConnection doAs(HttpURLConnection conn, String type, String username, String doAsParamName) {
+    String url = conn.getURL().toString();
+    if (url.toLowerCase().contains(doAsParamName.toLowerCase())) {
+      throw new IllegalArgumentException("URL cannot contain \"" + doAsParamName + "\" parameter");
+    }
+
+    try {
+      conn.setRequestMethod(type);
+    } catch (IOException e) {
+      return null;
+    }
+
+    conn.setRequestProperty(doAsParamName, username);
+    return conn;
+  }
+
+  /**
+   * Returns the result of the HTTP request by setting the "doAs" impersonation for the query param and username
+   * in @param impersonatorSetting.
+   * @param urlToRead URL to request
+   * @param requestType HTTP Request type: GET, PUT, POST, DELETE, etc.
+   * @param impersonatorSetting Setting class with default values for username and doAs param name.
+   *                           To use different values, call the setters of the object.
+   * @return Return a response as a String
+   */
+  @Override
+  public String requestURL(String urlToRead, String requestType, final ImpersonatorSetting impersonatorSetting) {
+    String result = "";
+    BufferedReader rd;
+    String line = null;
+    String url = urlToRead;
+
+    if (url.toLowerCase().contains(impersonatorSetting.getDoAsParamName().toLowerCase())) {
+      throw new IllegalArgumentException("URL cannot contain \"" + impersonatorSetting.getDoAsParamName() + "\" parameter");
+    }
+
+    try {
+      URLStreamProvider urlStreamProvider = new URLStreamProvider(ProxyService.URL_CONNECT_TIMEOUT, ProxyService.URL_READ_TIMEOUT, null, null, null);
+
+      Map<String, List<String>> headers = new HashMap<String, List<String>>();
+      headers.put(impersonatorSetting.getDoAsParamName(), new ArrayList<String>() {{add(impersonatorSetting.getUsername()); }} );
+
+      HttpURLConnection connection = urlStreamProvider.processURL(url, requestType, null, headers);
+
+      int responseCode = connection.getResponseCode();
+      InputStream resultInputStream = null;
+      if (responseCode >= ProxyService.HTTP_ERROR_RANGE_START) {
+        resultInputStream = connection.getErrorStream();
+      } else {
+        resultInputStream = connection.getInputStream();
+      }
+
+      rd = this.helper.makeBR(new InputStreamReader(resultInputStream));
+
+      if (rd != null) {
+        line = rd.readLine();
+        while (line != null) {
+          result += line;
+          line = rd.readLine();
+        }
+        rd.close();
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return result;
+  }
+}

+ 63 - 0
ambari-server/src/main/java/org/apache/ambari/server/view/ImpersonatorSettingImpl.java

@@ -0,0 +1,63 @@
+/**
+ * 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.view;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.ImpersonatorSetting;
+
+/**
+ * Class that provides default values for impersonating, such as the username and doAs parameter name.
+ */
+public class ImpersonatorSettingImpl implements ImpersonatorSetting {
+  private String doAsParamName;
+  private String username;
+
+  public static final String DEFAULT_DO_AS_PARAM = "doAs";
+
+  public ImpersonatorSettingImpl(ViewContext context) {
+    // Default values
+    this.doAsParamName = DEFAULT_DO_AS_PARAM;
+    this.username = context.getUsername();
+  }
+
+  /**
+   * @return The parameter name used for "doAs" impersonation.
+   */
+  @Override
+  public String getDoAsParamName() { return this.doAsParamName; }
+
+  /**
+   * @return The username value that will be used for "doAs" impersonation.
+   */
+  @Override
+  public String getUsername() { return this.username; }
+
+  /**
+   * Set the parameter name used for "doAs" impersonation.
+   * @param doAsParamName Query parameter name
+   */
+  @Override
+  public void setDoAsParamName(String doAsParamName) { this.doAsParamName = doAsParamName; }
+
+  /**
+   * Set the username value that will be used for "doAs" impersonation.
+   * @param username Username to impersonate as
+   */
+  @Override
+  public void setUsername(String username) { this.username = username; }
+}

+ 16 - 2
ambari-server/src/main/java/org/apache/ambari/server/view/ViewContextImpl.java

@@ -35,6 +35,8 @@ import org.apache.ambari.view.Masker;
 import org.apache.ambari.view.ResourceProvider;
 import org.apache.ambari.view.SecurityException;
 import org.apache.ambari.view.URLStreamProvider;
+import org.apache.ambari.server.controller.internal.AppCookieManager;
+import org.apache.ambari.view.ImpersonatorSetting;
 import org.apache.ambari.view.ViewContext;
 import org.apache.ambari.view.ViewController;
 import org.apache.ambari.view.ViewDefinition;
@@ -88,7 +90,7 @@ public class ViewContextImpl implements ViewContext, ViewController {
   /**
    * The available stream provider.
    */
-  private final URLStreamProvider streamProvider;
+  private final ViewURLStreamProvider streamProvider;
 
   /**
    * The data store.
@@ -300,6 +302,15 @@ public class ViewContextImpl implements ViewContext, ViewController {
     return this;
   }
 
+  @Override
+  public HttpImpersonatorImpl getHttpImpersonator() {
+    return new HttpImpersonatorImpl(this, this.streamProvider.getAppCookieManager());
+  }
+
+  @Override
+  public ImpersonatorSetting getImpersonatorSetting() {
+    return new ImpersonatorSettingImpl(this);
+  }
 
   // ----- ViewController ----------------------------------------------------
 
@@ -409,7 +420,6 @@ public class ViewContextImpl implements ViewContext, ViewController {
      */
     private final org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider;
 
-
     // ----- Constructor -----------------------------------------------------
 
     protected ViewURLStreamProvider(org.apache.ambari.server.controller.internal.URLStreamProvider streamProvider) {
@@ -448,6 +458,10 @@ public class ViewContextImpl implements ViewContext, ViewController {
               configuration.getTruststoreType());
       return new ViewURLStreamProvider(streamProvider);
     }
+
+    protected AppCookieManager getAppCookieManager() {
+      return streamProvider.getAppCookieManager();
+    }
   }
 
   // ----- Inner class : ParameterResolver -------------------------------

+ 5 - 21
ambari-server/src/main/java/org/apache/ambari/server/view/ViewProviderModule.java

@@ -22,9 +22,7 @@ import org.apache.ambari.server.controller.spi.PropertyProvider;
 import org.apache.ambari.server.controller.spi.ProviderModule;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
-import org.apache.ambari.server.orm.entities.ViewEntity;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -34,11 +32,6 @@ import java.util.Map;
  * any resource types not defined in a view.
  */
 public class ViewProviderModule implements ProviderModule {
-  /**
-   * Mapping of view resource type to resource provider.
-   */
-  private final Map<Resource.Type, ResourceProvider> resourceProviders;
-
   /**
    * The delegate provider module.
    */
@@ -51,12 +44,9 @@ public class ViewProviderModule implements ProviderModule {
    * Construct a view provider module.
    *
    * @param providerModule     the delegate provider module
-   * @param resourceProviders  the map of view resource types to resource providers
    */
-  private ViewProviderModule(ProviderModule providerModule,
-                            Map<Resource.Type, ResourceProvider> resourceProviders) {
+  private ViewProviderModule(ProviderModule providerModule) {
     this.providerModule = providerModule;
-    this.resourceProviders = resourceProviders;
   }
 
 
@@ -65,6 +55,9 @@ public class ViewProviderModule implements ProviderModule {
   @Override
   public ResourceProvider getResourceProvider(Resource.Type type) {
 
+    Map<Resource.Type, ResourceProvider> resourceProviders =
+        ViewRegistry.getInstance().getResourceProviders();
+
     if (resourceProviders.containsKey(type)) {
       return resourceProviders.get(type);
     }
@@ -87,15 +80,6 @@ public class ViewProviderModule implements ProviderModule {
    * @return a view provider module
    */
   public static ViewProviderModule getViewProviderModule(ProviderModule module) {
-    Map<Resource.Type, ResourceProvider> resourceProviders = new HashMap<Resource.Type, ResourceProvider>();
-
-    ViewRegistry registry = ViewRegistry.getInstance();
-    for (ViewEntity definition : registry.getDefinitions()) {
-      for (Resource.Type type : definition.getViewResourceTypes()){
-        ResourceProvider provider = definition.getResourceProvider(type);
-        resourceProviders.put(type, provider);
-      }
-    }
-    return new ViewProviderModule(module, resourceProviders);
+    return new ViewProviderModule(module);
   }
 }

+ 21 - 1
ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java

@@ -29,6 +29,7 @@ import org.apache.ambari.server.api.services.ViewExternalSubResourceService;
 import org.apache.ambari.server.api.services.ViewSubResourceService;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.orm.dao.MemberDAO;
 import org.apache.ambari.server.orm.dao.PrivilegeDAO;
 import org.apache.ambari.server.orm.dao.ResourceDAO;
@@ -124,6 +125,12 @@ public class ViewRegistry {
   private final Map<String, Set<SubResourceDefinition>> subResourceDefinitionsMap =
       new HashMap<String, Set<SubResourceDefinition>>();
 
+  /**
+   * Mapping of view types to resource providers.
+   */
+  private final Map<Resource.Type, ResourceProvider> resourceProviders =
+      new ConcurrentHashMap<Resource.Type, ResourceProvider>();
+
   /**
    * Mapping of view names to registered listeners.
    */
@@ -732,6 +739,15 @@ public class ViewRegistry {
     listeners.clear();
   }
 
+  /**
+   * Get the view resource provider mapping.
+   *
+   * @return the map of view resource providers
+   */
+  protected Map<Resource.Type, ResourceProvider> getResourceProviders() {
+    return resourceProviders;
+  }
+
   // get a view entity for the given internal view name
   private ViewEntity getDefinition(String viewName) {
     return viewDefinitions.get(viewName);
@@ -768,6 +784,8 @@ public class ViewRegistry {
         new ViewExternalSubResourceProvider(externalResourceType, viewDefinition);
     viewDefinition.addResourceProvider(externalResourceType, viewExternalSubResourceProvider );
 
+    resourceProviders.put(externalResourceType, viewExternalSubResourceProvider);
+
     ResourceInstanceFactoryImpl.addResourceDefinition(externalResourceType,
         new ViewExternalSubResourceDefinition(externalResourceType));
 
@@ -799,7 +817,9 @@ public class ViewRegistry {
         Class<?> clazz      = resourceConfiguration.getResourceClass(cl);
         String   idProperty = resourceConfiguration.getIdProperty();
 
-        viewDefinition.addResourceProvider(type, new ViewSubResourceProvider(type, clazz, idProperty, viewDefinition));
+        ViewSubResourceProvider provider = new ViewSubResourceProvider(type, clazz, idProperty, viewDefinition);
+        viewDefinition.addResourceProvider(type, provider);
+        resourceProviders.put(type, provider);
 
         resources.add(viewResourceEntity);
       }

+ 74 - 0
ambari-server/src/main/python/ambari-server.py

@@ -41,6 +41,8 @@ import random
 import pwd
 from ambari_server.resourceFilesKeeper import ResourceFilesKeeper, KeeperException
 import json
+import base64
+from threading import Thread
 from ambari_commons import OSCheck, OSConst, Firewall
 from ambari_server import utils
 
@@ -64,6 +66,7 @@ UPGRADE_STACK_ACTION = "upgradestack"
 STATUS_ACTION = "status"
 SETUP_HTTPS_ACTION = "setup-https"
 LDAP_SETUP_ACTION = "setup-ldap"
+LDAP_SYNC_ACTION = "sync-ldap"
 SETUP_GANGLIA_HTTPS_ACTION = "setup-ganglia-https"
 SETUP_NAGIOS_HTTPS_ACTION = "setup-nagios-https"
 ENCRYPT_PASSWORDS_ACTION = "encrypt-passwords"
@@ -119,6 +122,14 @@ SERVER_LOG_FILE = "/var/log/ambari-server/ambari-server.log"
 BLIND_PASSWORD = "*****"
 ROOT_FS_PATH = "/"
 
+# api properties
+SERVER_API_HOST = '127.0.0.1'
+SERVER_API_PROTOCOL = 'http'
+SERVER_API_PORT = '8080'
+SERVER_API_LDAP_URL = '/api/v1/controllers/ldap'
+SERVER_API_LOGIN = 'admin'
+SERVER_API_PASS = 'admin'
+
 # terminal styles
 BOLD_ON = '\033[1m'
 BOLD_OFF = '\033[0m'
@@ -2994,6 +3005,67 @@ def get_prompt_default(defaultStr=None):
   else:
     return '(' + defaultStr + ')'
 
+def sync_ldap():
+  if not is_root():
+    err = 'Ambari-server sync-ldap should be run with ' \
+          'root-level privileges'
+    raise FatalException(4, err)
+
+  server_status, pid = is_server_runing()
+  if not server_status:
+    err = 'Ambari Server is not running.'
+    raise FatalException(1, err)
+
+  ldap_configured = get_ambari_properties().get_property(IS_LDAP_CONFIGURED)
+  if ldap_configured != 'true':
+    err = "LDAP is not configured. Run 'ambari-server setup-ldap' first."
+    raise FatalException(1, err)
+
+  url = '{0}://{1}:{2!s}{3}'.format(SERVER_API_PROTOCOL, SERVER_API_HOST, SERVER_API_PORT, SERVER_API_LDAP_URL)
+  admin_auth = base64.encodestring('%s:%s' % (SERVER_API_LOGIN, SERVER_API_PASS)).replace('\n', '')
+  request = urllib2.Request(url)
+  request.add_header('Authorization', 'Basic %s' % admin_auth)
+  request.add_header('X-Requested-By', 'ambari')
+  body = [{"LDAP":{"synced_groups":"*","synced_users":"*"}}]
+  request.add_data(json.dumps(body))
+  request.get_method = lambda: 'PUT'
+  progress_message_thread = None
+  request_in_progress = True
+  def print_progress(message):
+    sys.stdout.write(message)
+    sys.stdout.flush()
+    while request_in_progress:
+      sys.stdout.write('.')
+      sys.stdout.flush()
+      time.sleep(1)
+    sys.stdout.write('\n')
+    sys.stdout.flush()
+  try:
+    progress_message_thread = Thread(target=print_progress, args=('Syncing Ambari Database for permissions for the LDAP Users and Groups..',))
+    progress_message_thread.start()
+    response = urllib2.urlopen(request)
+  except Exception as e:
+    err = 'Sync failed. Error details: %s' % e
+    raise FatalException(1, err)
+  finally:
+    request_in_progress = False
+    if progress_message_thread is not None:
+      progress_message_thread.join()
+  response_status_code = response.getcode()
+  if response_status_code != 200:
+    err = 'Error during syncing. Http status code - ' + response_status_code
+    raise FatalException(1, err)
+  response_body = json.loads(response.read())
+  sync_info = response_body['resources'][0]['Sync']
+  if sync_info['status'] != 'successful':
+    raise FatalException(1, sync_info['summary'])
+  else:
+    print 'Synced:'
+    for principal_type, summary in sync_info['summary'].iteritems():
+      print '    {0}:'.format(principal_type)
+      for action, amount in summary.iteritems():
+        print '        - {0} = {1!s}'.format(action, amount)
+    print 'Finished LDAP Sync.'
 
 def setup_ldap():
   if not is_root():
@@ -4343,6 +4415,8 @@ def main():
       upgrade_stack(options, stack_id, repo_url, repo_url_os)
     elif action == LDAP_SETUP_ACTION:
       setup_ldap()
+    elif action == LDAP_SYNC_ACTION:
+      sync_ldap()
     elif action == SETUP_SECURITY_ACTION:
       need_restart = setup_security(options)
     elif action == REFRESH_STACK_HASH_ACTION:

+ 2 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/package/scripts/application_timeline_server.py

@@ -49,6 +49,8 @@ class ApplicationTimelineServer(Script):
   def status(self, env):
     import status_params
     env.set_params(status_params)
+    Execute(format("mv {yarn_historyserver_pid_file_old} {yarn_historyserver_pid_file}"),
+            only_if = format("test -e {yarn_historyserver_pid_file_old}", user=status_params.yarn_user))
     check_process_status(status_params.yarn_historyserver_pid_file)
 
 if __name__ == "__main__":

+ 1 - 0
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/YARN/package/scripts/status_params.py

@@ -31,5 +31,6 @@ mapred_pid_dir = format("{mapred_pid_dir_prefix}/{mapred_user}")
 
 resourcemanager_pid_file = format("{yarn_pid_dir}/yarn-{yarn_user}-resourcemanager.pid")
 nodemanager_pid_file = format("{yarn_pid_dir}/yarn-{yarn_user}-nodemanager.pid")
+yarn_historyserver_pid_file_old = format("{yarn_pid_dir}/yarn-{yarn_user}-historyserver.pid")
 yarn_historyserver_pid_file = format("{yarn_pid_dir}/yarn-{yarn_user}-timelineserver.pid")  # *-historyserver.pid is deprecated
 mapred_historyserver_pid_file = format("{mapred_pid_dir}/mapred-{mapred_user}-historyserver.pid")

+ 2 - 2
ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/scripts/params.py

@@ -34,12 +34,12 @@ else:
   slider_conf_dir = "/usr/lib/slider/conf"
   slider_bin_dir = "/usr/lib/slider/bin"
 
+hadoop_conf_dir = "/etc/hadoop/conf"
 smokeuser = config['configurations']['cluster-env']['smokeuser']
-user_group = config['configurations']['cluster-env']['user_group']
 security_enabled = config['configurations']['cluster-env']['security_enabled']
 smokeuser_keytab = config['configurations']['cluster-env']['smokeuser_keytab']
 kinit_path_local = functions.get_kinit_path(["/usr/bin", "/usr/kerberos/bin", "/usr/sbin"])
 
 java64_home = config['hostLevelParams']['java_home']
 log4j_props = config['configurations']['slider-log4j']['content']
-slider_cmd = format("{slider_bin_dir}/slider-wrapper")
+slider_cmd = format("{slider_bin_dir}/slider")

+ 2 - 20
ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/scripts/slider.py

@@ -34,27 +34,9 @@ def slider():
             configurations=params.config['configurations']['slider-client']
   )
 
-  XmlConfig("core-site.xml",
-            conf_dir=params.slider_conf_dir,
-            configurations=params.config['configurations']['core-site'],
-            configuration_attributes=params.config['configuration_attributes']['core-site']
-  )
-
-  XmlConfig("hdfs-site.xml",
-            conf_dir=params.slider_conf_dir,
-            configurations=params.config['configurations']['hdfs-site'],
-            configuration_attributes=params.config['configuration_attributes']['hdfs-site']
-  )
-
-  XmlConfig("yarn-site.xml",
-            conf_dir=params.slider_conf_dir,
-            configurations=params.config['configurations']['yarn-site'],
-            configuration_attributes=params.config['configuration_attributes']['yarn-site']
-  )
-
-  File(format("{slider_bin_dir}/slider-wrapper"),
+  File(format("{slider_conf_dir}/slider-env.sh"),
        mode=0755,
-       content=Template('slider-wrapper.j2')
+       content=Template('slider-env.sh.j2')
   )
 
   if (params.log4j_props != None):

+ 2 - 6
ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/templates/slider-wrapper.j2 → ambari-server/src/main/resources/stacks/HDP/2.2/services/SLIDER/package/templates/slider-env.sh.j2

@@ -34,9 +34,5 @@
 # * See the License for the specific language governing permissions and
 # * limitations under the License.
 # */
-import slider
-import os, sys
-
-if __name__ == '__main__':
-  os.environ["JAVA_HOME"] = "{{java64_home}}"
-  slider.main()
+export JAVA_HOME={{java64_home}}
+export HADOOP_CONF_DIR={{hadoop_conf_dir}}

+ 49 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/resources/LdapSyncEventResourceDefinitionTest.java

@@ -0,0 +1,49 @@
+/**
+ * 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;
+
+/**
+ * LdapSyncEventResourceDefinition tests.
+ */
+public class LdapSyncEventResourceDefinitionTest {
+  @Test
+  public void testGetPluralName() throws Exception {
+    LdapSyncEventResourceDefinition resourceDefinition = new LdapSyncEventResourceDefinition();
+    Assert.assertEquals("ldap_sync_events", resourceDefinition.getPluralName());
+  }
+
+  @Test
+  public void testGetSingularName() throws Exception {
+    LdapSyncEventResourceDefinition resourceDefinition = new LdapSyncEventResourceDefinition();
+    Assert.assertEquals("ldap_sync_event", resourceDefinition.getSingularName());
+  }
+
+  @Test
+  public void testGetSubResourceDefinitions() throws Exception {
+    LdapSyncEventResourceDefinition resourceDefinition = new LdapSyncEventResourceDefinition();
+    Set<SubResourceDefinition> subResourceDefinitions = resourceDefinition.getSubResourceDefinitions ();
+
+    Assert.assertEquals(0, subResourceDefinitions.size());
+  }
+}

+ 139 - 66
ambari-server/src/test/java/org/apache/ambari/server/api/services/AmbariMetaInfoTest.java

@@ -18,9 +18,6 @@
 
 package org.apache.ambari.server.api.services;
 
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -30,9 +27,7 @@ import static org.junit.Assert.fail;
 
 import java.io.File;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -41,6 +36,7 @@ import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 
+import javax.persistence.EntityManager;
 import javax.xml.bind.JAXBException;
 
 import junit.framework.Assert;
@@ -49,31 +45,24 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.util.StackExtensionHelper;
 import org.apache.ambari.server.metadata.ActionMetadata;
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
-import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
-import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
 import org.apache.ambari.server.state.AutoDeployInfo;
-import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
 import org.apache.ambari.server.state.CustomCommandDefinition;
 import org.apache.ambari.server.state.DependencyInfo;
 import org.apache.ambari.server.state.OperatingSystemInfo;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.RepositoryInfo;
-import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.Stack;
-import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.alert.AlertDefinition;
-import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
 import org.apache.ambari.server.state.alert.PortSource;
 import org.apache.ambari.server.state.alert.Reporting;
 import org.apache.ambari.server.state.alert.Source;
 import org.apache.ambari.server.state.stack.MetricDefinition;
 import org.apache.commons.io.FileUtils;
-import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -127,6 +116,9 @@ public class AmbariMetaInfoTest {
     injector = Guice.createInjector(Modules.override(
         new InMemoryDefaultTestModule()).with(new MockModule()));
 
+    injector.getInstance(GuiceJpaInitializer.class);
+    injector.getInstance(EntityManager.class);
+
     File stackRoot = new File("src/test/resources/stacks");
     LOG.info("Stacks file " + stackRoot.getAbsolutePath());
     metaInfo = new AmbariMetaInfo(stackRoot, new File("target/version"));
@@ -180,6 +172,130 @@ public class AmbariMetaInfoTest {
     assertFalse(repository.get("centos6").isEmpty());
   }
 
+  @Test
+  public void testGetRepositoryDefault() throws Exception {
+    // Scenario: user has internet and does nothing to repos via api
+    // use the latest
+    String buildDir = tmpFolder.getRoot().getAbsolutePath();
+    AmbariMetaInfo ambariMetaInfo = setupTempAmbariMetaInfo(buildDir);
+    // The current stack already has (HDP, 2.1.1, redhat6) with valid latest
+    // url
+    ambariMetaInfo.init();
+
+    List<RepositoryInfo> redhat6Repo = ambariMetaInfo.getRepositories(
+        STACK_NAME_HDP, "2.1.1", "redhat6");
+    assertNotNull(redhat6Repo);
+    for (RepositoryInfo ri : redhat6Repo) {
+      if (STACK_NAME_HDP.equals(ri.getRepoName())) {
+        assertFalse(ri.getBaseUrl().equals(ri.getDefaultBaseUrl()));
+        assertEquals(ri.getBaseUrl(), ri.getLatestBaseUrl());
+      }
+    }
+  }
+
+  @Test
+  public void testGetRepositoryNoInternetDefault() throws Exception {
+    // Scenario: user has no internet and does nothing to repos via api
+    // use the default
+    String buildDir = tmpFolder.getRoot().getAbsolutePath();
+    AmbariMetaInfo ambariMetaInfo = setupTempAmbariMetaInfo(buildDir);
+    // The current stack already has (HDP, 2.1.1, redhat6).
+
+    // Deleting the json file referenced by the latestBaseUrl to simulate No
+    // Internet.
+    File latestUrlFile = new File(buildDir,
+        "ambari-metaInfo/HDP/2.1.1/repos/hdp.json");
+    FileUtils.deleteQuietly(latestUrlFile);
+    assertTrue(!latestUrlFile.exists());
+    ambariMetaInfo.init();
+
+    List<RepositoryInfo> redhat6Repo = ambariMetaInfo.getRepositories(
+        STACK_NAME_HDP, "2.1.1", "redhat6");
+    assertNotNull(redhat6Repo);
+    for (RepositoryInfo ri : redhat6Repo) {
+      if (STACK_NAME_HDP.equals(ri.getRepoName())) {
+        // baseUrl should be same as defaultBaseUrl since No Internet to load the
+        // latestBaseUrl from the json file.
+        assertEquals(ri.getBaseUrl(), ri.getDefaultBaseUrl());
+      }
+    }
+  }
+
+  @Test
+  public void testGetRepositoryUpdatedBaseUrl() throws Exception {
+    // Scenario: user has internet and but calls to set repos via api
+    // use whatever they set
+    String buildDir = tmpFolder.getRoot().getAbsolutePath();
+    AmbariMetaInfo ambariMetaInfo = setupTempAmbariMetaInfo(buildDir);
+    // The current stack already has (HDP, 2.1.1, redhat6)
+
+    // Updating the baseUrl
+    String newBaseUrl = "http://myprivate-repo-1.hortonworks.com/HDP/centos6/2.x/updates/2.0.6.0";
+    ambariMetaInfo.updateRepoBaseURL(STACK_NAME_HDP, "2.1.1", "redhat6",
+        STACK_NAME_HDP + "-2.1.1", newBaseUrl);
+    RepositoryInfo repoInfo = ambariMetaInfo.getRepository(STACK_NAME_HDP, "2.1.1", "redhat6",
+        STACK_NAME_HDP + "-2.1.1");
+    assertEquals(newBaseUrl, repoInfo.getBaseUrl());
+    String prevBaseUrl = repoInfo.getDefaultBaseUrl();
+    ambariMetaInfo.init();
+
+    List<RepositoryInfo> redhat6Repo = ambariMetaInfo.getRepositories(
+        STACK_NAME_HDP, "2.1.1", "redhat6");
+    assertNotNull(redhat6Repo);
+    for (RepositoryInfo ri : redhat6Repo) {
+      if (STACK_NAME_HDP.equals(ri.getRepoName())) {
+        assertEquals(newBaseUrl, ri.getBaseUrl());
+        // defaultBaseUrl and baseUrl should not be same, since it is updated.
+        assertFalse(ri.getBaseUrl().equals(ri.getDefaultBaseUrl()));
+      }
+    }
+
+    // Reset the database with the original baseUrl
+    ambariMetaInfo.updateRepoBaseURL(STACK_NAME_HDP, "2.1.1", "redhat6",
+        STACK_NAME_HDP + "-2.1.1", prevBaseUrl);
+  }
+
+  @Test
+  public void testGetRepositoryNoInternetUpdatedBaseUrl() throws Exception {
+    // Scenario: user has no internet and but calls to set repos via api
+    // use whatever they set
+    String buildDir = tmpFolder.getRoot().getAbsolutePath();
+    AmbariMetaInfo ambariMetaInfo = setupTempAmbariMetaInfo(buildDir);
+    // The current stack already has (HDP, 2.1.1, redhat6).
+
+    // Deleting the json file referenced by the latestBaseUrl to simulate No
+    // Internet.
+    File latestUrlFile = new File(buildDir,
+        "ambari-metaInfo/HDP/2.1.1/repos/hdp.json");
+    FileUtils.deleteQuietly(latestUrlFile);
+    assertTrue(!latestUrlFile.exists());
+
+    // Update baseUrl
+    String newBaseUrl = "http://myprivate-repo-1.hortonworks.com/HDP/centos6/2.x/updates/2.0.6.0";
+    ambariMetaInfo.updateRepoBaseURL("HDP", "2.1.1", "redhat6", "HDP-2.1.1",
+        newBaseUrl);
+    RepositoryInfo repoInfo = ambariMetaInfo.getRepository(STACK_NAME_HDP, "2.1.1", "redhat6",
+        STACK_NAME_HDP + "-2.1.1");
+    assertEquals(newBaseUrl, repoInfo.getBaseUrl());
+    String prevBaseUrl = repoInfo.getDefaultBaseUrl();
+    ambariMetaInfo.init();
+
+    List<RepositoryInfo> redhat6Repo = ambariMetaInfo.getRepositories(
+        STACK_NAME_HDP, "2.1.1", "redhat6");
+    assertNotNull(redhat6Repo);
+    for (RepositoryInfo ri : redhat6Repo) {
+      if (STACK_NAME_HDP.equals(ri.getRepoName())) {
+        // baseUrl should point to the updated baseUrl
+        assertEquals(newBaseUrl, ri.getBaseUrl());
+        assertFalse(ri.getDefaultBaseUrl().equals(ri.getBaseUrl()));
+      }
+    }
+
+    // Reset the database with the original baseUrl
+    ambariMetaInfo.updateRepoBaseURL(STACK_NAME_HDP, "2.1.1", "redhat6",
+        STACK_NAME_HDP + "-2.1.1", prevBaseUrl);
+  }
+
   @Test
   public void isSupportedStack() throws AmbariException {
     boolean supportedStack = metaInfo.isSupportedStack(STACK_NAME_HDP,
@@ -1501,58 +1617,15 @@ public class AmbariMetaInfoTest {
     assertNotNull(reporting.getWarning().getValue());
   }
 
-  /**
-   * @throws Exception
-   */
-  @Test
-  public void testAlertReconcile() throws Exception {
-    Clusters clusters = createMock(Clusters.class);
-    Cluster cluster = createMock(Cluster.class);
-    AlertDefinitionDAO dao = createMock(AlertDefinitionDAO.class);
-
-    metaInfo.alertDefinitionDao = dao;
-    Set<AlertDefinition> alertDefinitions = metaInfo.getAlertDefinitions(
-        STACK_NAME_HDP, "2.0.6", "HDFS");
-
-    assertEquals(4, alertDefinitions.size());
-
-    Map<String, Cluster> clustersMap = new HashMap<String, Cluster>();
-    clustersMap.put("c1", cluster);
-
-    StackId stackId = new StackId(STACK_NAME_HDP, "2.0.6");
-    Map<String, Service> clusterServiceMap = new HashMap<String, Service>();
-    clusterServiceMap.put("HDFS", null);
-
-    List<AlertDefinitionEntity> entities = new ArrayList<AlertDefinitionEntity>();
-
-    expect(clusters.getClusters()).andReturn(clustersMap).anyTimes();
-    expect(clusters.getCluster((String) anyObject())).andReturn(cluster).atLeastOnce();
-    expect(cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
-    expect(cluster.getDesiredStackVersion()).andReturn(stackId).anyTimes();
-    expect(cluster.getServices()).andReturn(clusterServiceMap).anyTimes();
-    expect(dao.findAll(EasyMock.anyInt())).andReturn(entities).atLeastOnce();
-    dao.createOrUpdate(EasyMock.anyObject(AlertDefinitionEntity.class));
-    EasyMock.expectLastCall().times(4);
-
-    EasyMock.replay(clusters, cluster, dao);
-    metaInfo.reconcileAlertDefinitions(clusters);
-    EasyMock.verify(cluster, cluster, dao);
-
-    AlertDefinitionFactory alertDefinitionFactory = injector.getInstance(AlertDefinitionFactory.class);
-    for (AlertDefinition definition : alertDefinitions) {
-      entities.add(alertDefinitionFactory.coerce(1, definition));
-    }
-
-    EasyMock.reset(clusters, cluster, dao);
-    expect(clusters.getClusters()).andReturn(clustersMap).anyTimes();
-    expect(clusters.getCluster((String) anyObject())).andReturn(cluster).atLeastOnce();
-    expect(cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
-    expect(cluster.getDesiredStackVersion()).andReturn(stackId).anyTimes();
-    expect(cluster.getServices()).andReturn(clusterServiceMap).anyTimes();
-    expect(dao.findAll(EasyMock.anyInt())).andReturn(entities).atLeastOnce();
-
-    EasyMock.replay(clusters, cluster, dao);
-    metaInfo.reconcileAlertDefinitions(clusters);
-    EasyMock.verify(cluster, cluster, dao);
+  private AmbariMetaInfo setupTempAmbariMetaInfo(String buildDir)
+      throws Exception {
+    File stackRootTmp = new File(buildDir + "/ambari-metaInfo");
+    File stackRoot = new File("src/test/resources/stacks");
+    stackRootTmp.mkdir();
+    FileUtils.copyDirectory(stackRoot, stackRootTmp);
+    AmbariMetaInfo ambariMetaInfo = new AmbariMetaInfo(stackRootTmp, new File(
+        "target/version"));
+    injector.injectMembers(ambariMetaInfo);
+    return ambariMetaInfo;
   }
 }

+ 97 - 0
ambari-server/src/test/java/org/apache/ambari/server/api/services/LdapSyncEventServiceTest.java

@@ -0,0 +1,97 @@
+/**
+ * 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.api.services.parsers.RequestBodyParser;
+import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.apache.ambari.server.controller.spi.Resource;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * LdapSyncEventService tests.
+ */
+public class LdapSyncEventServiceTest extends BaseServiceTest {
+
+  public List<ServiceTestInvocation> getTestInvocations() throws Exception {
+    List<ServiceTestInvocation> listInvocations = new ArrayList<ServiceTestInvocation>();
+
+    //getEvent
+    LdapSyncEventService permissionService = new TestLdapSyncEventService();
+    Method m = permissionService.getClass().getMethod("getEvent", HttpHeaders.class, UriInfo.class, String.class);
+    Object[] args = new Object[] {getHttpHeaders(), getUriInfo(), "1"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, permissionService, m, args, null));
+
+    //getEvents
+    permissionService = new TestLdapSyncEventService();
+    m = permissionService.getClass().getMethod("getEvents", HttpHeaders.class, UriInfo.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.GET, permissionService, m, args, null));
+
+    //createEvent
+    permissionService = new TestLdapSyncEventService();
+    m = permissionService.getClass().getMethod("createEvent", String.class, HttpHeaders.class, UriInfo.class);
+    args = new Object[] {"body", getHttpHeaders(), getUriInfo()};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.POST, permissionService, m, args, "body"));
+
+    //deleteEvent
+    permissionService = new TestLdapSyncEventService();
+    m = permissionService.getClass().getMethod("deleteEvent", HttpHeaders.class, UriInfo.class, String.class);
+    args = new Object[] {getHttpHeaders(), getUriInfo(), "1"};
+    listInvocations.add(new ServiceTestInvocation(Request.Type.DELETE, permissionService, m, args, null));
+
+    return listInvocations;
+  }
+
+
+  private class TestLdapSyncEventService extends LdapSyncEventService {
+
+    private TestLdapSyncEventService() {
+      super();
+    }
+
+    @Override
+    protected ResourceInstance createResource(Resource.Type type, Map<Resource.Type, String> mapIds) {
+      return getTestResource();
+    }
+
+    @Override
+    RequestFactory getRequestFactory() {
+      return getTestRequestFactory();
+    }
+
+    @Override
+    protected RequestBodyParser getBodyParser() {
+      return getTestBodyParser();
+    }
+
+    @Override
+    protected ResultSerializer getResultSerializer() {
+      return getTestResultSerializer();
+    }
+  }
+  
+  
+}

+ 114 - 0
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/LdapSyncEventResourceProviderTest.java

@@ -0,0 +1,114 @@
+/**
+ * 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 org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createNiceMock;
+
+/**
+ * LdapSyncEventResourceProvider tests.
+ */
+public class LdapSyncEventResourceProviderTest {
+  @Test
+  public void testCreateResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+    Map<String, Object> propertyMap = new HashMap<String, Object>();
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    propertyMap.put(LdapSyncEventResourceProvider.EVENT_SPECS_PROPERTY_ID, specs);
+    properties.add(propertyMap);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+  }
+
+  @Test
+  public void testGetResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+    Map<String, Object> propertyMap = new HashMap<String, Object>();
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    propertyMap.put(LdapSyncEventResourceProvider.EVENT_SPECS_PROPERTY_ID, specs);
+    properties.add(propertyMap);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+
+    Set<Resource> resources = provider.getResources(PropertyHelper.getReadRequest(), null);
+    Assert.assertEquals(1, resources.size());
+  }
+
+  @Test
+  public void testUpdateResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Request request = createNiceMock(Request.class);
+
+    try {
+      provider.updateResources(request, null);
+      Assert.fail("expected UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      // expected
+    }
+  }
+
+  @Test
+  public void testDeleteResources() throws Exception {
+    LdapSyncEventResourceProvider provider = new TestLdapSyncEventResourceProvider();
+
+    Set<Map<String, Object>> properties = new HashSet<Map<String, Object>>();
+    Map<String, Object> propertyMap = new HashMap<String, Object>();
+
+    Set<Map<String, String>> specs = new HashSet<Map<String, String>>();
+
+    propertyMap.put(LdapSyncEventResourceProvider.EVENT_SPECS_PROPERTY_ID, specs);
+    properties.add(propertyMap);
+
+    provider.createResources(PropertyHelper.getCreateRequest(properties, null));
+
+    Set<Resource> resources = provider.getResources(PropertyHelper.getReadRequest(), null);
+    Assert.assertEquals(1, resources.size());
+
+    provider.deleteResources(null);
+
+    resources = provider.getResources(PropertyHelper.getReadRequest(), null);
+    Assert.assertEquals(0, resources.size());
+  }
+
+
+  protected static class TestLdapSyncEventResourceProvider extends LdapSyncEventResourceProvider {
+    @Override
+    protected void ensureEventProcessor() {
+    }
+  }
+}

+ 166 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncEventEntityTest.java

@@ -0,0 +1,166 @@
+/**
+ * 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.orm.entities;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * LdapSyncEventEntity tests.
+ */
+public class LdapSyncEventEntityTest {
+  @Test
+  public void testGetId() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    Assert.assertEquals(1L, event.getId());
+  }
+
+  @Test
+  public void testSetGetStatus() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Pending, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Running);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Running, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Complete);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Complete, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Error);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Error, event.getStatus());
+
+    event.setStatus(LdapSyncEventEntity.Status.Pending);
+    Assert.assertEquals(LdapSyncEventEntity.Status.Pending, event.getStatus());
+  }
+
+  @Test
+  public void testSetGetStatusDetail() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setStatusDetail("some detail");
+    Assert.assertEquals("some detail", event.getStatusDetail());
+  }
+
+  @Test
+  public void testSetGetSpecs() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    LdapSyncSpecEntity spec = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Groups,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+
+    event.setSpecs(Collections.singletonList(spec));
+
+    List<LdapSyncSpecEntity> specs = event.getSpecs();
+    Assert.assertEquals(1, specs.size());
+
+    Assert.assertEquals(spec, specs.get(0));
+  }
+
+  @Test
+  public void testSetGetStartTime() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setStartTime(10001000L);
+    Assert.assertEquals(10001000L, event.getStartTime());
+  }
+
+  @Test
+  public void testSetGetEndTime() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setEndTime(90009000L);
+    Assert.assertEquals(90009000L, event.getEndTime());
+  }
+
+  @Test
+  public void testSetGetUsersFetched() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersFetched(99);
+    Assert.assertEquals(Integer.valueOf(99), event.getUsersFetched());
+  }
+
+  @Test
+  public void testSetGetUsersCreated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersCreated(98);
+    Assert.assertEquals(Integer.valueOf(98), event.getUsersCreated());
+  }
+
+  @Test
+  public void testSetGetUsersUpdated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersUpdated(97);
+    Assert.assertEquals(Integer.valueOf(97), event.getUsersUpdated());
+  }
+
+  @Test
+  public void testSetGetUsersRemoved() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setUsersRemoved(96);
+    Assert.assertEquals(Integer.valueOf(96), event.getUsersRemoved());
+  }
+
+  @Test
+  public void testSetGetGroupsFetched() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsFetched(95);
+    Assert.assertEquals(Integer.valueOf(95), event.getGroupsFetched());
+  }
+
+  @Test
+  public void testSetGetGroupsCreated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsCreated(94);
+    Assert.assertEquals(Integer.valueOf(94), event.getGroupsCreated());
+  }
+
+  @Test
+  public void testSetGetGroupsUpdated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsUpdated(93);
+    Assert.assertEquals(Integer.valueOf(93), event.getGroupsUpdated());
+  }
+
+  @Test
+  public void testSetGetGroupsRemoved() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setGroupsRemoved(92);
+    Assert.assertEquals(Integer.valueOf(92), event.getGroupsRemoved());
+  }
+
+  @Test
+  public void testSetGetMembershipsFetched() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setMembershipsFetched(91);
+    Assert.assertEquals(Integer.valueOf(91), event.getMembershipsFetched());
+  }
+
+  @Test
+  public void testSetGetMembershipsCreated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setMembershipsCreated(90);
+    Assert.assertEquals(Integer.valueOf(90), event.getMembershipsCreated());
+  }
+
+  @Test
+  public void testSetGetMembershipsUpdated() throws Exception {
+    LdapSyncEventEntity event = new LdapSyncEventEntity(1L);
+    event.setMembershipsUpdated(99);
+    Assert.assertEquals(Integer.valueOf(99), event.getMembershipsUpdated());
+  }
+}

+ 95 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/entities/LdapSyncSpecEntityTest.java

@@ -0,0 +1,95 @@
+/**
+ * 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.orm.entities;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * LdapSyncSpecEntity tests.
+ */
+public class LdapSyncSpecEntityTest {
+  @Test
+  public void testGetPrincipalType() throws Exception {
+    LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.PrincipalType.Users, entity.getPrincipalType());
+
+    entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Groups,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.PrincipalType.Groups, entity.getPrincipalType());
+  }
+
+  @Test
+  public void testGetSyncType() throws Exception {
+    LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.All, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.SyncType.All, entity.getSyncType());
+
+    entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.Existing, Collections.<String>emptyList());
+    Assert.assertEquals(LdapSyncSpecEntity.SyncType.Existing, entity.getSyncType());
+  }
+
+  @Test
+  public void testGetPrincipalNames() throws Exception {
+    List<String> names = new LinkedList<String>();
+    names.add("joe");
+    names.add("fred");
+
+    LdapSyncSpecEntity entity = new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+        LdapSyncSpecEntity.SyncType.Specific, names);
+    Assert.assertEquals(names, entity.getPrincipalNames());
+  }
+
+  @Test
+  public void testIllegalConstruction() throws Exception {
+    try {
+      new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+          LdapSyncSpecEntity.SyncType.Specific, Collections.<String>emptyList());
+      Assert.fail("expected IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    List<String> names = new LinkedList<String>();
+    names.add("joe");
+    names.add("fred");
+
+    try {
+      new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+          LdapSyncSpecEntity.SyncType.All, names);
+      Assert.fail("expected IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    try {
+      new LdapSyncSpecEntity(LdapSyncSpecEntity.PrincipalType.Users,
+          LdapSyncSpecEntity.SyncType.Existing, names);
+      Assert.fail("expected IllegalArgumentException");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+  }
+}

+ 14 - 0
ambari-server/src/test/java/org/apache/ambari/server/proxy/ProxyServiceTest.java

@@ -65,6 +65,7 @@ class ProxyServiceTest extends BaseServiceTest {
     URLStreamProvider streamProviderMock = PowerMock.createNiceMock(URLStreamProvider.class);
     HttpURLConnection urlConnectionMock = createMock(HttpURLConnection.class);
     URI uriMock = PowerMock.createMock(URI.class);
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     Map<String, List<String>> headerParamsToForward = new HashMap<String, List<String>>();
     Response.ResponseBuilder responseBuilderMock = PowerMock.createMock(ResponseBuilderImpl.class);
@@ -79,6 +80,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uriMock);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(uriMock.getQuery()).andReturn("url=testurl");
     expect(streamProviderMock.processURL("testurl", "GET", null, headerParamsToForward)).andReturn(urlConnectionMock);
     expect(urlConnectionMock.getResponseCode()).andReturn(200);
@@ -101,6 +103,7 @@ class ProxyServiceTest extends BaseServiceTest {
     URLStreamProvider streamProviderMock = PowerMock.createNiceMock(URLStreamProvider.class);
     HttpURLConnection urlConnectionMock = createMock(HttpURLConnection.class);
     URI uriMock = PowerMock.createMock(URI.class);
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     Map<String, List<String>> headerParamsToForward = new HashMap<String, List<String>>();
     Response.ResponseBuilder responseBuilderMock = PowerMock.createMock(ResponseBuilderImpl.class);
@@ -115,6 +118,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uriMock);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(uriMock.getQuery()).andReturn("url=testurl");
     expect(getHttpHeaders().getMediaType()).andReturn(APPLICATION_FORM_URLENCODED_TYPE);
     expect(streamProviderMock.processURL("testurl", "POST", is, headerParamsToForward)).andReturn(urlConnectionMock);
@@ -138,6 +142,7 @@ class ProxyServiceTest extends BaseServiceTest {
     URLStreamProvider streamProviderMock = PowerMock.createNiceMock(URLStreamProvider.class);
     HttpURLConnection urlConnectionMock = createMock(HttpURLConnection.class);
     URI uriMock = PowerMock.createMock(URI.class);
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     Map<String, List<String>> headerParamsToForward = new HashMap<String, List<String>>();
     Response.ResponseBuilder responseBuilderMock = PowerMock.createMock(ResponseBuilderImpl.class);
@@ -152,6 +157,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uriMock);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(uriMock.getQuery()).andReturn("url=testurl");
     expect(getHttpHeaders().getMediaType()).andReturn(APPLICATION_FORM_URLENCODED_TYPE);
     expect(streamProviderMock.processURL("testurl", "PUT", is, headerParamsToForward)).andReturn(urlConnectionMock);
@@ -175,6 +181,7 @@ class ProxyServiceTest extends BaseServiceTest {
     URLStreamProvider streamProviderMock = PowerMock.createNiceMock(URLStreamProvider.class);
     HttpURLConnection urlConnectionMock = createMock(HttpURLConnection.class);
     URI uriMock = PowerMock.createMock(URI.class);
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     Map<String, List<String>> headerParamsToForward = new HashMap<String, List<String>>();
     Response.ResponseBuilder responseBuilderMock = PowerMock.createMock(ResponseBuilderImpl.class);
@@ -189,6 +196,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uriMock);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(uriMock.getQuery()).andReturn("url=testurl");
     expect(streamProviderMock.processURL("testurl", "DELETE", null, headerParamsToForward)).andReturn(urlConnectionMock);
     expect(urlConnectionMock.getResponseCode()).andReturn(200);
@@ -214,6 +222,7 @@ class ProxyServiceTest extends BaseServiceTest {
     URI uriMock = PowerMock.createMock(URI.class);
     Response responseMock = createMock(ResponseImpl.class);
     InputStream es = new ByteArrayInputStream("error".getBytes());
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     Map<String, List<String>> headerParamsToForward = new HashMap<String, List<String>>();
     headerParams.add("AmbariProxy-User-Remote","testuser");
@@ -225,6 +234,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uriMock);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(uriMock.getQuery()).andReturn("url=testurl");
     expect(streamProviderMock.processURL("testurl", "GET", null, headerParamsToForward)).andReturn(urlConnectionMock);
     expect(urlConnectionMock.getResponseCode()).andReturn(400).times(2);
@@ -247,6 +257,7 @@ class ProxyServiceTest extends BaseServiceTest {
     URLStreamProvider streamProviderMock = PowerMock.createNiceMock(URLStreamProvider.class);
     HttpURLConnection urlConnectionMock = createMock(HttpURLConnection.class);
     URI uriMock = PowerMock.createMock(URI.class);
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     Map<String, List<String>> headerParamsToForward = new HashMap<String, List<String>>();
     Response.ResponseBuilder responseBuilderMock = PowerMock.createMock(ResponseBuilderImpl.class);
@@ -261,6 +272,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uriMock);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(uriMock.getQuery()).andReturn("url=testurl");
     expect(streamProviderMock.processURL("testurl", "GET", null, headerParamsToForward)).andReturn(urlConnectionMock);
     expect(urlConnectionMock.getResponseCode()).andReturn(200);
@@ -281,6 +293,7 @@ class ProxyServiceTest extends BaseServiceTest {
   public void testEscapedURL() throws Exception {
     ProxyService ps = new ProxyService();
     URLStreamProvider streamProviderMock = PowerMock.createNiceMock(URLStreamProvider.class);
+    MultivaluedMap<String, String> queryParams = new MultivaluedMapImpl();
     MultivaluedMap<String, String> headerParams = new MultivaluedMapImpl();
     HttpURLConnection urlConnectionMock = createMock(HttpURLConnection.class);
     URI uri = UriBuilder.fromUri("http://dev01.hortonworks.com:8080/proxy?url=http%3a%2f%2fserver%3a8188%2fws%2fv1%2f" +
@@ -295,6 +308,7 @@ class ProxyServiceTest extends BaseServiceTest {
     expect(getHttpHeaders().getRequestHeaders()).andReturn(headerParams);
     expect(getHttpHeaders().getRequestHeader("AmbariProxy-User-Remote")).andReturn(userRemoteParams);
     expect(getUriInfo().getRequestUri()).andReturn(uri);
+    expect(getUriInfo().getQueryParameters()).andReturn(queryParams);
     expect(urlConnectionMock.getResponseCode()).andReturn(200);
     expect(urlConnectionMock.getContentType()).andReturn("text/plain");
     expect(urlConnectionMock.getInputStream()).andReturn(is);

File diff suppressed because it is too large
+ 1271 - 32
ambari-server/src/test/java/org/apache/ambari/server/security/ldap/AmbariLdapDataPopulatorTest.java


+ 6 - 4
ambari-server/src/test/java/org/apache/ambari/server/security/ldap/LdapPerformanceTest.java

@@ -81,14 +81,16 @@ public class LdapPerformanceTest {
     }
     System.out.println("Data fetch: " + (System.currentTimeMillis() - time));
     time = System.currentTimeMillis();
-    LdapBatchDto batchDto = populator.synchronizeLdapUsersAndGroups(userNames, groupNames);
-    batchDto = populator.synchronizeLdapUsersAndGroups(userNames, groupNames);
+    LdapBatchDto batchDto = new LdapBatchDto();
+    populator.synchronizeLdapUsers(userNames, batchDto);
+    populator.synchronizeLdapGroups(groupNames, batchDto);
     this.users.processLdapSync(batchDto);
     System.out.println("Initial sync: " + (System.currentTimeMillis() - time));
     time = System.currentTimeMillis();
-    batchDto = populator.synchronizeLdapUsersAndGroups(userNames, groupNames);
+    batchDto = new LdapBatchDto();
+    populator.synchronizeLdapUsers(userNames, batchDto);
+    populator.synchronizeLdapGroups(groupNames, batchDto);
     this.users.processLdapSync(batchDto);
     System.out.println("Subsequent sync: " + (System.currentTimeMillis() - time));
-    time = System.currentTimeMillis();
   }
 }

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog161Test.java

@@ -172,7 +172,7 @@ public class UpgradeCatalog161Test {
             "reducer=1000000000\npig.exec.reducers.max=999\n\n# Temporary location to store the intermediate " +
             "data.\npig.temp.dir=/tmp/\n\n# Threshold for merging FRJoin fragment files\npig.files.concatenation." +
             "threshold=100\npig.optimistic.files.concatenation=false;\n\npig.disable.counter=false\n\n" +
-            "hcat.bin=/usr/bin/hcat"), true, true);
+            "hcat.bin=/usr/bin/hcat"), true, false);
     expectLastCall();
 
     replay(upgradeCatalog, dbAccessor, configuration, injector, provider, em,

+ 6 - 29
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog170Test.java

@@ -423,9 +423,11 @@ public class UpgradeCatalog170Test {
     Method m = AbstractUpgradeCatalog.class.getDeclaredMethod
         ("updateConfigurationProperties", String.class, Map.class, boolean.class, boolean.class);
     Method n = AbstractUpgradeCatalog.class.getDeclaredMethod("getEntityManagerProvider");
-
+    Method l = AbstractUpgradeCatalog.class.getDeclaredMethod
+        ("addNewConfigurationsFromXml");
+    
     UpgradeCatalog170 upgradeCatalog = createMockBuilder(UpgradeCatalog170.class)
-      .addMockedMethod(m).addMockedMethod(n).createMock();
+      .addMockedMethod(m).addMockedMethod(n).addMockedMethod(l).createMock();
 
     List<ConfigGroupConfigMappingEntity> configGroupConfigMappingEntities =
             new ArrayList<ConfigGroupConfigMappingEntity>();
@@ -456,34 +458,10 @@ public class UpgradeCatalog170Test {
     contentOfHadoopEnv.put("content", "env file contents");
 
     upgradeCatalog.updateConfigurationProperties("hadoop-env",
-        globalConfigs, true, true);
-    expectLastCall();
-
-    upgradeCatalog.updateConfigurationProperties("hadoop-env",
-        contentOfHadoopEnv, true, true);
-    expectLastCall();
-
-    upgradeCatalog.updateConfigurationProperties("hbase-env",
-        Collections.singletonMap("hbase_regionserver_xmn_max", "512"), false, false);
-    expectLastCall();
-
-    upgradeCatalog.updateConfigurationProperties("hbase-env",
-        Collections.singletonMap("hbase_regionserver_xmn_ratio", "0.2"), false, false);
-    expectLastCall();
-
-    upgradeCatalog.updateConfigurationProperties("yarn-env",
-        Collections.singletonMap("min_user_id", "1000"), false, false);
-    expectLastCall();
-
-    upgradeCatalog.updateConfigurationProperties("sqoop-env", Collections.singletonMap("sqoop_user", "sqoop"), false, false);
-    expectLastCall();
-
-    upgradeCatalog.updateConfigurationProperties("hadoop-env",
-            Collections.singletonMap("hadoop_root_logger", "INFO,RFA"), false, false);
+        globalConfigs, false, true);
     expectLastCall();
 
-    upgradeCatalog.updateConfigurationProperties("oozie-env",
-            Collections.singletonMap("oozie_admin_port", "11001"), false, false);
+    upgradeCatalog.addNewConfigurationsFromXml();
     expectLastCall();
 
     expect(dbAccessor.executeSelect("SELECT role_name, user_id FROM user_roles")).andReturn(userRolesResultSet).once();
@@ -521,7 +499,6 @@ public class UpgradeCatalog170Test {
     expect(configHelper.findConfigTypesByPropertyName(new StackId("HDP", "2.1"), "smokeuser_keytab", "c1")).andReturn(new HashSet<String>()).once();
     expect(configHelper.findConfigTypesByPropertyName(new StackId("HDP", "2.1"), "content", "c1")).andReturn(envDicts).once();
     expect(configHelper.findConfigTypesByPropertyName(new StackId("HDP", "2.1"), "dtnode_heapsize", "c1")).andReturn(configTypes).once();
-    expect(configHelper.getPropertyValueFromStackDefenitions(cluster, "hadoop-env", "content")).andReturn("env file contents").once();
 
     expect(injector.getInstance(ConfigGroupConfigMappingDAO.class)).andReturn(configGroupConfigMappingDAO).anyTimes();
     expect(injector.getInstance(UserDAO.class)).andReturn(userDAO).anyTimes();

+ 128 - 0
ambari-server/src/test/java/org/apache/ambari/server/view/HttpImpersonatorImplTest.java

@@ -0,0 +1,128 @@
+/**
+ * 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.view;
+
+import junit.framework.TestCase;
+import org.apache.ambari.view.ImpersonatorSetting;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.server.controller.internal.AppCookieManager;
+import org.junit.Assert;
+import org.mockito.Mockito;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.UUID;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.when;
+
+
+public class HttpImpersonatorImplTest extends TestCase {
+
+  String cookie;
+  String username;
+  ViewContext viewContext;
+  HttpImpersonatorImpl impersonator;
+  String expectedResult;
+
+  public void setUp() throws Exception {
+    String uuid = UUID.randomUUID().toString().replace("-", "");
+    this.cookie = uuid;
+    this.username = "admin" + uuid;
+
+    AppCookieManager mockAppCookieManager = Mockito.mock(AppCookieManager.class);
+    when(mockAppCookieManager.getAppCookie(anyString(), anyBoolean())).thenReturn(cookie);
+
+    this.expectedResult = "Dummy text from HTTP response";
+    BufferedReader mockBufferedReader = Mockito.mock(BufferedReader.class);
+    when(mockBufferedReader.readLine()).thenReturn(expectedResult).thenReturn(null);
+
+    HttpImpersonatorImpl.FactoryHelper mockFactory = Mockito.mock(HttpImpersonatorImpl.FactoryHelper.class);
+    when(mockFactory.makeBR(any(InputStreamReader.class))).thenReturn(mockBufferedReader);
+
+    this.viewContext = Mockito.mock(ViewContext.class);
+    when(this.viewContext.getUsername()).thenReturn(username);
+
+    this.impersonator = new HttpImpersonatorImpl(this.viewContext, mockAppCookieManager, mockFactory);
+    when(this.viewContext.getHttpImpersonator()).thenReturn(this.impersonator);
+  }
+
+  @org.junit.Test
+  public void testBasic() throws Exception {
+    String urlToRead = "http://foo.com";
+    String requestMethod = "GET";
+    URL url = new URL(urlToRead);
+
+    // Test default params
+    HttpURLConnection conn1 = (HttpURLConnection) url.openConnection();
+
+    conn1 = this.viewContext.getHttpImpersonator().doAs(conn1, requestMethod);
+    Assert.assertEquals(requestMethod, conn1.getRequestMethod());
+    Assert.assertEquals(username, conn1.getRequestProperty("doAs"));
+
+    // Test specific params
+    HttpURLConnection conn2 = (HttpURLConnection) url.openConnection();
+    conn2 = this.viewContext.getHttpImpersonator().doAs(conn1, requestMethod, "admin", "username");
+    Assert.assertEquals(requestMethod, conn1.getRequestMethod());
+    Assert.assertEquals("admin", conn1.getRequestProperty("username"));
+  }
+
+  @org.junit.Test
+  public void testRequestURL() throws Exception {
+    String urlToRead = "http://foo.com";
+    String requestMethod = "GET";
+
+    // Test default params
+    ImpersonatorSetting impersonatorSetting = new ImpersonatorSettingImpl(this.viewContext);
+    when(this.viewContext.getImpersonatorSetting()).thenReturn(impersonatorSetting);
+    String actualResult = this.viewContext.getHttpImpersonator().requestURL(urlToRead, requestMethod, impersonatorSetting);
+    Assert.assertEquals(this.expectedResult, actualResult);
+  }
+
+  @org.junit.Test
+  public void testRequestURLWithCustom() throws Exception {
+    String urlToRead = "http://foo.com";
+    String requestMethod = "GET";
+
+    // Test custom params
+    ImpersonatorSetting impersonatorSetting = new ImpersonatorSettingImpl(this.viewContext);
+    impersonatorSetting.setDoAsParamName("impersonate");
+    impersonatorSetting.setUsername("hive");
+    when(this.viewContext.getImpersonatorSetting()).thenReturn(impersonatorSetting);
+    String actualResult = this.viewContext.getHttpImpersonator().requestURL(urlToRead, requestMethod, impersonatorSetting);
+    Assert.assertEquals(this.expectedResult, actualResult);
+  }
+
+  @org.junit.Test
+  public void testInvalidURL() throws Exception {
+    String urlToRead = "http://foo.com?" + "doAs" + "=hive";
+    String requestMethod = "GET";
+    URL url = new URL(urlToRead);
+
+    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+    try {
+      conn = this.viewContext.getHttpImpersonator().doAs(conn, requestMethod);
+      fail("Expected an exception to be thrown." );
+    } catch(Exception e) {
+      ;
+    }
+  }
+}

+ 20 - 0
ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java

@@ -518,6 +518,26 @@ public class ViewRegistryTest {
     Assert.assertNull(listener.getLastEvent());
   }
 
+  @Test
+  public void testGetResourceProviders() throws Exception {
+
+    ViewConfig config = ViewConfigTest.getConfig();
+
+    ViewEntity viewDefinition = ViewEntityTest.getViewEntity();
+
+    ViewRegistry registry = ViewRegistry.getInstance();
+
+    registry.setupViewDefinition(viewDefinition, config, getClass().getClassLoader());
+
+    Map<Resource.Type, ResourceProvider> providerMap = registry.getResourceProviders();
+
+    Assert.assertEquals(3, providerMap.size());
+
+    Assert.assertTrue(providerMap.containsKey(Resource.Type.valueOf("MY_VIEW{1.0.0}/resource")));
+    Assert.assertTrue(providerMap.containsKey(Resource.Type.valueOf("MY_VIEW{1.0.0}/subresource")));
+    Assert.assertTrue(providerMap.containsKey(Resource.Type.valueOf("MY_VIEW{1.0.0}/resources")));
+  }
+
   @Test
   public void testAddGetDefinitions() throws Exception {
     ViewEntity viewDefinition = ViewEntityTest.getViewEntity();

+ 4 - 25
ambari-server/src/test/python/stacks/2.2/SLIDER/test_slider_client.py

@@ -38,30 +38,9 @@ class TestSliderClient(RMFTestCase):
                               configurations=self.getConfig()['configurations']['slider-client']
     )
 
-    self.assertResourceCalled('XmlConfig',
-                              'core-site.xml',
-                              conf_dir='/usr/lib/slider/conf',
-                              configurations=self.getConfig()['configurations']['core-site'],
-                              configuration_attributes=self.getConfig()['configuration_attributes']['core-site']
-    )
-
-    self.assertResourceCalled('XmlConfig',
-                              'hdfs-site.xml',
-                              conf_dir='/usr/lib/slider/conf',
-                              configurations=self.getConfig()['configurations']['hdfs-site'],
-                              configuration_attributes=self.getConfig()['configuration_attributes']['hdfs-site']
-    )
-
-    self.assertResourceCalled('XmlConfig',
-                              'yarn-site.xml',
-                              conf_dir='/usr/lib/slider/conf',
-                              configurations=self.getConfig()['configurations']['yarn-site'],
-                              configuration_attributes=self.getConfig()['configuration_attributes']['yarn-site']
-    )
-
     self.assertResourceCalled('File',
-                              '/usr/lib/slider/bin/slider-wrapper',
-                              content=Template('slider-wrapper.j2'),
+                              '/usr/lib/slider/conf/slider-env.sh',
+                              content=Template('slider-env.sh.j2'),
                               mode=0755
     )
 
@@ -83,7 +62,7 @@ class TestSliderClient(RMFTestCase):
     )
 
     self.assertResourceCalled('Execute',
-                              '/usr/bin/kinit -kt /etc/security/keytabs/smokeuser.headless.keytab ambari-qa; /usr/lib/slider/bin/slider-wrapper list',
+                              '/usr/bin/kinit -kt /etc/security/keytabs/smokeuser.headless.keytab ambari-qa; /usr/lib/slider/bin/slider list',
                               logoutput=True,
                               tries=3,
                               user='ambari-qa',
@@ -99,7 +78,7 @@ class TestSliderClient(RMFTestCase):
                        config_file="default.json"
     )
 
-    self.assertResourceCalled('Execute', ' /usr/lib/slider/bin/slider-wrapper list',
+    self.assertResourceCalled('Execute', ' /usr/lib/slider/bin/slider list',
                               logoutput=True,
                               tries=3,
                               user='ambari-qa',

+ 53 - 0
ambari-views/src/main/java/org/apache/ambari/view/HttpImpersonator.java

@@ -0,0 +1,53 @@
+/**
+ * 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.view;
+
+import java.net.HttpURLConnection;
+
+/**
+ * Interface for views to impersonate users over HTTP request.
+ */
+public interface HttpImpersonator {
+
+  /**
+   * @param conn HTTP connection that will be modified and returned
+   * @param type HTTP Request type: GET, PUT, POST, DELETE, etc.
+   * @return HTTP Connection object with the "doAs" query param set to the currently logged on user.
+   */
+  public HttpURLConnection doAs(HttpURLConnection conn, String type);
+
+  /**
+   * @param conn HTTP connection that will be modified and returned
+   * @param type HTTP Request type: GET, PUT, POST, DELETE, etc.
+   * @param username Username to impersonate
+   * @param doAsParamName Query param, typically "doAs"
+   * @return HTTP Connection object with the doAs query param set to the provider username.
+   */
+  public HttpURLConnection doAs(HttpURLConnection conn, String type, String username, String doAsParamName);
+
+  /**
+   * Returns the result of the HTTP request by setting the "doAs" impersonation for the query param and username
+   * in @param impersonatorSetting.
+   * @param urlToRead URL to request
+   * @param requestType HTTP Request type: GET, PUT, POST, DELETE, etc.
+   * @param impersonatorSetting Setting class with default values for username and doAs param name.
+   *                           To use different values, call the setters of the object.
+   * @return Return a response as a String
+   */
+  public String requestURL(String urlToRead, String requestType, ImpersonatorSetting impersonatorSetting);
+}

+ 46 - 0
ambari-views/src/main/java/org/apache/ambari/view/ImpersonatorSetting.java

@@ -0,0 +1,46 @@
+/**
+ * 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.view;
+
+/**
+ * Interface that provides default values for impersonating, such as the username and doAs parameter name.
+ */
+public interface ImpersonatorSetting {
+
+  /**
+   * @return The parameter name used for "doAs" impersonation.
+   */
+  public String getDoAsParamName();
+
+  /**
+   * @return The username value that will be used for "doAs" impersonation.
+   */
+  public String getUsername();
+
+  /**
+   * Set the parameter name used for "doAs" impersonation.
+   * @param doAsParamName Query parameter name
+   */
+  public void setDoAsParamName(String doAsParamName);
+
+  /**
+   * Set the username value that will be used for "doAs" impersonation.
+   * @param username Username to impersonate as
+   */
+  public void setUsername(String username);
+}

+ 15 - 0
ambari-views/src/main/java/org/apache/ambari/view/ViewContext.java

@@ -20,6 +20,7 @@ package org.apache.ambari.view;
 
 import java.util.Collection;
 import java.util.Map;
+import org.apache.ambari.view.HttpImpersonator;
 
 /**
  * Context object available to the view components to provide access to
@@ -174,4 +175,18 @@ public interface ViewContext {
    * @return the view controller
    */
   public ViewController getController();
+
+  /**
+   * Get the HTTP Impersonator.
+   *
+   * @return the HTTP Impersonator, which internally uses the App Cookie Manager
+   */
+  public HttpImpersonator getHttpImpersonator();
+
+  /**
+   * Get the default settings for the Impersonator.
+   *
+   * @return the Impersonator settings.
+   */
+  public ImpersonatorSetting getImpersonatorSetting();
 }

+ 0 - 22
ambari-web/app/utils/ajax/ajax.js

@@ -1698,28 +1698,6 @@ var urls = {
       }
     }
   },
-
-  'jobs.lastID': {
-    'real': '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/HIVE_QUERY_ID?limit=1&secondaryFilter=tez:true',
-    'mock': 'data/jobs/hive-queries.json',
-    'apiPrefix': ''
-  },
-
-  'jobs.tezDag.NametoID': {
-    'real': '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:{tezDagName}',
-    'mock': '/data/jobs/tezDag-name-to-id.json',
-    'apiPrefix': ''
-  },
-  'jobs.tezDag.tezDagId': {
-    'real': '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/TEZ_DAG_ID/{tezDagId}?fields=relatedentities,otherinfo',
-    'mock': '/data/jobs/tezDag.json',
-    'apiPrefix': ''
-  },
-  'jobs.tezDag.tezDagVertexId': {
-    'real': '/proxy?url=http://{historyServerHostName}:{ahsWebPort}/ws/v1/timeline/TEZ_VERTEX_ID/{tezDagVertexId}?fields=otherinfo',
-    'mock': '/data/jobs/tezDagVertex.json',
-    'apiPrefix': ''
-  },
   'views.info': {
     'real': '/views',
     'mock': '/data/views/views.json'

+ 15 - 0
contrib/views/jobs/pom.xml

@@ -158,6 +158,7 @@
                 <directory>src/main/resources</directory>
                 <filtering>false</filtering>
                 <includes>
+                    <include>WEB-INF/web.xml</include>
                     <include>META-INF/**/*</include>
                     <include>view.xml</include>
                 </includes>
@@ -168,4 +169,18 @@
             </resource>
         </resources>
     </build>
+<dependencies>
+      <dependency>
+        <groupId>org.apache.ambari</groupId>
+        <artifactId>ambari-views</artifactId>
+        <version>${ambari.version}</version>
+      </dependency>
+      
+      <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>servlet-api</artifactId>
+        <version>2.5</version>
+        <scope>provided</scope>
+      </dependency>
+    </dependencies>
 </project>

+ 68 - 0
contrib/views/jobs/src/main/java/org/apache/ambari/view/jobs/ProxyServlet.java

@@ -0,0 +1,68 @@
+/**
+ * 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.view.jobs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.HttpImpersonator;
+import org.apache.ambari.view.ImpersonatorSetting;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Simple servlet for proxying requests with doAs impersonation.
+ */
+public class ProxyServlet extends HttpServlet {
+
+  private ViewContext viewContext;
+  private HttpImpersonator impersonator;
+  private ImpersonatorSetting impersonatorSetting;
+
+  @Override
+  public void init(ServletConfig config) throws ServletException {
+    super.init(config);
+
+    ServletContext context = config.getServletContext();
+    viewContext = (ViewContext) context.getAttribute(ViewContext.CONTEXT_ATTRIBUTE);
+
+    this.impersonator = viewContext.getHttpImpersonator();
+    this.impersonatorSetting = viewContext.getImpersonatorSetting();
+  }
+
+  @Override
+  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
+    String urlToRead = request.getParameter("url");
+    response.setContentType("text/html");
+    response.setStatus(HttpServletResponse.SC_OK);
+
+    // Getting the result is super simply by using the impersonator and the default values in the factory.
+    String result = this.impersonator.requestURL(urlToRead, "GET", this.impersonatorSetting);
+
+    PrintWriter writer = response.getWriter();
+    writer.print(result);
+  }
+}
+

+ 37 - 0
contrib/views/jobs/src/main/resources/WEB-INF/web.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<!--
+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. Kerberos, LDAP, Custom. Binary/Htt
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+         version="2.4">
+
+  <display-name>Proxy Application</display-name>
+  <description>
+    This is the proxy application.
+  </description>
+  <servlet>
+    <servlet-name>ProxyServlet</servlet-name>
+    <servlet-class>org.apache.ambari.view.jobs.ProxyServlet</servlet-class>
+  </servlet>
+  <servlet-mapping>
+    <servlet-name>ProxyServlet</servlet-name>
+    <url-pattern>/proxy</url-pattern>
+  </servlet-mapping>
+</web-app>

+ 8 - 2
contrib/views/jobs/src/main/resources/ui/app/scripts/controllers/jobs_controller.js

@@ -591,7 +591,10 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically, {
         name: 'jobs_lastID',
         sender: this,
         data: {
-          atsURL: atsURL
+          atsURL: atsURL,
+          view: App.get("view"),
+          version: App.get("version"),
+          instanceName: App.get("instanceName")
         },
         success: 'lastIDSuccessCallback',
         error : 'lastIDErrorCallback'
@@ -601,7 +604,10 @@ App.JobsController = Ember.ArrayController.extend(App.RunPeriodically, {
         sender: this,
         data: {
           atsURL: atsURL,
-          filtersLink: this.get('filterObject').createJobsFiltersLink()
+          filtersLink: this.get('filterObject').createJobsFiltersLink(),
+          view: App.get("view"),
+          version: App.get("version"),
+          instanceName: App.get("instanceName")
         },
         success: 'loadJobsSuccessCallback',
         error: 'loadJobsErrorCallback'

+ 19 - 6
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/ajax.js

@@ -27,41 +27,54 @@
  *  testInProduction - can this request be executed on production tests (used only in tests)
  *
  * @type {Object}
+ *
+ * Any property inside {braces} is substituted dynamically by the formatUrl function provided that the property is passed into the "data" dictionary
+ * by the ajax call.
+ * E.g.,
+   App.ajax.send({
+     name: 'key_foo',
+     data: {
+          property1: value1,
+          property2: value2
+     }
+   });
+
+   Where the "urls" dictionary contains 'key_foo': {real: 'some_value_with_{property1}_and_{property2}' }
  */
 var urls = {
 
   'load_jobs': {
-    real: '/proxy?url={atsURL}/ws/v1/timeline/HIVE_QUERY_ID{filtersLink}',
+    real: '/views/{view}/{version}/{instanceName}/proxy?url={atsURL}/ws/v1/timeline/HIVE_QUERY_ID{filtersLink}',
     mock: '/scripts/assets/hive-queries.json',
     apiPrefix: ''
   },
 
   'jobs_lastID': {
-    real: '/proxy?url={atsURL}/ws/v1/timeline/HIVE_QUERY_ID?limit=1&secondaryFilter=tez:true',
+    real: '/views/{view}/{version}/{instanceName}/proxy?url={atsURL}/ws/v1/timeline/HIVE_QUERY_ID?limit=1&secondaryFilter=tez:true',
     mock: '/scripts/assets/hive-queries.json',
     apiPrefix: ''
   },
 
   'job_details': {
-    real: '/proxy?url={atsURL}/ws/v1/timeline/HIVE_QUERY_ID/{job_id}?fields=events,otherinfo',
+    real: '/views/{view}/{version}/{instanceName}/proxy?url={atsURL}/ws/v1/timeline/HIVE_QUERY_ID/{job_id}?fields=events,otherinfo',
     mock: '/scripts/assets/hive-query-2.json',
     apiPrefix: ''
   },
 
   'jobs.tezDag.NametoID': {
-    'real': '/proxy?url={atsURL}/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:{tezDagName}',
+    'real': '/views/{view}/{version}/{instanceName}/proxy?url={atsURL}/ws/v1/timeline/TEZ_DAG_ID?primaryFilter=dagName:{tezDagName}',
     'mock': '/scripts/assets/tezDag-name-to-id.json',
     'apiPrefix': ''
   },
 
   'jobs.tezDag.tezDagId': {
-    'real': '/proxy?url={atsURL}/ws/v1/timeline/TEZ_DAG_ID/{tezDagId}?fields=relatedentities,otherinfo',
+    'real': '/views/{view}/{version}/{instanceName}/proxy?url={atsURL}/ws/v1/timeline/TEZ_DAG_ID/{tezDagId}?fields=relatedentities,otherinfo',
     'mock': '/scripts/assets/tezDag.json',
     'apiPrefix': ''
   },
 
   'jobs.tezDag.tezDagVertexId': {
-    'real': '/proxy?url={atsURL}/ws/v1/timeline/TEZ_VERTEX_ID/{tezDagVertexId}?fields=otherinfo',
+    'real': '/views/{view}/{version}/{instanceName}/proxy?url={atsURL}/ws/v1/timeline/TEZ_VERTEX_ID/{tezDagVertexId}?fields=otherinfo',
     'mock': '/scripts/assets/tezDagVertex.json',
     'apiPrefix': ''
   },

+ 12 - 3
contrib/views/jobs/src/main/resources/ui/app/scripts/helpers/jobs.js

@@ -88,7 +88,10 @@ App.Helpers.jobs = {
         sender: sender,
         data: {
           atsURL: params.atsURL,
-          tezDagName: tezDagName
+          tezDagName: tezDagName,
+          view: App.get("view"),
+          version: App.get("version"),
+          instanceName: App.get("instanceName")
         },
         success: 'dagNameToIdSuccess',
         error: 'dagNameToIdError'
@@ -146,7 +149,10 @@ App.Helpers.jobs = {
         sender: sender,
         data: {
           tezDagId: tezDagInstanceId,
-          atsURL: atsURL
+          atsURL: atsURL,
+          view: App.get("view"),
+          version: App.get("version"),
+          instanceName: App.get("instanceName")
         },
         success: 'loadTezDagSuccess',
         error: 'loadTezDagError'
@@ -241,7 +247,10 @@ App.Helpers.jobs = {
       sender: sender,
       data: {
         atsURL: atsURL,
-        tezDagVertexId: tezVertexInstanceId
+        tezDagVertexId: tezVertexInstanceId,
+        view: App.get("view"),
+        version: App.get("version"),
+        instanceName: App.get("instanceName")
       },
       success: 'loadTezDagVertexSuccess',
       error: 'loadTezDagVertexError'

+ 8 - 0
contrib/views/jobs/src/main/resources/ui/app/scripts/routes/application_route.js

@@ -36,6 +36,14 @@ App.JobsRoute = Ember.Route.extend({
 
   setupController: function(controller, model) {
     this._super(controller, model);
+    var hashArray = location.pathname.split('/');
+    var view = hashArray[2];
+    var version = hashArray[3];
+    var instanceName = hashArray[4];
+    App.set('view', view);
+    App.set('version', version);
+    App.set('instanceName', instanceName);
+
     controller.set('interval', 6000);
     controller.loop('loadJobs', true);
     // This observer should be set with addObserver

+ 1 - 1
contrib/views/slider/src/main/resources/ui/app/controllers/slider_app_controller.js

@@ -56,7 +56,7 @@ App.SliderAppController = Ember.ObjectController.extend(App.AjaxErrorHandler, {
         confirm: true
       });
     }
-    if ('FINISHED' !== status) {
+    if ('RUNNING' == status) {
       actions.push({
         title: 'Flex',
         action: 'flex',

+ 9 - 0
contrib/views/slider/src/main/resources/ui/app/mappers/slider_apps_mapper.js

@@ -22,6 +22,14 @@
  */
 App.SliderAppsMapper = App.Mapper.createWithMixins(App.RunPeriodically, {
 
+  /**
+   * List of app state display names
+   */
+  stateMap: {
+    'FROZEN': 'STOPPED',
+    'THAWED': 'RUNNING'
+  },
+
   /**
    * Load data from <code>App.urlPrefix + this.urlSuffix</code> one time
    * @method load
@@ -207,6 +215,7 @@ App.SliderAppsMapper = App.Mapper.createWithMixins(App.RunPeriodically, {
           yarnId: app.yarnId,
           name: app.name,
           status: app.state,
+          displayStatus: self.stateMap[app.state] || app.state,
           user: app.user,
           started: app.startTime ? (new Date(app.startTime).toUTCString()) : "-",
           ended: app.endTime ? (new Date(app.endTime).toUTCString()) : "-",

+ 5 - 0
contrib/views/slider/src/main/resources/ui/app/models/slider_app.js

@@ -33,6 +33,11 @@ App.SliderApp = DS.Model.extend({
    */
   status: DS.attr('string'),
 
+  /**
+   * @type {displayStatus}
+   */
+  displayStatus: DS.attr('string'),
+
   /**
    * @type {string}
    */

+ 3 - 3
contrib/views/slider/src/main/resources/ui/app/styles/application.less

@@ -595,6 +595,9 @@ select {
         border-width: 0;
       }
     }
+    td:nth-child(2) span{
+      word-break: break-all;
+    }
   }
   .panel-heading {
     font-weight: 700;
@@ -604,9 +607,6 @@ select {
     td:first-child {
       text-align: right;
     }
-    span{
-      word-break: break-all;
-    }
   }
   .panel-components {
     .status {

+ 1 - 1
contrib/views/slider/src/main/resources/ui/app/templates/slider_app/summary.hbs

@@ -24,7 +24,7 @@
         <tbody>
         <tr>
           <td>{{t common.status}}</td>
-          <td>{{controller.model.status}}</td>
+          <td>{{controller.model.displayStatus}}</td>
         </tr>
         <tr>
           <td>{{t common.type}}</td>

+ 1 - 1
contrib/views/slider/src/main/resources/ui/app/templates/slider_apps.hbs

@@ -48,7 +48,7 @@
             {{/link-to}}
           </td>
 
-          <td>{{slider.status}}</td>
+          <td>{{slider.displayStatus}}</td>
 
           <td>{{slider.appType.displayName}}</td>
 

Some files were not shown because too many files changed in this diff