You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2014/08/13 22:01:11 UTC

[1/6] git commit: AMBARI-6847. After second canceling of background operation was opened error (Max Shepel via alexantonenko)

Repository: ambari
Updated Branches:
  refs/heads/branch-alerts-dev 56ff1abdb -> 84b988bcb


AMBARI-6847. After second canceling of background operation was opened error (Max Shepel via alexantonenko)


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

Branch: refs/heads/branch-alerts-dev
Commit: 1eaf6a9e92d5620cf7c7952a100e9dc243b6b576
Parents: 56ff1ab
Author: Alex Antonenko <hi...@gmail.com>
Authored: Wed Aug 13 18:26:29 2014 +0300
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Wed Aug 13 18:27:00 2014 +0300

----------------------------------------------------------------------
 .../templates/common/host_progress_popup.hbs    |  2 +-
 ambari-web/app/utils/host_progress_popup.js     | 42 ++++++++++++++++----
 .../test/utils/host_progress_popup_test.js      | 26 ++++++++----
 3 files changed, 55 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/1eaf6a9e/ambari-web/app/templates/common/host_progress_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/host_progress_popup.hbs b/ambari-web/app/templates/common/host_progress_popup.hbs
index 939177a..b5df61d 100644
--- a/ambari-web/app/templates/common/host_progress_popup.hbs
+++ b/ambari-web/app/templates/common/host_progress_popup.hbs
@@ -46,7 +46,7 @@
                 <div class="operation-name-icon-wrap">
                   <i {{bindAttr class=":service-status servicesInfo.status servicesInfo.icon"}}></i>
                   {{#if App.supports.abortRequests}}
-                    <i {{action abortRequest servicesInfo}} {{translateAttr title="hostPopup.bgop.abortRequest.title"}} class="abort-icon icon-remove-circle hidden"></i>
+                    <i {{action abortRequest servicesInfo}} {{translateAttr title="hostPopup.bgop.abortRequest.title"}} {{bindAttr class="servicesInfo.abortClassName servicesInfo.abortable:abortable :abort-icon :icon-remove-circle :hidden"}}></i>
                   {{/if}}
                   <a href="#">
                     {{servicesInfo.name}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/1eaf6a9e/ambari-web/app/utils/host_progress_popup.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/host_progress_popup.js b/ambari-web/app/utils/host_progress_popup.js
index 0da534d..25b0899 100644
--- a/ambari-web/app/utils/host_progress_popup.js
+++ b/ambari-web/app/utils/host_progress_popup.js
@@ -68,6 +68,12 @@ App.HostPopup = Em.Object.create({
   isPopup: null,
 
   /**
+   * List of aborted requests
+   * @type {Array}
+   */
+  abortedRequests: [],
+
+  /**
    * Entering point of this component
    * @param {String} serviceName
    * @param {Object} controller
@@ -264,8 +270,9 @@ App.HostPopup = Em.Object.create({
       this.set("servicesInfo", null);
       this.get("inputData").forEach(function (service) {
         var status = statuses[service.status] || pendingStatus;
+        var id = service.id;
         var newService = Ember.Object.create({
-          id: service.id,
+          id: id,
           displayName: service.displayName,
           progress: service.progress,
           status: App.format.taskStatus(status[0]),
@@ -281,8 +288,19 @@ App.HostPopup = Em.Object.create({
           sourceRequestScheduleId: service.get('sourceRequestScheduleId'),
           contextCommand: service.get('contextCommand')
         });
+        if (App.get('supports.abortRequests')) {
+          var abortable = !Em.keys(statuses).contains(service.status) || service.status == 'IN_PROGRESS';
+          if (!abortable) {
+            var abortedRequests = this.get('abortedRequests');
+            this.set('abortedRequests', abortedRequests.without(id));
+          }
+          newService.setProperties({
+            abortable: abortable,
+            abortClassName: 'abort' + id
+          });
+        }
         allNewServices.push(newService);
-      });
+      }, this);
       self.set('servicesInfo', allNewServices);
       this.setBackgroundOperationHeader(isServiceListHidden);
     }
@@ -482,10 +500,16 @@ App.HostPopup = Em.Object.create({
         if (App.get('supports.abortRequests')) {
           $(document).on({
             mouseenter: function () {
-              if ($(this).find('.service-status').hasClass(App.format.taskStatus('IN_PROGRESS')) || $(this).find('.service-status').hasClass(App.format.taskStatus('PENDING'))) {
-                App.tooltip($('.abort-icon'));
-                $(this).find('.service-status').addClass('hidden');
-                $(this).find('.abort-icon').removeClass('hidden');
+              var statusIcon = $(this).find('.service-status');
+              var abortIcon = $(this).find('.abort-icon.abortable');
+              if (abortIcon.length > 0) {
+                var id = parseInt(abortIcon.attr('class').split(' ')[0].match(/\d+/)[0]);
+                var abortedRequests = self.get('abortedRequests');
+                if (!(abortedRequests && abortedRequests.contains(id))) {
+                  App.tooltip($('.abort-icon'));
+                  statusIcon.addClass('hidden');
+                  abortIcon.removeClass('hidden');
+                }
               }
             },
             mouseleave: function () {
@@ -1014,6 +1038,8 @@ App.HostPopup = Em.Object.create({
           var requestName = event.context.get('name');
           var self = this;
           App.showConfirmationPopup(function () {
+            var requestId = event.context.get('id');
+            self.get('controller.abortedRequests').push(requestId);
             App.ajax.send({
               name: 'background_operations.abort_request',
               sender: self,
@@ -1038,7 +1064,9 @@ App.HostPopup = Em.Object.create({
         /**
          * Method called on unsuccessful sending request to abort operation
          */
-        abortRequestErrorCallback: function (xhr, textStatus, error, opt) {
+        abortRequestErrorCallback: function (xhr, textStatus, error, opt, data) {
+          var abortedRequests = this.get('controller.abortedRequests');
+          this.set('controller.abortedRequests', abortedRequests.without(data.requestId));
           App.ajax.defaultErrorHandler(xhr, opt.url, 'PUT', xhr.status);
         },
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/1eaf6a9e/ambari-web/test/utils/host_progress_popup_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/host_progress_popup_test.js b/ambari-web/test/utils/host_progress_popup_test.js
index 3a9caa2..b8bc177 100644
--- a/ambari-web/test/utils/host_progress_popup_test.js
+++ b/ambari-web/test/utils/host_progress_popup_test.js
@@ -373,28 +373,40 @@ describe('App.HostPopup', function () {
   });
 
   describe('#abortRequestErrorCallback', function () {
+    var popup = App.HostPopup.createPopup();
     beforeEach(function () {
       sinon.stub(App.ajax, 'get', function(k) {
         if (k === 'modalPopup') return null;
         return Em.get(App, k);
       });
-      App.HostPopup.createPopup();
       sinon.spy(App.ModalPopup, 'show');
+      sinon.stub(popup, 'get', function(k) {
+        if (k === 'abortedRequests') return [0];
+        return Em.get(popup, k);
+      });
+      popup.get('bodyClass').create().abortRequestErrorCallback({
+        responseText: {
+          message: 'message'
+        },
+        status: 404
+      }, 'status', 'error', {
+        url: 'url'
+      }, {
+        requestId: 0
+      });
     });
     afterEach(function () {
       App.HostPopup.get('isPopup').hide();
       App.ModalPopup.show.restore();
+      popup.get.restore();
       App.ajax.get.restore();
     });
     it('should open popup', function () {
-      App.HostPopup.get('isPopup.bodyClass').create().abortRequestErrorCallback({
-        responseText: {
-          message: 'message'
-        },
-        status: 404
-      }, 'url', 'PUT', 404);
       expect(App.ModalPopup.show.calledOnce).to.be.true;
     });
+    it('should remove current request id from abortedRequests', function () {
+      expect(App.HostPopup.get('abortedRequests')).to.be.empty;
+    });
   });
 
 });


[5/6] git commit: AMBARI-6777. Views: view.xml instance changes are not picked up on redeploy.

Posted by jo...@apache.org.
AMBARI-6777. Views: view.xml instance changes are not picked up on redeploy.


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

Branch: refs/heads/branch-alerts-dev
Commit: 1b72f6df78a8e683256ed0654146deb59e341490
Parents: f3bd5cc
Author: Siddharth Wagle <sw...@hortonworks.com>
Authored: Wed Aug 13 12:45:35 2014 -0700
Committer: Siddharth Wagle <sw...@hortonworks.com>
Committed: Wed Aug 13 12:45:35 2014 -0700

----------------------------------------------------------------------
 .../ambari/server/controller/AmbariServer.java  |   4 +-
 .../internal/PrivilegeResourceProvider.java     |  14 +-
 .../internal/ViewPrivilegeResourceProvider.java |   5 +-
 .../server/orm/entities/ViewInstanceEntity.java |  25 +++
 .../server/upgrade/UpgradeCatalog170.java       |   2 +
 .../apache/ambari/server/view/ViewRegistry.java | 224 ++++++++++++++-----
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   2 +-
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   2 +-
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   2 +-
 .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql     |   2 +-
 .../ambari/server/view/ViewRegistryTest.java    |  69 ++++--
 11 files changed, 265 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index e6eab3f..98556f9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -68,6 +68,7 @@ import org.apache.ambari.server.orm.dao.PermissionDAO;
 import org.apache.ambari.server.orm.dao.PrincipalDAO;
 import org.apache.ambari.server.orm.dao.PrivilegeDAO;
 import org.apache.ambari.server.orm.dao.ResourceDAO;
+import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
 import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.dao.ViewDAO;
 import org.apache.ambari.server.orm.dao.ViewInstanceDAO;
@@ -539,7 +540,8 @@ public class AmbariServer {
     ClusterPrivilegeResourceProvider.init(injector.getInstance(ClusterDAO.class));
     ViewRegistry.init(injector.getInstance(ViewDAO.class), injector.getInstance(ViewInstanceDAO.class),
         injector.getInstance(UserDAO.class), injector.getInstance(MemberDAO.class),
-        injector.getInstance(PrivilegeDAO.class), injector.getInstance(SecurityHelper.class));
+        injector.getInstance(PrivilegeDAO.class), injector.getInstance(SecurityHelper.class),
+        injector.getInstance(ResourceDAO.class), injector.getInstance(ResourceTypeDAO.class));
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/PrivilegeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/PrivilegeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/PrivilegeResourceProvider.java
index 60d6396..d8fce4d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/PrivilegeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/PrivilegeResourceProvider.java
@@ -142,8 +142,9 @@ public abstract class PrivilegeResourceProvider<T> extends AbstractResourceProvi
    * @param properties the set of properties
    *
    * @return the entities
+   * @throws AmbariException if resource entities were not found
    */
-  public abstract Map<Long, T> getResourceEntities(Map<String, Object> properties);
+  public abstract Map<Long, T> getResourceEntities(Map<String, Object> properties) throws AmbariException;
 
   /**
    * Get the id for the resource specified by predicate.
@@ -183,7 +184,12 @@ public abstract class PrivilegeResourceProvider<T> extends AbstractResourceProvi
     }
 
     for (Map<String, Object> properties : propertyMaps) {
-      Map<Long, T> resourceEntities = getResourceEntities(properties);
+      Map<Long, T> resourceEntities;
+      try {
+        resourceEntities = getResourceEntities(properties);
+      } catch (AmbariException e) {
+        throw new SystemException("Could not get resource list from request", e);
+      }
 
       resourceIds.addAll(resourceEntities.keySet());
 
@@ -319,9 +325,7 @@ public abstract class PrivilegeResourceProvider<T> extends AbstractResourceProvi
     PrivilegeEntity entity         = new PrivilegeEntity();
     String          permissionName = (String) properties.get(PERMISSION_NAME_PROPERTY_ID);
     ResourceEntity  resourceEntity = resourceDAO.findById(resourceId);
-
     PermissionEntity permission = getPermission(permissionName, resourceEntity);
-
     if (permission == null) {
       throw new AmbariException("Can't find a permission named " + permissionName +
           " for the resource.");
@@ -331,7 +335,6 @@ public abstract class PrivilegeResourceProvider<T> extends AbstractResourceProvi
 
     String principalName = (String) properties.get(PRINCIPAL_NAME_PROPERTY_ID);
     String principalType = (String) properties.get(PRINCIPAL_TYPE_PROPERTY_ID);
-
     if (PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE_NAME.equalsIgnoreCase(principalType)) {
       GroupEntity groupEntity = groupDAO.findGroupByName(principalName);
       if (groupEntity != null) {
@@ -381,7 +384,6 @@ public abstract class PrivilegeResourceProvider<T> extends AbstractResourceProvi
           throw new AmbariException("Can't grant " + entity.getPermission().getResourceType().getName() +
               " permission on a " + entity.getResource().getResourceType().getName() + " resource.");
         }
-
         privilegeDAO.create(entity);
         return null;
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewPrivilegeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewPrivilegeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewPrivilegeResourceProvider.java
index 57eb28e..ea4acef 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewPrivilegeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewPrivilegeResourceProvider.java
@@ -101,7 +101,7 @@ public class ViewPrivilegeResourceProvider extends PrivilegeResourceProvider<Vie
   // ----- PrivilegeResourceProvider -----------------------------------------
 
   @Override
-  public Map<Long, ViewInstanceEntity> getResourceEntities(Map<String, Object> properties) {
+  public Map<Long, ViewInstanceEntity> getResourceEntities(Map<String, Object> properties) throws AmbariException {
     ViewRegistry viewRegistry = ViewRegistry.getInstance();
 
     String viewName     = (String) properties.get(PRIVILEGE_VIEW_NAME_PROPERTY_ID);
@@ -112,6 +112,9 @@ public class ViewPrivilegeResourceProvider extends PrivilegeResourceProvider<Vie
       ViewInstanceEntity viewInstanceEntity =
           viewRegistry.getInstanceDefinition(viewName, viewVersion, instanceName);
 
+      if (viewInstanceEntity == null) {
+        throw new AmbariException("View instance " + instanceName + " of " + viewName + viewVersion + " was not found");
+      }
       return Collections.singletonMap(viewInstanceEntity.getResource().getId(), viewInstanceEntity);
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
index 2878dd6..3f1cd8f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ViewInstanceEntity.java
@@ -123,6 +123,13 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   private String icon64;
 
   /**
+   * The XML driven instance flag.
+   */
+  @Column(name="xml_driven")
+  @Basic
+  private char xmlDriven = 'N';
+
+  /**
    * The instance properties.
    */
   @OneToMany(cascade = CascadeType.ALL, mappedBy = "viewInstance")
@@ -397,6 +404,24 @@ public class ViewInstanceEntity implements ViewInstanceDefinition {
   }
 
   /**
+   * Get the xml driven flag.
+   *
+   * @return the xml driven flag
+   */
+  public boolean isXmlDriven() {
+    return xmlDriven == 'y' || xmlDriven == 'Y';
+  }
+
+  /**
+   * Set the xml driven flag.
+   *
+   * @param xmlDriven the xml driven flag
+   */
+  public void setXmlDriven(boolean xmlDriven) {
+    this.xmlDriven = (xmlDriven) ? 'Y' : 'N';
+  }
+
+  /**
    * Get the instance properties.
    *
    * @return the instance properties

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
index 9a0f4ca..bb248ba 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
@@ -191,6 +191,8 @@ public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
         Integer.class, 1, 1, false));
     dbAccessor.addColumn("viewinstance", new DBColumnInfo("resource_id",
         Long.class, 1, 1, false));
+    dbAccessor.addColumn("viewinstance", new DBColumnInfo("xml_driven",
+        Character.class, 1, null, true));
     dbAccessor.addColumn("clusters", new DBColumnInfo("resource_id",
         Long.class, 1, 1, false));
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
index 808de92..0acbb62 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/view/ViewRegistry.java
@@ -18,9 +18,31 @@
 
 package org.apache.ambari.server.view;
 
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
+import java.beans.IntrospectionException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
 
 import org.apache.ambari.server.api.resources.ResourceInstanceFactoryImpl;
 import org.apache.ambari.server.api.resources.SubResourceDefinition;
@@ -31,6 +53,8 @@ import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.orm.dao.MemberDAO;
 import org.apache.ambari.server.orm.dao.PrivilegeDAO;
+import org.apache.ambari.server.orm.dao.ResourceDAO;
+import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
 import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.dao.ViewDAO;
 import org.apache.ambari.server.orm.dao.ViewInstanceDAO;
@@ -72,30 +96,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.security.core.GrantedAuthority;
 
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-
-import java.beans.IntrospectionException;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
 
 /**
  * Registry for view and view instance definitions.
@@ -178,6 +181,15 @@ public class ViewRegistry {
    */
   private static SecurityHelper securityHelper;
 
+  /**
+   * Resource data access object.
+   */
+  private static ResourceDAO resourceDAO;
+
+  /**
+   * Resource type data access object.
+   */
+  private static ResourceTypeDAO resourceTypeDAO;
 
   // ----- Constructors ------------------------------------------------------
 
@@ -388,13 +400,17 @@ public class ViewRegistry {
                 // extract the archive and get the class loader
                 ClassLoader cl = extractViewArchive(archiveFile, helper.getFile(archivePath));
 
+                viewConfig = helper.getViewConfigFromExtractedArchive(archivePath);
+
                 ViewEntity viewDefinition = createViewDefinition(viewConfig, configuration, cl, archivePath);
 
                 Set<ViewInstanceEntity> instanceDefinitions = new HashSet<ViewInstanceEntity>();
 
                 for (InstanceConfig instanceConfig : viewConfig.getInstances()) {
                   try {
-                    instanceDefinitions.add(createViewInstanceDefinition(viewConfig, viewDefinition, instanceConfig));
+                    ViewInstanceEntity instanceEntity = createViewInstanceDefinition(viewConfig, viewDefinition, instanceConfig);
+                    instanceEntity.setXmlDriven(true);
+                    instanceDefinitions.add(instanceEntity);
                   } catch (Exception e) {
                     LOG.error("Caught exception adding view instance for view " +
                         viewDefinition.getViewName(), e);
@@ -451,9 +467,11 @@ public class ViewRegistry {
         }
         instanceEntity.validate(viewEntity);
 
+        ResourceTypeEntity resourceTypeEntity = resourceTypeDAO.findByName(ViewEntity.getViewName(viewName, version));
         // create an admin resource to represent this view instance
         ResourceEntity resourceEntity = new ResourceEntity();
-        resourceEntity.setResourceType(viewEntity.getResourceType());
+        resourceEntity.setResourceType(resourceTypeEntity);
+        resourceDAO.create(resourceEntity);
 
         instanceEntity.setResource(resourceEntity);
 
@@ -467,6 +485,7 @@ public class ViewRegistry {
           throw new IllegalStateException(message);
         }
         instanceEntity.setViewInstanceId(persistedInstance.getViewInstanceId());
+        instanceEntity.setResource(persistedInstance.getResource());
 
         try {
           // bind the view instance to a view
@@ -507,6 +526,9 @@ public class ViewRegistry {
       ViewInstanceEntity entity = getInstanceDefinition(viewName, version, instanceName);
 
       if (entity != null) {
+        if (entity.isXmlDriven()) {
+          throw new IllegalStateException("View instances defined via xml can't be updated through api requests");
+        }
         if (LOG.isDebugEnabled()) {
           LOG.debug("Updating view instance " + viewName + "/" +
               version + "/" + instanceName);
@@ -542,8 +564,9 @@ public class ViewRegistry {
    * Uninstall a view instance for the view with the given view name.
    *
    * @param instanceEntity  the view instance entity
+   * @throws IllegalStateException if the given instance is not in a valid state
    */
-  public void uninstallViewInstance(ViewInstanceEntity instanceEntity) {
+  public void uninstallViewInstance(ViewInstanceEntity instanceEntity) throws IllegalStateException {
     ViewEntity viewEntity = getDefinition(instanceEntity.getViewName());
 
     if (viewEntity != null) {
@@ -552,7 +575,9 @@ public class ViewRegistry {
       String version      = viewEntity.getVersion();
 
       if (getInstanceDefinition(viewName, version, instanceName) != null) {
-
+        if (instanceEntity.isXmlDriven()) {
+          throw new IllegalStateException("View instances defined via xml can't be deleted through api requests");
+        }
         if (LOG.isDebugEnabled()) {
           LOG.debug("Deleting view instance " + viewName + "/" +
               version + "/" +instanceName);
@@ -882,10 +907,6 @@ public class ViewRegistry {
 
     setPersistenceEntities(viewInstanceDefinition);
 
-    ResourceEntity resourceEntity = new ResourceEntity();
-    resourceEntity.setResourceType(viewDefinition.getResourceType());
-    viewInstanceDefinition.setResource(resourceEntity);
-
     viewDefinition.addInstanceDefinition(viewInstanceDefinition);
   }
 
@@ -983,13 +1004,28 @@ public class ViewRegistry {
     }
   }
 
-  // sync the given view with the db
+  /**
+   * Sync given view with data in DB. Ensures that view data in DB is updated,
+   * all instances changes from xml config are reflected to DB
+   *
+   * @param view view config from xml
+   * @param instanceDefinitions view instances from xml
+   * @throws Exception
+   */
   private void syncView(ViewEntity view,
                         Set<ViewInstanceEntity> instanceDefinitions)
       throws Exception {
-
     String viewName = view.getName();
 
+    // get or create an admin resource type to represent this view
+    ResourceTypeEntity resourceTypeEntity = resourceTypeDAO.findByName(viewName);
+    if (resourceTypeEntity == null) {
+      resourceTypeEntity = new ResourceTypeEntity();
+      resourceTypeEntity.setName(view.getName());
+      resourceTypeDAO.create(resourceTypeEntity);
+    }
+    view.setResourceType(resourceTypeEntity);
+
     ViewEntity persistedView = viewDAO.findByName(viewName);
 
     // if the view is not yet persisted ...
@@ -997,13 +1033,31 @@ public class ViewRegistry {
       if (LOG.isDebugEnabled()) {
         LOG.debug("Creating View " + viewName + ".");
       }
+
+      for( ViewInstanceEntity instance : view.getInstances()) {
+
+        // create an admin resource to represent this view instance
+        ResourceEntity resourceEntity = new ResourceEntity();
+        resourceEntity.setResourceType(resourceTypeEntity);
+        resourceDAO.create(resourceEntity);
+
+        instance.setResource(resourceEntity);
+      }
       // ... merge it
-      persistedView = viewDAO.merge(view);
+      viewDAO.merge(view);
+
+      persistedView = viewDAO.findByName(viewName);
+      if (persistedView == null) {
+        String message = "View  " + viewName + " can not be found.";
+
+        LOG.error(message);
+        throw new IllegalStateException(message);
+      }
     }
 
-    Map<String, ViewInstanceEntity> instanceEntityMap = new HashMap<String, ViewInstanceEntity>();
+    Map<String, ViewInstanceEntity> xmlInstanceEntityMap = new HashMap<String, ViewInstanceEntity>();
     for( ViewInstanceEntity instance : view.getInstances()) {
-      instanceEntityMap.put(instance.getName(), instance);
+      xmlInstanceEntityMap.put(instance.getName(), instance);
     }
 
     view.setResourceType(persistedView.getResourceType());
@@ -1016,7 +1070,12 @@ public class ViewRegistry {
       ViewInstanceEntity instance =
           view.getInstanceDefinition(instanceName);
 
-      instanceEntityMap.remove(instanceName);
+      if (persistedInstance.isXmlDriven() && !xmlInstanceEntityMap.containsKey(instanceName)) {
+        instanceDAO.remove(persistedInstance);
+        xmlInstanceEntityMap.remove(instanceName);
+        continue;
+      }
+      xmlInstanceEntityMap.remove(instanceName);
 
       // if the persisted instance is not in the registry ...
       if (instance == null) {
@@ -1027,23 +1086,39 @@ public class ViewRegistry {
       }
       instance.setViewInstanceId(persistedInstance.getViewInstanceId());
 
-      // apply the persisted overrides to the in-memory instance
-      instance.setLabel(persistedInstance.getLabel());
-      instance.setDescription(persistedInstance.getDescription());
-      instance.setVisible(persistedInstance.isVisible());
-      instance.setData(persistedInstance.getData());
-      instance.setProperties(persistedInstance.getProperties());
-      instance.setEntities(persistedInstance.getEntities());
+      if (instance.isXmlDriven()) {
+        // override db data with data from {@InstanceConfig}
+        persistedInstance.setLabel(instance.getLabel());
+        persistedInstance.setDescription(instance.getDescription());
+        persistedInstance.setVisible(instance.isVisible());
+        persistedInstance.setIcon(instance.getIcon());
+        persistedInstance.setIcon64(instance.getIcon64());
+        persistedInstance.setProperties(instance.getProperties());
+
+        instanceDAO.merge(persistedInstance);
+      } else {
+        // apply the persisted overrides to the in-memory instance
+        view.removeInstanceDefinition(instanceName);
+        view.addInstanceDefinition(persistedInstance);
+      }
 
       instance.setResource(persistedInstance.getResource());
     }
 
-    // these instances appear in the archive but have been deleted
-    // from the db... remove them from the registry
-    for (ViewInstanceEntity instance : instanceEntityMap.values()) {
-      view.removeInstanceDefinition(instance.getName());
-      instanceDefinitions.remove(instance);
+    // these instances appear in the archive but not present in the db... add
+    // them to db and registry
+    for (ViewInstanceEntity instance : xmlInstanceEntityMap.values()) {
+      // create an admin resource to represent this view instance
+      ResourceEntity resourceEntity = new ResourceEntity();
+      resourceEntity.setResourceType(resourceTypeEntity);
+      resourceDAO.create(resourceEntity);
+      instance.setResource(resourceEntity);
+
+      instanceDAO.merge(instance);
+      bindViewInstance(view, instance);
+      instanceDefinitions.add(instance);
     }
+
   }
 
   // ensure that the extracted view archive directory exists
@@ -1196,13 +1271,16 @@ public class ViewRegistry {
    */
   public static void init(ViewDAO viewDAO, ViewInstanceDAO instanceDAO,
                           UserDAO userDAO, MemberDAO memberDAO, PrivilegeDAO privilegeDAO,
-                          SecurityHelper securityHelper) {
+                          SecurityHelper securityHelper, ResourceDAO resourceDAO,
+                          ResourceTypeDAO resourceTypeDAO) {
     setViewDAO(viewDAO);
     setInstanceDAO(instanceDAO);
     setUserDAO(userDAO);
     setMemberDAO(memberDAO);
     setPrivilegeDAO(privilegeDAO);
     setSecurityHelper(securityHelper);
+    setResourceDAO(resourceDAO);
+    setResourceTypeDAO(resourceTypeDAO);
   }
 
   /**
@@ -1259,6 +1337,24 @@ public class ViewRegistry {
     ViewRegistry.securityHelper = securityHelper;
   }
 
+  /**
+   * Set the resource DAO.
+   *
+   * @param resourceDAO the resource DAO
+   */
+  protected static void setResourceDAO(ResourceDAO resourceDAO) {
+    ViewRegistry.resourceDAO = resourceDAO;
+  }
+
+  /**
+   * Set the resource type DAO.
+   *
+   * @param resourceTypeDAO the resource type DAO.
+   */
+  protected static void setResourceTypeDAO(ResourceTypeDAO resourceTypeDAO) {
+    ViewRegistry.resourceTypeDAO = resourceTypeDAO;
+  }
+
 
   // ----- inner class : ViewRegistryHelper ----------------------------------
 
@@ -1286,6 +1382,26 @@ public class ViewRegistry {
     }
 
     /**
+     * Get the view configuration from the extracted archive file.
+     *
+     * @param archivePath path to extracted archive
+     *
+     * @return the associated view configuration
+     *
+     * @throws JAXBException if xml is malformed
+     * @throws FileNotFoundException if xml was not found
+     */
+    public ViewConfig getViewConfigFromExtractedArchive(String archivePath)
+        throws JAXBException, FileNotFoundException {
+
+      InputStream configStream      = new FileInputStream(new File(archivePath + File.separator + VIEW_XML));
+      JAXBContext  jaxbContext      = JAXBContext.newInstance(ViewConfig.class);
+      Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
+
+      return (ViewConfig) jaxbUnmarshaller.unmarshal(configStream);
+    }
+
+    /**
      * Get a new file instance for the given path.
      *
      * @param path  the path

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index b4c7fb6..ff6c8c0 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -69,7 +69,7 @@ CREATE TABLE blueprint_configuration (blueprint_name VARCHAR(255) NOT NULL, type
 CREATE TABLE hostgroup_configuration (blueprint_name VARCHAR(255) NOT NULL, hostgroup_name VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, config_data TEXT NOT NULL, config_attributes TEXT, PRIMARY KEY(blueprint_name, hostgroup_name, type_name));
 CREATE TABLE viewmain (view_name VARCHAR(255) NOT NULL, label VARCHAR(255), version VARCHAR(255), resource_type_id INTEGER NOT NULL, icon VARCHAR(255), icon64 VARCHAR(255), archive VARCHAR(255), mask VARCHAR(255), PRIMARY KEY(view_name));
 CREATE TABLE viewinstancedata (view_instance_id BIGINT, view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, user_name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(VIEW_INSTANCE_ID, NAME, USER_NAME));
-CREATE TABLE viewinstance (view_instance_id BIGINT, resource_id BIGINT NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), PRIMARY KEY(view_instance_id));
+CREATE TABLE viewinstance (view_instance_id BIGINT, resource_id BIGINT NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), xml_driven CHAR(1), PRIMARY KEY(view_instance_id));
 CREATE TABLE viewinstanceproperty (view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_name, view_instance_name, name));
 CREATE TABLE viewparameter (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, description VARCHAR(255), required CHAR(1), masked CHAR(1), PRIMARY KEY(view_name, name));
 CREATE TABLE viewresource (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, plural_name VARCHAR(255), id_property VARCHAR(255), subResource_names VARCHAR(255), provider VARCHAR(255), service VARCHAR(255), resource VARCHAR(255), PRIMARY KEY(view_name, name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 87d0fd9..bdb20b8 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -60,7 +60,7 @@ CREATE TABLE blueprint_configuration (blueprint_name VARCHAR2(255) NOT NULL, typ
 CREATE TABLE hostgroup_configuration (blueprint_name VARCHAR2(255) NOT NULL, hostgroup_name VARCHAR2(255) NOT NULL, type_name VARCHAR2(255) NOT NULL, config_data CLOB NOT NULL, config_attributes CLOB, PRIMARY KEY(blueprint_name, hostgroup_name, type_name));
 CREATE TABLE viewmain (view_name VARCHAR(255) NOT NULL, label VARCHAR(255), version VARCHAR(255), resource_type_id NUMBER(10) NOT NULL, icon VARCHAR(255), icon64 VARCHAR(255), archive VARCHAR(255), mask VARCHAR(255), PRIMARY KEY(view_name));
 CREATE TABLE viewinstancedata (view_instance_id NUMBER(19), view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, user_name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_instance_id, name, user_name));
-CREATE TABLE viewinstance (view_instance_id NUMBER(19), resource_id NUMBER(19) NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), PRIMARY KEY(view_instance_id));
+CREATE TABLE viewinstance (view_instance_id NUMBER(19), resource_id NUMBER(19) NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), xml_driven CHAR(1), PRIMARY KEY(view_instance_id));
 CREATE TABLE viewinstanceproperty (view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_name, view_instance_name, name));
 CREATE TABLE viewparameter (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, description VARCHAR(255), required CHAR(1), masked CHAR(1), PRIMARY KEY(view_name, name));
 CREATE TABLE viewresource (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, plural_name VARCHAR(255), id_property VARCHAR(255), subResource_names VARCHAR(255), provider VARCHAR(255), service VARCHAR(255), "resource" VARCHAR(255), PRIMARY KEY(view_name, name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index bec8fd9..1f3b3e5 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -97,7 +97,7 @@ CREATE TABLE hostgroup_configuration (blueprint_name VARCHAR(255) NOT NULL, host
 
 CREATE TABLE viewmain (view_name VARCHAR(255) NOT NULL, label VARCHAR(255), version VARCHAR(255), resource_type_id INTEGER NOT NULL, icon VARCHAR(255), icon64 VARCHAR(255), archive VARCHAR(255), mask VARCHAR(255), PRIMARY KEY(view_name));
 CREATE TABLE viewinstancedata (view_instance_id BIGINT, view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, user_name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_instance_id, name, user_name));
-CREATE TABLE viewinstance (view_instance_id BIGINT, resource_id BIGINT NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), PRIMARY KEY(view_instance_id));
+CREATE TABLE viewinstance (view_instance_id BIGINT, resource_id BIGINT NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), xml_driven CHAR(1), PRIMARY KEY(view_instance_id));
 CREATE TABLE viewinstanceproperty (view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_name, view_instance_name, name));
 CREATE TABLE viewparameter (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, description VARCHAR(255), required CHAR(1), masked CHAR(1), PRIMARY KEY(view_name, name));
 CREATE TABLE viewresource (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, plural_name VARCHAR(255), id_property VARCHAR(255), subResource_names VARCHAR(255), provider VARCHAR(255), service VARCHAR(255), resource VARCHAR(255), PRIMARY KEY(view_name, name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
index 0dd33af..58ad54a 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
@@ -149,7 +149,7 @@ GRANT ALL PRIVILEGES ON TABLE ambari.hostgroup_configuration TO :username;
 
 CREATE TABLE ambari.viewmain (view_name VARCHAR(255) NOT NULL, label VARCHAR(255), version VARCHAR(255), resource_type_id INTEGER NOT NULL, icon VARCHAR(255), icon64 VARCHAR(255), archive VARCHAR(255), mask VARCHAR(255), PRIMARY KEY(view_name));
 CREATE TABLE ambari.viewinstancedata (view_instance_id BIGINT, view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, user_name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_instance_id, name, user_name));
-CREATE TABLE ambari.viewinstance (view_instance_id BIGINT, resource_id BIGINT NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), PRIMARY KEY(view_instance_id));
+CREATE TABLE ambari.viewinstance (view_instance_id BIGINT, resource_id BIGINT NOT NULL, view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, label VARCHAR(255), description VARCHAR(255), visible CHAR(1), icon VARCHAR(255), icon64 VARCHAR(255), xml_driven CHAR(1), PRIMARY KEY(view_instance_id));
 CREATE TABLE ambari.viewinstanceproperty (view_name VARCHAR(255) NOT NULL, view_instance_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, value VARCHAR(2000) NOT NULL, PRIMARY KEY(view_name, view_instance_name, name));
 CREATE TABLE ambari.viewparameter (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, description VARCHAR(255), required CHAR(1), masked CHAR(1), PRIMARY KEY(view_name, name));
 CREATE TABLE ambari.viewresource (view_name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, plural_name VARCHAR(255), id_property VARCHAR(255), subResource_names VARCHAR(255), provider VARCHAR(255), service VARCHAR(255), resource VARCHAR(255), PRIMARY KEY(view_name, name));

http://git-wip-us.apache.org/repos/asf/ambari/blob/1b72f6df/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
index 5396eec..5a95ee8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/view/ViewRegistryTest.java
@@ -24,6 +24,8 @@ 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;
+import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
 import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.dao.ViewDAO;
 import org.apache.ambari.server.orm.dao.ViewInstanceDAO;
@@ -46,13 +48,14 @@ import org.apache.ambari.server.view.events.EventImpl;
 import org.apache.ambari.server.view.events.EventImplTest;
 import org.apache.ambari.view.events.Event;
 import org.apache.ambari.view.events.Listener;
-import org.easymock.Capture;
+import org.easymock.EasyMock;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
 import javax.xml.bind.JAXBException;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -70,7 +73,6 @@ import java.util.Set;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
-import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.createNiceMock;
 import static org.easymock.EasyMock.expect;
@@ -165,8 +167,14 @@ public class ViewRegistryTest {
     resourceTypeEntity.setName("MY_VIEW{1.0.0}");
 
     ViewDAO vDAO = createMock(ViewDAO.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
+    ViewInstanceDAO viDAO = createNiceMock(ViewInstanceDAO.class);
 
     ViewRegistry.setViewDAO(vDAO);
+    ViewRegistry.setResourceDAO(rDAO);
+    ViewRegistry.setResourceTypeDAO(rtDAO);
+    ViewRegistry.setInstanceDAO(viDAO);
 
     ViewEntity viewDefinition = ViewEntityTest.getViewEntity();
     viewDefinition.setResourceType(resourceTypeEntity);
@@ -206,6 +214,7 @@ public class ViewRegistryTest {
     expect(viewDir.listFiles()).andReturn(new File[]{viewArchive});
 
     expect(viewArchive.isDirectory()).andReturn(false);
+    expect(viewArchive.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}").anyTimes();
 
     expect(archiveDir.exists()).andReturn(false);
     expect(archiveDir.getAbsolutePath()).andReturn(
@@ -243,16 +252,19 @@ public class ViewRegistryTest {
     expect(libDir.listFiles()).andReturn(new File[]{fileEntry});
     expect(fileEntry.toURI()).andReturn(new URI("file:./"));
 
-    Capture<ViewEntity> captureViewEntity = new Capture<ViewEntity>();
-
-    expect(vDAO.findByName("MY_VIEW{1.0.0}")).andReturn(null);
-    expect(vDAO.merge(capture(captureViewEntity))).andReturn(viewDefinition);
+    expect(vDAO.findByName("MY_VIEW{1.0.0}")).andReturn(viewDefinition);
 
     expect(vDAO.findAll()).andReturn(Collections.<ViewEntity>emptyList());
 
+    expect(rtDAO.findByName("MY_VIEW{1.0.0}")).andReturn(null);
+    rtDAO.create(EasyMock.anyObject(ResourceTypeEntity.class));
+    EasyMock.expectLastCall().anyTimes();
+
+    expect(viDAO.merge(EasyMock.anyObject(ViewInstanceEntity.class))).andReturn(null).times(2);
+
     // replay mocks
     replay(configuration, viewDir, extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir,
-        libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, vDAO);
+        libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, rDAO, rtDAO, vDAO, viDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
     registry.setHelper(new TestViewRegistryHelper(viewConfigs, files, outputStreams, jarFiles));
@@ -260,11 +272,10 @@ public class ViewRegistryTest {
     Set<ViewInstanceEntity> instanceEntities = registry.readViewArchives(configuration);
 
     Assert.assertEquals(2, instanceEntities.size());
-    Assert.assertEquals("MY_VIEW", captureViewEntity.getValue().getCommonName());
 
     // verify mocks
     verify(configuration, viewDir, extractedArchiveDir, viewArchive, archiveDir, entryFile, classesDir,
-        libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, vDAO);
+        libDir, fileEntry, viewJarFile, enumeration, jarEntry, is, fos, rDAO, rtDAO, vDAO, viDAO);
   }
 
   @Test
@@ -331,6 +342,7 @@ public class ViewRegistryTest {
     expect(viewDir.listFiles()).andReturn(new File[]{viewArchive});
 
     expect(viewArchive.isDirectory()).andReturn(false);
+    expect(viewArchive.getAbsolutePath()).andReturn("/var/lib/ambari-server/resources/views/work/MY_VIEW{1.0.0}");
 
     expect(archiveDir.exists()).andReturn(false);
     expect(archiveDir.getAbsolutePath()).andReturn(
@@ -368,11 +380,6 @@ public class ViewRegistryTest {
     expect(libDir.listFiles()).andReturn(new File[]{fileEntry});
     expect(fileEntry.toURI()).andReturn(new URI("file:./"));
 
-    Capture<ViewEntity> captureViewEntity = new Capture<ViewEntity>();
-
-    expect(vDAO.findByName("MY_VIEW{1.0.0}")).andReturn(null);
-    expect(vDAO.merge(capture(captureViewEntity))).andThrow(new IllegalArgumentException("Expected exception."));
-
     expect(vDAO.findAll()).andReturn(Collections.<ViewEntity>emptyList());
 
     // replay mocks
@@ -508,8 +515,10 @@ public class ViewRegistryTest {
     MemberDAO memberDAO = createNiceMock(MemberDAO.class);
     PrivilegeDAO privilegeDAO = createNiceMock(PrivilegeDAO.class);
     SecurityHelper securityHelper = createNiceMock(SecurityHelper.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
 
-    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper);
+    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper, rDAO, rtDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
 
@@ -548,8 +557,10 @@ public class ViewRegistryTest {
     MemberDAO memberDAO = createNiceMock(MemberDAO.class);
     PrivilegeDAO privilegeDAO = createNiceMock(PrivilegeDAO.class);
     SecurityHelper securityHelper = createNiceMock(SecurityHelper.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
 
-    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper);
+    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper, rDAO, rtDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
 
@@ -583,8 +594,10 @@ public class ViewRegistryTest {
     MemberDAO memberDAO = createNiceMock(MemberDAO.class);
     PrivilegeDAO privilegeDAO = createNiceMock(PrivilegeDAO.class);
     SecurityHelper securityHelper = createNiceMock(SecurityHelper.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
 
-    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper);
+    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper, rDAO, rtDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
 
@@ -619,8 +632,10 @@ public class ViewRegistryTest {
     MemberDAO memberDAO = createNiceMock(MemberDAO.class);
     PrivilegeDAO privilegeDAO = createNiceMock(PrivilegeDAO.class);
     SecurityHelper securityHelper = createNiceMock(SecurityHelper.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
 
-    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper);
+    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper, rDAO, rtDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
 
@@ -663,8 +678,10 @@ public class ViewRegistryTest {
     MemberDAO memberDAO = createNiceMock(MemberDAO.class);
     PrivilegeDAO privilegeDAO = createNiceMock(PrivilegeDAO.class);
     SecurityHelper securityHelper = createNiceMock(SecurityHelper.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
 
-    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper);
+    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper, rDAO, rtDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
 
@@ -705,8 +722,10 @@ public class ViewRegistryTest {
     MemberDAO memberDAO = createNiceMock(MemberDAO.class);
     PrivilegeDAO privilegeDAO = createNiceMock(PrivilegeDAO.class);
     SecurityHelper securityHelper = createNiceMock(SecurityHelper.class);
+    ResourceDAO rDAO = createNiceMock(ResourceDAO.class);
+    ResourceTypeDAO rtDAO = createNiceMock(ResourceTypeDAO.class);
 
-    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper);
+    ViewRegistry.init(viewDAO, viewInstanceDAO, userDAO, memberDAO, privilegeDAO, securityHelper, rDAO, rtDAO);
 
     ViewRegistry registry = ViewRegistry.getInstance();
 
@@ -760,6 +779,16 @@ public class ViewRegistryTest {
       return viewConfigs.get(archiveFile);
     }
 
+    public ViewConfig getViewConfigFromExtractedArchive(String archivePath)
+        throws JAXBException, FileNotFoundException {
+      for (File viewConfigKey: viewConfigs.keySet()) {
+        if (viewConfigKey.getAbsolutePath().equals(archivePath)) {
+          return viewConfigs.get(viewConfigKey);
+        }
+      }
+      return null;
+    }
+
     @Override
     public File getFile(String path) {
       return files.get(path);


[6/6] git commit: AMBARI-6853 - Alerts: Calculate Hash Based On Alert Definitions (jonathanhurley)

Posted by jo...@apache.org.
AMBARI-6853 - Alerts: Calculate Hash Based On Alert Definitions (jonathanhurley)


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

Branch: refs/heads/branch-alerts-dev
Commit: 84b988bcb4a6d127a1bb0a284e34f157ccf78fce
Parents: 1b72f6d
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Wed Aug 13 15:39:35 2014 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Wed Aug 13 16:00:08 2014 -0400

----------------------------------------------------------------------
 .../ambari/server/agent/HeartBeatHandler.java   |  93 ++++---
 .../ambari/server/agent/HeartBeatResponse.java  |  54 ++--
 .../AlertDefinitionResourceProvider.java        | 139 +++++-----
 .../server/orm/dao/AlertDefinitionDAO.java      | 129 ++++++++-
 .../orm/entities/AlertDefinitionEntity.java     |   9 +-
 .../server/state/alert/AlertDefinitionHash.java | 272 +++++++++++++++++++
 .../src/main/resources/properties.json          |   3 +-
 .../AlertDefinitionResourceProviderTest.java    | 134 ++++-----
 .../server/orm/dao/AlertDefinitionDAOTest.java  | 117 +++++++-
 .../state/alerts/AlertDefinitionHashTest.java   | 224 +++++++++++++++
 10 files changed, 969 insertions(+), 205 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
index fa633c1..8a818a6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
@@ -17,20 +17,16 @@
  */
 package org.apache.ambari.server.agent;
 
-import com.google.gson.Gson;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Singleton;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.HostNotFoundException;
 import org.apache.ambari.server.RoleCommand;
@@ -46,7 +42,6 @@ import org.apache.ambari.server.controller.MaintenanceStateHelper;
 import org.apache.ambari.server.metadata.ActionMetadata;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Alert;
-import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
@@ -63,6 +58,7 @@ import org.apache.ambari.server.state.ServiceInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.server.state.State;
+import org.apache.ambari.server.state.alert.AlertDefinitionHash;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.host.HostHealthyHeartbeatEvent;
 import org.apache.ambari.server.state.host.HostRegistrationRequestEvent;
@@ -78,6 +74,11 @@ import org.apache.ambari.server.utils.VersionUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import com.google.gson.Gson;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Singleton;
+
 
 /**
  * This class handles the heartbeats coming from the agent, passes on the information
@@ -90,29 +91,40 @@ public class HeartBeatHandler {
   private final Clusters clusterFsm;
   private final ActionQueue actionQueue;
   private final ActionManager actionManager;
+  private HeartbeatMonitor heartbeatMonitor;
+
   @Inject
-  Injector injector;
+  private Injector injector;
+
   @Inject
-  Configuration config;
+  private Configuration config;
+
   @Inject
-  AmbariMetaInfo ambariMetaInfo;
+  private AmbariMetaInfo ambariMetaInfo;
+
   @Inject
-  ActionMetadata actionMetadata;
-  private HeartbeatMonitor heartbeatMonitor;
+  private ActionMetadata actionMetadata;
+
   @Inject
   private Gson gson;
+
   @Inject
-  ConfigHelper configHelper;
+  private ConfigHelper configHelper;
+
+  @Inject
+  private AlertDefinitionHash alertDefinitionHash;
+
   private Map<String, Long> hostResponseIds = new ConcurrentHashMap<String, Long>();
+
   private Map<String, HeartBeatResponse> hostResponses = new ConcurrentHashMap<String, HeartBeatResponse>();
 
   @Inject
   public HeartBeatHandler(Clusters fsm, ActionQueue aq, ActionManager am,
                           Injector injector) {
-    this.clusterFsm = fsm;
-    this.actionQueue = aq;
-    this.actionManager = am;
-    this.heartbeatMonitor = new HeartbeatMonitor(fsm, aq, am, 60000, injector);
+    clusterFsm = fsm;
+    actionQueue = aq;
+    actionManager = am;
+    heartbeatMonitor = new HeartbeatMonitor(fsm, aq, am, 60000, injector);
     injector.injectMembers(this);
   }
 
@@ -130,14 +142,17 @@ public class HeartBeatHandler {
     if(heartbeat.getAgentEnv() != null && heartbeat.getAgentEnv().getHostHealth() != null) {
       heartbeat.getAgentEnv().getHostHealth().setServerTimeStampAtReporting(now);
     }
+
     String hostname = heartbeat.getHostname();
     Long currentResponseId = hostResponseIds.get(hostname);
     HeartBeatResponse response;
+
     if (currentResponseId == null) {
       //Server restarted, or unknown host.
       LOG.error("CurrentResponseId unknown for " + hostname + " - send register command");
       return createRegisterCommand();
     }
+
     LOG.debug("Received heartbeat from host"
         + ", hostname=" + hostname
         + ", currentResponseId=" + currentResponseId
@@ -195,18 +210,23 @@ public class HeartBeatHandler {
 
     // Examine heartbeart for component live status reports
     processStatusReports(heartbeat, hostname, clusterFsm);
-    
+
     // Calculate host status
     // NOTE: This step must be after processing command/status reports
     processHostStatus(heartbeat, hostname);
-    
+
     calculateHostAlerts(heartbeat, hostname);
 
     // Send commands if node is active
     if (hostObject.getState().equals(HostState.HEALTHY)) {
       sendCommands(hostname, response);
       annotateResponse(hostname, response);
-    }    
+    }
+
+    // send the alert definition hash for this host
+    Map<String, String> alertDefinitionHashes = alertDefinitionHash.getHashes(hostname);
+    response.setAlertDefinitionHash(alertDefinitionHashes);
+
     return response;
   }
 
@@ -218,7 +238,7 @@ public class HeartBeatHandler {
         }
       }
   }
-   
+
   protected void processHostStatus(HeartBeat heartbeat, String hostname) throws AmbariException {
 
     Host host = clusterFsm.getHost(hostname);
@@ -268,7 +288,7 @@ public class HeartBeatHandler {
         StackId stackId;
         Cluster cluster = clusterFsm.getCluster(clusterName);
         stackId = cluster.getDesiredStackVersion();
-        
+
         MaintenanceStateHelper psh = injector.getInstance(MaintenanceStateHelper.class);
 
         List<ServiceComponentHost> scHosts = cluster.getServiceComponentHosts(heartbeat.getHostname());
@@ -347,7 +367,7 @@ public class HeartBeatHandler {
          "STOP".equals(report.getCustomCommand())))) {
         continue;
       }
-      
+
       Cluster cl = clusterFsm.getCluster(report.getClusterName());
       String service = report.getServiceName();
       if (service == null || service.isEmpty()) {
@@ -439,7 +459,7 @@ public class HeartBeatHandler {
         if (status.getClusterName().equals(cl.getClusterName())) {
           try {
             Service svc = cl.getService(status.getServiceName());
-            
+
             String componentName = status.getComponentName();
             if (svc.getServiceComponents().containsKey(componentName)) {
               ServiceComponent svcComp = svc.getServiceComponent(
@@ -470,7 +490,7 @@ public class HeartBeatHandler {
               if (null != status.getConfigTags()) {
                 scHost.updateActualConfigs(status.getConfigTags());
               }
-              
+
               Map<String, Object> extra = status.getExtra();
               if (null != extra && !extra.isEmpty()) {
                 try {
@@ -479,7 +499,7 @@ public class HeartBeatHandler {
                     List<Map<String, String>> list = (List<Map<String, String>>) extra.get("processes");
                     scHost.setProcesses(list);
                   }
-                  
+
                 } catch (Exception e) {
                   LOG.error("Could not access extra JSON for " +
                       scHost.getServiceComponentName() + " from " +
@@ -487,7 +507,7 @@ public class HeartBeatHandler {
                       " (" + e.getMessage() + ")");
                 }
               }
-              
+
               if (null != status.getAlerts()) {
                 List<Alert> clusterAlerts = new ArrayList<Alert>();
                 for (AgentAlert aa : status.getAlerts()) {
@@ -496,14 +516,15 @@ public class HeartBeatHandler {
                       scHost.getHostName(), aa.getState());
                   alert.setLabel(aa.getLabel());
                   alert.setText(aa.getText());
-                  
+
                   clusterAlerts.add(alert);
                 }
-                
-               if (0 != clusterAlerts.size())
-                 cl.addAlerts(clusterAlerts);
+
+               if (0 != clusterAlerts.size()) {
+                cl.addAlerts(clusterAlerts);
               }
-              
+              }
+
 
             } else {
               // TODO: What should be done otherwise?
@@ -563,7 +584,7 @@ public class HeartBeatHandler {
           throw new AmbariException("Could not get jaxb string for command", e);
         }
         switch (ac.getCommandType()) {
-          case BACKGROUND_EXECUTION_COMMAND: 
+          case BACKGROUND_EXECUTION_COMMAND:
           case EXECUTION_COMMAND: {
             response.addExecutionCommand((ExecutionCommand) ac);
             break;
@@ -699,7 +720,7 @@ public class HeartBeatHandler {
    * @throws org.apache.ambari.server.AmbariException
    */
   private void annotateResponse(String hostname, HeartBeatResponse response) throws AmbariException {
-    for (Cluster cl : this.clusterFsm.getClustersForHost(hostname)) {
+    for (Cluster cl : clusterFsm.getClustersForHost(hostname)) {
       List<ServiceComponentHost> scHosts = cl.getServiceComponentHosts(hostname);
       if (scHosts != null && scHosts.size() > 0) {
         response.setHasMappedComponents(true);
@@ -718,19 +739,19 @@ public class HeartBeatHandler {
       throws AmbariException {
     ComponentsResponse response = new ComponentsResponse();
 
-    Cluster cluster = this.clusterFsm.getCluster(clusterName);
+    Cluster cluster = clusterFsm.getCluster(clusterName);
     StackId stackId = cluster.getCurrentStackVersion();
     if (stackId == null) {
       throw new AmbariException("Cannot provide stack components map. " +
         "Stack hasn't been selected yet.");
     }
-    StackInfo stack = this.ambariMetaInfo.getStackInfo(stackId.getStackName(),
+    StackInfo stack = ambariMetaInfo.getStackInfo(stackId.getStackName(),
         stackId.getStackVersion());
 
     response.setClusterName(clusterName);
     response.setStackName(stackId.getStackName());
     response.setStackVersion(stackId.getStackVersion());
-    response.setComponents(this.getComponentsMap(stack));
+    response.setComponents(getComponentsMap(stack));
 
     return response;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatResponse.java
index 1670beb..24bd8a2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatResponse.java
@@ -20,26 +20,34 @@ package org.apache.ambari.server.agent;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.codehaus.jackson.annotate.JsonProperty;
 
 /**
- *
  * Controller to Agent response data model.
- *
  */
 public class HeartBeatResponse {
 
   private long responseId;
- 
-  List<ExecutionCommand> executionCommands = new ArrayList<ExecutionCommand>();
-  List<StatusCommand> statusCommands = new ArrayList<StatusCommand>();
-  List<CancelCommand> cancelCommands = new ArrayList<CancelCommand>();
 
-  RegistrationCommand registrationCommand;
+  private List<ExecutionCommand> executionCommands = new ArrayList<ExecutionCommand>();
+  private List<StatusCommand> statusCommands = new ArrayList<StatusCommand>();
+  private List<CancelCommand> cancelCommands = new ArrayList<CancelCommand>();
+
+  private RegistrationCommand registrationCommand;
+
+  private boolean restartAgent = false;
+  private boolean hasMappedComponents = false;
 
-  boolean restartAgent = false;
-  boolean hasMappedComponents = false;
+  /**
+   * A mapping between cluster name and the alert defintion hash for that
+   * cluster. The alert definition hash for a cluster is a hashed value of all
+   * of the UUIDs for each alert definition that the agent host should be
+   * scheduling. If any of the alert definitions change, their UUID will change
+   * which will cause this hash value to change.
+   */
+  private Map<String, String> alertDefinitionHashes = null;
 
   @JsonProperty("responseId")
   public long getResponseId() {
@@ -111,6 +119,16 @@ public class HeartBeatResponse {
     this.hasMappedComponents = hasMappedComponents;
   }
 
+  @JsonProperty("alertDefinitionHashes")
+  public Map<String, String> getAlertDefinitionHash() {
+    return alertDefinitionHashes;
+  }
+
+  @JsonProperty("alertDefinitionHashes")
+  public void setAlertDefinitionHash(Map<String, String> alertDefinitionHashes) {
+    this.alertDefinitionHashes = alertDefinitionHashes;
+  }
+
   public void addExecutionCommand(ExecutionCommand execCmd) {
     executionCommands.add(execCmd);
   }
@@ -125,13 +143,15 @@ public class HeartBeatResponse {
 
   @Override
   public String toString() {
-    return "HeartBeatResponse{" +
-            "responseId=" + responseId +
-            ", executionCommands=" + executionCommands +
-            ", statusCommands=" + statusCommands +
-            ", cancelCommands=" + cancelCommands +
-            ", registrationCommand=" + registrationCommand +
-            ", restartAgent=" + restartAgent +
-            '}';
+    StringBuilder buffer = new StringBuilder("HeartBeatResponse{");
+    buffer.append("responseId=").append(responseId);
+    buffer.append(", executionCommands=").append(executionCommands);
+    buffer.append(", statusCommands=").append(statusCommands);
+    buffer.append(", cancelCommands=").append(cancelCommands);
+    buffer.append(", registrationCommand=").append(registrationCommand);
+    buffer.append(", restartAgent=").append(restartAgent);
+    buffer.append(", alertDefinitionHashes=").append(alertDefinitionHashes);
+    buffer.append('}');
+    return buffer.toString();
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java
index 6f00c27..fd8210b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProvider.java
@@ -64,13 +64,14 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
   protected static final String ALERT_DEF_COMPONENT_NAME = "AlertDefinition/component_name";
   protected static final String ALERT_DEF_ENABLED = "AlertDefinition/enabled";
   protected static final String ALERT_DEF_SCOPE = "AlertDefinition/scope";
-  
+  protected static final String ALERT_DEF_UUID = "AlertDefinition/uuid";
+
   private static Set<String> pkPropertyIds = new HashSet<String>(
       Arrays.asList(ALERT_DEF_ID, ALERT_DEF_NAME));
   private static AlertDefinitionDAO alertDefinitionDAO = null;
-  
+
   private static Gson gson = new Gson();
-  
+
   /**
    * @param instance
    */
@@ -78,13 +79,13 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
   public static void init(AlertDefinitionDAO instance) {
     alertDefinitionDAO = instance;
   }
-  
+
   AlertDefinitionResourceProvider(Set<String> propertyIds,
       Map<Resource.Type, String> keyPropertyIds,
       AmbariManagementController managementController) {
     super(propertyIds, keyPropertyIds, managementController);
   }
-  
+
   @Override
   protected Set<String> getPKPropertyIds() {
     return pkPropertyIds;
@@ -103,49 +104,55 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
       }
     });
     notifyCreate(Resource.Type.AlertDefinition, request);
-    
+
     return getRequestStatus(null);
   }
-  
+
   private void createAlertDefinitions(Set<Map<String, Object>> requestMaps)
     throws AmbariException {
     List<AlertDefinitionEntity> entities = new ArrayList<AlertDefinitionEntity>();
-    
+
     for (Map<String, Object> requestMap : requestMaps) {
       entities.add(toCreateEntity(requestMap));
     }
 
     // !!! TODO multi-create in a transaction
-    for (AlertDefinitionEntity entity : entities)
+    for (AlertDefinitionEntity entity : entities) {
       alertDefinitionDAO.create(entity);
+    }
   }
-  
+
   private AlertDefinitionEntity toCreateEntity(Map<String, Object> requestMap)
     throws AmbariException {
 
     String clusterName = (String) requestMap.get(ALERT_DEF_CLUSTER_NAME);
-    
-    if (null == clusterName || clusterName.isEmpty())
+
+    if (null == clusterName || clusterName.isEmpty()) {
       throw new IllegalArgumentException("Invalid argument, cluster name is required");
-    
-    if (!requestMap.containsKey(ALERT_DEF_INTERVAL))
+    }
+
+    if (!requestMap.containsKey(ALERT_DEF_INTERVAL)) {
       throw new IllegalArgumentException("Check interval must be specified");
-    
+    }
+
     Integer interval = Integer.valueOf((String) requestMap.get(ALERT_DEF_INTERVAL));
 
-    if (!requestMap.containsKey(ALERT_DEF_NAME))
+    if (!requestMap.containsKey(ALERT_DEF_NAME)) {
       throw new IllegalArgumentException("Definition name must be specified");
-    
-    if (!requestMap.containsKey(ALERT_DEF_SERVICE_NAME))
+    }
+
+    if (!requestMap.containsKey(ALERT_DEF_SERVICE_NAME)) {
       throw new IllegalArgumentException("Service name must be specified");
-    
-    if (!requestMap.containsKey(ALERT_DEF_SOURCE_TYPE))
+    }
+
+    if (!requestMap.containsKey(ALERT_DEF_SOURCE_TYPE)) {
       throw new IllegalArgumentException(String.format(
           "Source type must be specified and one of %s", EnumSet.allOf(
               SourceType.class)));
+    }
 
     JsonObject jsonObj = new JsonObject();
-    
+
     for (Entry<String, Object> entry : requestMap.entrySet()) {
       String propCat = PropertyHelper.getPropertyCategory(entry.getKey());
       String propName = PropertyHelper.getPropertyName(entry.getKey());
@@ -155,11 +162,12 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
       }
     }
 
-    if (0 == jsonObj.entrySet().size())
+    if (0 == jsonObj.entrySet().size()) {
       throw new IllegalArgumentException("Source must be specified");
-    
+    }
+
     Cluster cluster = getManagementController().getClusters().getCluster(clusterName);
-    
+
     AlertDefinitionEntity entity = new AlertDefinitionEntity();
     entity.setClusterId(Long.valueOf(cluster.getClusterId()));
     entity.setComponentName((String) requestMap.get(ALERT_DEF_COMPONENT_NAME));
@@ -167,14 +175,14 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
 
     boolean enabled = requestMap.containsKey(ALERT_DEF_ENABLED) ?
         Boolean.parseBoolean((String)requestMap.get(ALERT_DEF_ENABLED)) : true;
-    
+
     entity.setEnabled(enabled);
     entity.setHash(UUID.randomUUID().toString());
     entity.setScheduleInterval(interval);
     entity.setServiceName((String) requestMap.get(ALERT_DEF_SERVICE_NAME));
     entity.setSourceType((String) requestMap.get(ALERT_DEF_SOURCE_TYPE));
     entity.setSource(jsonObj.toString());
-    
+
     Scope scope = null;
     String desiredScope = (String) requestMap.get(ALERT_DEF_SCOPE);
     if (null != desiredScope && desiredScope.length() > 0) {
@@ -190,17 +198,18 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
   public Set<Resource> getResources(Request request, Predicate predicate)
       throws SystemException, UnsupportedPropertyException,
       NoSuchResourceException, NoSuchParentResourceException {
-    
+
     Set<String> requestPropertyIds = getRequestPropertyIds(request, predicate);
-    
+
     Set<Resource> results = new HashSet<Resource>();
-    
+
     for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) {
       String clusterName = (String) propertyMap.get(ALERT_DEF_CLUSTER_NAME);
-      
-      if (null == clusterName || clusterName.isEmpty())
+
+      if (null == clusterName || clusterName.isEmpty()) {
         throw new IllegalArgumentException("Invalid argument, cluster name is required");
-      
+      }
+
       String id = (String) propertyMap.get(ALERT_DEF_ID);
       if (null != id) {
         AlertDefinitionEntity entity = alertDefinitionDAO.findById(Long.parseLong(id));
@@ -208,14 +217,14 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
           results.add(toResource(false, clusterName, entity, requestPropertyIds));
         }
       } else {
-        
+
         Cluster cluster = null;
         try {
           cluster = getManagementController().getClusters().getCluster(clusterName);
         } catch (AmbariException e) {
           throw new NoSuchResourceException("Parent Cluster resource doesn't exist", e);
         }
-        
+
         List<AlertDefinitionEntity> entities = alertDefinitionDAO.findAll(
             cluster.getClusterId());
 
@@ -224,7 +233,7 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
         }
       }
     }
-    
+
     return results;
   }
 
@@ -236,40 +245,44 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
     for (Map<String, Object> requestPropMap : request.getProperties()) {
       for (Map<String, Object> propertyMap : getPropertyMaps(requestPropMap, predicate)) {
         Long id = (Long) propertyMap.get(ALERT_DEF_ID);
-        
+
         AlertDefinitionEntity entity = alertDefinitionDAO.findById(id.longValue());
-        if (null == entity)
+        if (null == entity) {
           continue;
+        }
 
-        if (propertyMap.containsKey(ALERT_DEF_NAME))
+        if (propertyMap.containsKey(ALERT_DEF_NAME)) {
           entity.setDefinitionName((String) propertyMap.get(ALERT_DEF_NAME));
-        
+        }
+
         if (propertyMap.containsKey(ALERT_DEF_ENABLED)) {
           entity.setEnabled(Boolean.parseBoolean(
               (String) propertyMap.get(ALERT_DEF_ENABLED)));
         }
-        
+
         if (propertyMap.containsKey(ALERT_DEF_INTERVAL)) {
           entity.setScheduleInterval(Integer.valueOf(
               (String) propertyMap.get(ALERT_DEF_INTERVAL)));
         }
-                
+
         if (propertyMap.containsKey(ALERT_DEF_SCOPE)){
           Scope scope = null;
           String desiredScope = (String) propertyMap.get(ALERT_DEF_SCOPE);
-              
-          if (null != desiredScope && desiredScope.length() > 0)
+
+          if (null != desiredScope && desiredScope.length() > 0) {
             scope = Scope.valueOf((desiredScope));
-         
+          }
+
           entity.setScope(scope);
         }
-        
 
-        if (propertyMap.containsKey(ALERT_DEF_SOURCE_TYPE))
+
+        if (propertyMap.containsKey(ALERT_DEF_SOURCE_TYPE)) {
           entity.setSourceType((String) propertyMap.get(ALERT_DEF_SOURCE_TYPE));
-          
+        }
+
         JsonObject jsonObj = new JsonObject();
-        
+
         for (Entry<String, Object> entry : propertyMap.entrySet()) {
           String propCat = PropertyHelper.getPropertyCategory(entry.getKey());
           String propName = PropertyHelper.getPropertyName(entry.getKey());
@@ -278,16 +291,16 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
             jsonObj.addProperty(propName, entry.getValue().toString());
           }
         }
-        
+
         entity.setHash(UUID.randomUUID().toString());
-        
+
         alertDefinitionDAO.merge(entity);
       }
     }
-    
+
     notifyUpdate(Resource.Type.AlertDefinition, request, predicate);
 
-    return getRequestStatus(null);    
+    return getRequestStatus(null);
   }
 
   @Override
@@ -297,7 +310,7 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
 
     Set<Resource> resources = getResources(
         new RequestImpl(null, null, null, null), predicate);
-    
+
     Set<Long> definitionIds = new HashSet<Long>();
 
     for (final Resource resource : resources) {
@@ -307,7 +320,7 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
     for (Long definitionId : definitionIds) {
 
       LOG.info("Deleting alert definition {}", definitionId);
-      
+
       final AlertDefinitionEntity ad = alertDefinitionDAO.findById(definitionId.longValue());
 
       modifyResources(new Command<Void>() {
@@ -323,11 +336,11 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
     return getRequestStatus(null);
 
   }
-  
+
   private Resource toResource(boolean isCollection, String clusterName,
       AlertDefinitionEntity entity, Set<String> requestedIds) {
     Resource resource = new ResourceImpl(Resource.Type.AlertDefinition);
-    
+
     setResourceProperty(resource, ALERT_DEF_CLUSTER_NAME, clusterName, requestedIds);
     setResourceProperty(resource, ALERT_DEF_ID, entity.getDefinitionId(), requestedIds);
     setResourceProperty(resource, ALERT_DEF_NAME, entity.getDefinitionName(), requestedIds);
@@ -337,22 +350,24 @@ public class AlertDefinitionResourceProvider extends AbstractControllerResourceP
     setResourceProperty(resource, ALERT_DEF_ENABLED, Boolean.valueOf(entity.getEnabled()), requestedIds);
     setResourceProperty(resource, ALERT_DEF_SCOPE, entity.getScope(), requestedIds);
     setResourceProperty(resource, ALERT_DEF_SOURCE_TYPE, entity.getSourceType(), requestedIds);
-    
+    setResourceProperty(resource, ALERT_DEF_UUID, entity.getHash(),
+        requestedIds);
+
     if (!isCollection && null != resource.getPropertyValue(ALERT_DEF_SOURCE_TYPE)) {
-      
+
       try {
         Map<String, String> map = gson.<Map<String, String>>fromJson(entity.getSource(), Map.class);
-        
+
         for (Entry<String, String> entry : map.entrySet()) {
           String subProp = PropertyHelper.getPropertyId(ALERT_DEF_SOURCE, entry.getKey());
-          resource.setProperty(subProp, entry.getValue());  
+          resource.setProperty(subProp, entry.getValue());
         }
       } catch (Exception e) {
         LOG.error("Could not coerce alert JSON into a type");
       }
     }
-    
+
     return resource;
   }
-  
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
index 05881e4..db5c63f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAO.java
@@ -17,12 +17,16 @@
  */
 package org.apache.ambari.server.orm.dao;
 
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
 
+import org.apache.ambari.server.controller.RootServiceResponseFactory;
 import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.state.alert.Scope;
 
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -61,7 +65,7 @@ public class AlertDefinitionDAO {
 
   /**
    * Gets an alert definition with the specified ID.
-   * 
+   *
    * @param definitionId
    *          the ID of the definition to retrieve.
    * @return the alert definition or {@code null} if none exists.
@@ -74,7 +78,7 @@ public class AlertDefinitionDAO {
   /**
    * Gets an alert definition with the specified name. Alert definition names
    * are unique within a cluster.
-   * 
+   *
    * @param clusterId
    *          the ID of the cluster.
    * @param definitionName
@@ -93,7 +97,7 @@ public class AlertDefinitionDAO {
 
   /**
    * Gets all alert definitions stored in the database.
-   * 
+   *
    * @return all alert definitions or an empty list if none exist (never
    *         {@code null}).
    */
@@ -106,7 +110,7 @@ public class AlertDefinitionDAO {
 
   /**
    * Gets all alert definitions stored in the database.
-   * 
+   *
    * @return all alert definitions or empty list if none exist (never
    *         {@code null}).
    */
@@ -120,8 +124,114 @@ public class AlertDefinitionDAO {
   }
 
   /**
+   * Gets all alert definitions for the given service in the specified cluster.
+   *
+   * @param clusterId
+   *          the ID of the cluster.
+   * @param serviceName
+   *          the name of the service.
+   *
+   * @return all alert definitions for the service or empty list if none exist
+   *         (never {@code null}).
+   */
+  public List<AlertDefinitionEntity> findByService(long clusterId,
+      String serviceName) {
+    TypedQuery<AlertDefinitionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "AlertDefinitionEntity.findByService", AlertDefinitionEntity.class);
+
+    query.setParameter("clusterId", clusterId);
+    query.setParameter("serviceName", serviceName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets all alert definitions for the specified services that do not have a
+   * component. These definitions are assumed to be run on the master hosts.
+   *
+   * @param clusterId
+   *          the ID of the cluster.
+   * @param services
+   *          the services to match on.
+   *
+   * @return all alert definitions for the services or empty list if none exist
+   *         (never {@code null}).
+   */
+  public List<AlertDefinitionEntity> findByServiceMaster(long clusterId,
+      Set<String> services) {
+    if (null == services || services.size() == 0) {
+      return Collections.emptyList();
+    }
+
+    TypedQuery<AlertDefinitionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "AlertDefinitionEntity.findByServiceMaster",
+        AlertDefinitionEntity.class);
+
+    query.setParameter("clusterId", clusterId);
+    query.setParameter("services", services);
+    query.setParameter("scope", Scope.SERVICE);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets all alert definitions that are not bound to a particular service. An
+   * example of this type of definition is a host capacity alert.
+   *
+   * @param clusterId
+   *          the ID of the cluster.
+   * @param serviceName
+   *          the name of the service (not {@code null}).
+   * @param componentName
+   *          the name of the service component (not {@code null}).
+   * @return all alert definitions that are not bound to a service or an empty
+   *         list (never {@code null}).
+   */
+  public List<AlertDefinitionEntity> findByServiceComponent(long clusterId,
+      String serviceName, String componentName) {
+    if (null == serviceName || null == componentName) {
+      return Collections.emptyList();
+    }
+
+    TypedQuery<AlertDefinitionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "AlertDefinitionEntity.findByServiceAndComponent",
+        AlertDefinitionEntity.class);
+
+    query.setParameter("clusterId", clusterId);
+    query.setParameter("serviceName", serviceName);
+    query.setParameter("componentName", componentName);
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
+   * Gets all alert definitions that are not bound to a particular service. An
+   * example of this type of definition is a host capacity alert.
+   *
+   * @param clusterId
+   *          the ID of the cluster.
+   * @return all alert definitions that are not bound to a service or an empty
+   *         list (never {@code null}).
+   */
+  public List<AlertDefinitionEntity> findAgentScoped(long clusterId) {
+    TypedQuery<AlertDefinitionEntity> query = entityManagerProvider.get().createNamedQuery(
+        "AlertDefinitionEntity.findByServiceAndComponent",
+        AlertDefinitionEntity.class);
+
+    query.setParameter("clusterId", clusterId);
+
+    query.setParameter("serviceName",
+        RootServiceResponseFactory.Services.AMBARI.name());
+
+    query.setParameter("componentName",
+        RootServiceResponseFactory.Components.AMBARI_AGENT.name());
+
+    return daoUtils.selectList(query);
+  }
+
+  /**
    * Persists a new alert definition.
-   * 
+   *
    * @param alertDefinition
    *          the definition to persist (not {@code null}).
    */
@@ -132,7 +242,7 @@ public class AlertDefinitionDAO {
 
   /**
    * Refresh the state of the alert definition from the database.
-   * 
+   *
    * @param alertDefinition
    *          the definition to refresh (not {@code null}).
    */
@@ -144,7 +254,7 @@ public class AlertDefinitionDAO {
   /**
    * Merge the speicified alert definition with the existing definition in the
    * database.
-   * 
+   *
    * @param alertDefinition
    *          the definition to merge (not {@code null}).
    * @return the updated definition with merged content (never {@code null}).
@@ -157,7 +267,7 @@ public class AlertDefinitionDAO {
   /**
    * Removes the specified alert definition and all related history and
    * associations from the database.
-   * 
+   *
    * @param alertDefinition
    *          the definition to remove.
    */
@@ -170,7 +280,8 @@ public class AlertDefinitionDAO {
     EntityManager entityManager = entityManagerProvider.get();
 
     alertDefinition = findById(alertDefinition.getDefinitionId());
-    if (null != alertDefinition)
+    if (null != alertDefinition) {
       entityManager.remove(alertDefinition);
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
index de30921..66a3af0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/AlertDefinitionEntity.java
@@ -50,9 +50,12 @@ import org.apache.ambari.server.state.alert.Scope;
     "cluster_id", "definition_name" }))
 @TableGenerator(name = "alert_definition_id_generator", table = "ambari_sequences", pkColumnName = "sequence_name", valueColumnName = "value", pkColumnValue = "alert_definition_id_seq", initialValue = 0, allocationSize = 1)
 @NamedQueries({
-    @NamedQuery(name = "AlertDefinitionEntity.findAll", query = "SELECT alertDefinition FROM AlertDefinitionEntity alertDefinition"),
-    @NamedQuery(name = "AlertDefinitionEntity.findAllInCluster", query = "SELECT alertDefinition FROM AlertDefinitionEntity alertDefinition WHERE alertDefinition.clusterId = :clusterId"),
-    @NamedQuery(name = "AlertDefinitionEntity.findByName", query = "SELECT alertDefinition FROM AlertDefinitionEntity alertDefinition WHERE alertDefinition.definitionName = :definitionName AND alertDefinition.clusterId = :clusterId"), })
+    @NamedQuery(name = "AlertDefinitionEntity.findAll", query = "SELECT ad FROM AlertDefinitionEntity ad"),
+    @NamedQuery(name = "AlertDefinitionEntity.findAllInCluster", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.clusterId = :clusterId"),
+    @NamedQuery(name = "AlertDefinitionEntity.findByName", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.definitionName = :definitionName AND ad.clusterId = :clusterId"),
+    @NamedQuery(name = "AlertDefinitionEntity.findByService", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName = :serviceName AND ad.clusterId = :clusterId"),
+    @NamedQuery(name = "AlertDefinitionEntity.findByServiceAndComponent", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName = :serviceName AND ad.componentName = :componentName AND ad.clusterId = :clusterId"),
+    @NamedQuery(name = "AlertDefinitionEntity.findByServiceMaster", query = "SELECT ad FROM AlertDefinitionEntity ad WHERE ad.serviceName IN :services AND ad.scope = :scope AND ad.clusterId = :clusterId") })
 public class AlertDefinitionEntity {
 
   @Id

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
new file mode 100644
index 0000000..1f31c35
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/alert/AlertDefinitionHash.java
@@ -0,0 +1,272 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.state.alert;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
+import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.commons.codec.binary.Hex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * The {@link AlertDefinitionHash} class is used to generate an MD5 hash for a
+ * list of {@link AlertDefinitionEntity}s. It is used in order to represent the
+ * state of a group of definitions by using
+ * {@link AlertDefinitionEntity#getHash()}
+ */
+@Singleton
+public class AlertDefinitionHash {
+
+  /**
+   * Logger.
+   */
+  private final static Logger LOG = LoggerFactory.getLogger(AlertDefinitionHash.class);
+
+  /**
+   * The hash returned when there are no definitions to hash.
+   */
+  public static String NULL_MD5_HASH = "37a6259cc0c1dae299a7866489dff0bd";
+
+  /**
+   * DAO for retrieving {@link AlertDefinitionEntity} instances.
+   */
+  @Inject
+  private AlertDefinitionDAO m_definitionDao;
+
+  /**
+   * All clusters.
+   */
+  @Inject
+  private Clusters m_clusters;
+
+  /**
+   * The hashes for all hosts.
+   */
+  private Map<String, String> m_hashes = new ConcurrentHashMap<String, String>();
+
+  /**
+   * Gets a unique hash value reprssenting all of the alert definitions that
+   * should be scheduled to run on a given host.
+   * <p/>
+   * This will not include alert definitions where the type is defined as
+   * {@link SourceType#AGGREGATE} since aggregate definitions are not scheduled
+   * to run on agent hosts.
+   * <p/>
+   * Hash values from this method are cached.
+   *
+   * @param clusterName
+   *          the cluster name (not {@code null}).
+   * @param hostName
+   *          the host name (not {@code null}).
+   * @return the unique hash or {@value #NULL_MD5_HASH} if none.
+   */
+  public String getHash(String clusterName, String hostName) {
+    String hash = m_hashes.get(hostName);
+    if (null != hash) {
+      return hash;
+    }
+
+    hash = hash(clusterName, hostName);
+    m_hashes.put(hostName, hash);
+
+    return hash;
+  }
+
+  /**
+   * Gets a mapping between cluster and alert definition hashes for all of the
+   * clusters that the given host belongs to.
+   *
+   * @param hostName
+   *          the host name (not {@code null}).
+   * @return a mapping between cluster and alert definition hash or an empty map
+   *         (never @code null).
+   * @see #getHash(String, String)
+   * @throws AmbariException
+   */
+  public Map<String, String> getHashes(String hostName)
+      throws AmbariException {
+    Set<Cluster> clusters = m_clusters.getClustersForHost(hostName);
+    if (null == clusters || clusters.size() == 0) {
+      return Collections.emptyMap();
+    }
+
+    Map<String, String> hashes = new HashMap<String, String>();
+    for (Cluster cluster : clusters) {
+      String clusterName = cluster.getClusterName();
+      String hash = getHash(clusterName, hostName);
+      hashes.put(clusterName, hash);
+    }
+
+    return hashes;
+  }
+
+  /**
+   * Gets the alert definitions for the specified host. This will include the
+   * following types of alert definitions:
+   * <ul>
+   * <li>Service/Component alerts</li>
+   * <li>Service alerts where the host is a MASTER</li>
+   * <li>Host alerts that are not bound to a service</li>
+   * </ul>
+   *
+   * @param clusterName
+   *          the cluster name (not {@code null}).
+   * @param hostName
+   *          the host name (not {@code null}).
+   * @return the alert definitions for the host, or an empty set (never
+   *         {@code null}).
+   */
+  public Set<AlertDefinitionEntity> getAlertDefinitions(String clusterName,
+      String hostName) {
+    Set<AlertDefinitionEntity> definitions = new HashSet<AlertDefinitionEntity>();
+
+    try {
+      Cluster cluster = m_clusters.getCluster(clusterName);
+      if (null == cluster) {
+        LOG.warn("Unable to get alert definitions for the missing cluster {}",
+            clusterName);
+
+        return Collections.emptySet();
+      }
+
+      long clusterId = cluster.getClusterId();
+      List<ServiceComponentHost> serviceComponents = cluster.getServiceComponentHosts(hostName);
+      if (null == serviceComponents || serviceComponents.size() == 0) {
+        LOG.warn(
+            "Unable to get alert definitions for {} since there are no service components defined",
+            hostName);
+
+        return Collections.emptySet();
+      }
+
+      for (ServiceComponentHost serviceComponent : serviceComponents) {
+        String serviceName = serviceComponent.getServiceName();
+        String componentName = serviceComponent.getServiceComponentName();
+
+        // add all alerts for this service/component pair
+        definitions.addAll(m_definitionDao.findByServiceComponent(
+            clusterId, serviceName, componentName));
+      }
+
+      // for every service, get the master components and see if the host
+      // is a master
+      Set<String> services = new HashSet<String>();
+      for (Entry<String, Service> entry : cluster.getServices().entrySet()) {
+        Service service = entry.getValue();
+        Map<String, ServiceComponent> components = service.getServiceComponents();
+        for (Entry<String, ServiceComponent> component : components.entrySet()) {
+          if (component.getValue().isMasterComponent()) {
+            Map<String, ServiceComponentHost> hosts = component.getValue().getServiceComponentHosts();
+
+            if( hosts.containsKey( hostName ) ){
+              services.add(service.getName());
+            }
+          }
+        }
+      }
+
+      // add all service scoped alerts
+      if( services.size() > 0 ){
+        definitions.addAll(m_definitionDao.findByServiceMaster(clusterId,
+            services));
+      }
+
+      // add any alerts not bound to a service (host level alerts)
+      definitions.addAll(m_definitionDao.findAgentScoped(clusterId));
+    } catch (AmbariException ambariException) {
+      LOG.error("Unable to get alert definitions", ambariException);
+      return Collections.emptySet();
+    }
+
+    return definitions;
+  }
+
+  /**
+   * Calculates a unique hash value representing all of the alert definitions
+   * that should be scheduled to run on a given host. Alerts of type
+   * {@link SourceType#AGGREGATE} are not included in the hash since they are
+   * not run on the agents.
+   *
+   * @param clusterName
+   *          the cluster name (not {@code null}).
+   * @param hostName
+   *          the host name (not {@code null}).
+   * @return the unique hash or {@value #NULL_MD5_HASH} if none.
+   */
+  private String hash(String clusterName, String hostName) {
+    Set<AlertDefinitionEntity> definitions = getAlertDefinitions(clusterName,
+        hostName);
+
+    // no definitions found for this host, don't bother hashing
+    if( null == definitions || definitions.size() == 0 ) {
+      return NULL_MD5_HASH;
+    }
+
+    // strip out all AGGREGATE types
+    Iterator<AlertDefinitionEntity> iterator = definitions.iterator();
+    while (iterator.hasNext()) {
+      if (SourceType.AGGREGATE.equals(iterator.next().getSourceType())) {
+        iterator.remove();
+      }
+    }
+
+    // build the UUIDs
+    List<String> uuids = new ArrayList<String>(definitions.size());
+    for (AlertDefinitionEntity definition : definitions) {
+      uuids.add(definition.getHash());
+    }
+
+    // sort the UUIDs so that the digest is created with bytes in the same order
+    Collections.sort(uuids);
+
+    try {
+      MessageDigest digest = MessageDigest.getInstance("MD5");
+      for (String uuid : uuids) {
+        digest.update(uuid.getBytes());
+      }
+
+      byte[] hashBytes = digest.digest();
+      return Hex.encodeHexString(hashBytes);
+    } catch (NoSuchAlgorithmException nsae) {
+      LOG.warn("Unable to calculate MD5 alert definition hash", nsae);
+      return NULL_MD5_HASH;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/main/resources/properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index e515b91..e80c0e6 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -420,7 +420,8 @@
       "AlertDefinition/interval",
       "AlertDefinition/enabled",
       "AlertDefinition/scope",
-      "AlertDefinition/source"
+      "AlertDefinition/source",
+      "AlertDefinition/uuid"
     ],      
     "Controller":[
         "Controllers/name",

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
index fc57389..c6e26e8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AlertDefinitionResourceProviderTest.java
@@ -34,6 +34,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -56,59 +57,65 @@ import org.junit.Test;
 public class AlertDefinitionResourceProviderTest {
 
   AlertDefinitionDAO dao = null;
-  
+
+  private static String DEFINITION_UUID = UUID.randomUUID().toString();
+
   @Before
   public void before() {
     dao = createStrictMock(AlertDefinitionDAO.class);
-    
+
     AlertDefinitionResourceProvider.init(dao);
   }
-  
+
   @Test
   public void testGetResourcesNoPredicate() throws Exception {
     AlertDefinitionResourceProvider provider = createProvider(null);
-    
+
     Request request = PropertyHelper.getReadRequest("AlertDefinition/cluster_name",
         "AlertDefinition/id");
-    
+
     Set<Resource> results = provider.getResources(request, null);
-    
+
     assertEquals(0, results.size());
-  }  
+  }
 
   @Test
   public void testGetResourcesClusterPredicate() throws Exception {
     Request request = PropertyHelper.getReadRequest(
         AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME,
         AlertDefinitionResourceProvider.ALERT_DEF_ID,
-        AlertDefinitionResourceProvider.ALERT_DEF_NAME);
-    
+        AlertDefinitionResourceProvider.ALERT_DEF_NAME,
+        AlertDefinitionResourceProvider.ALERT_DEF_UUID);
+
     AmbariManagementController amc = createMock(AmbariManagementController.class);
     Clusters clusters = createMock(Clusters.class);
     Cluster cluster = createMock(Cluster.class);
     expect(amc.getClusters()).andReturn(clusters).atLeastOnce();
     expect(clusters.getCluster((String) anyObject())).andReturn(cluster).atLeastOnce();
     expect(cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
-    
+
     Predicate predicate = new PredicateBuilder().property(
-        AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME).equals("c1").toPredicate();    
-    
+        AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME).equals("c1").toPredicate();
+
     expect(dao.findAll(1L)).andReturn(getMockEntities());
 
     replay(amc, clusters, cluster, dao);
-    
-    AlertDefinitionResourceProvider provider = createProvider(amc);    
+
+    AlertDefinitionResourceProvider provider = createProvider(amc);
     Set<Resource> results = provider.getResources(request, predicate);
-    
+
     assertEquals(1, results.size());
-    
+
     Resource r = results.iterator().next();
-    
+
     Assert.assertEquals("my_def", r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_NAME));
-    
+
+    Assert.assertEquals(DEFINITION_UUID,
+        r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_UUID));
+
     verify(amc, clusters, cluster, dao);
   }
-  
+
   @Test
   public void testGetSingleResource() throws Exception {
     Request request = PropertyHelper.getReadRequest(
@@ -116,29 +123,29 @@ public class AlertDefinitionResourceProviderTest {
         AlertDefinitionResourceProvider.ALERT_DEF_ID,
         AlertDefinitionResourceProvider.ALERT_DEF_NAME,
         AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE);
-    
+
     AmbariManagementController amc = createMock(AmbariManagementController.class);
     Clusters clusters = createMock(Clusters.class);
     Cluster cluster = createMock(Cluster.class);
     expect(amc.getClusters()).andReturn(clusters).atLeastOnce();
     expect(clusters.getCluster((String) anyObject())).andReturn(cluster).atLeastOnce();
     expect(cluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
-    
+
     Predicate predicate = new PredicateBuilder().property(
         AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME).equals("c1")
-          .and().property(AlertDefinitionResourceProvider.ALERT_DEF_ID).equals("1").toPredicate();    
-    
+          .and().property(AlertDefinitionResourceProvider.ALERT_DEF_ID).equals("1").toPredicate();
+
     expect(dao.findById(1L)).andReturn(getMockEntities().get(0));
 
     replay(amc, clusters, cluster, dao);
-    
-    AlertDefinitionResourceProvider provider = createProvider(amc);    
+
+    AlertDefinitionResourceProvider provider = createProvider(amc);
     Set<Resource> results = provider.getResources(request, predicate);
-    
+
     assertEquals(1, results.size());
-    
+
     Resource r = results.iterator().next();
-    
+
     Assert.assertEquals("my_def", r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_NAME));
     Assert.assertEquals("metric", r.getPropertyValue(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE));
     Assert.assertNotNull(r.getPropertyValue("AlertDefinition/source/type"));
@@ -156,24 +163,24 @@ public class AlertDefinitionResourceProviderTest {
     Capture<AlertDefinitionEntity> entityCapture = new Capture<AlertDefinitionEntity>();
     dao.create(capture(entityCapture));
     expectLastCall();
-    
+
     replay(amc, clusters, cluster, dao);
-    
+
     AlertDefinitionResourceProvider provider = createProvider(amc);
-    
+
     Map<String, Object> requestProps = new HashMap<String, Object>();
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME, "c1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_INTERVAL, "1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_NAME, "my_def");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SERVICE_NAME, "HDFS");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE, "METRIC");
-    
+
     Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
 
     provider.createResources(request);
-    
+
     Assert.assertTrue(entityCapture.hasCaptured());
-    AlertDefinitionEntity entity = entityCapture.getValue(); 
+    AlertDefinitionEntity entity = entityCapture.getValue();
     Assert.assertNotNull(entity);
 
     Assert.assertEquals(Long.valueOf(1), entity.getClusterId());
@@ -186,11 +193,11 @@ public class AlertDefinitionResourceProviderTest {
     Assert.assertEquals("HDFS", entity.getServiceName());
     Assert.assertNotNull(entity.getSource());
     Assert.assertEquals("METRIC", entity.getSourceType());
-    
+
     verify(amc, clusters, cluster, dao);
 
   }
-  
+
   @Test
   public void testUpdateResources() throws Exception {
     AmbariManagementController amc = createMock(AmbariManagementController.class);
@@ -203,40 +210,40 @@ public class AlertDefinitionResourceProviderTest {
     Capture<AlertDefinitionEntity> entityCapture = new Capture<AlertDefinitionEntity>();
     dao.create(capture(entityCapture));
     expectLastCall();
-    
+
     replay(amc, clusters, cluster, dao);
-    
+
     Map<String, Object> requestProps = new HashMap<String, Object>();
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME, "c1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_INTERVAL, "1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_NAME, "my_def");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SERVICE_NAME, "HDFS");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE, "METRIC");
-    
+
     Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
 
     AlertDefinitionResourceProvider provider = createProvider(amc);
-    
+
     provider.createResources(request);
-    
+
     Assert.assertTrue(entityCapture.hasCaptured());
-    AlertDefinitionEntity entity = entityCapture.getValue(); 
+    AlertDefinitionEntity entity = entityCapture.getValue();
     Assert.assertNotNull(entity);
-    
+
     Predicate p = new PredicateBuilder().property(
         AlertDefinitionResourceProvider.ALERT_DEF_ID).equals("1").and().property(
             AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME).equals("c1").toPredicate();
     // everything is mocked, there is no DB
     entity.setDefinitionId(Long.valueOf(1));
-    
+
     String oldName = entity.getDefinitionName();
     String oldHash = entity.getHash();
-    
+
     resetToStrict(dao);
     expect(dao.findById(1L)).andReturn(entity).anyTimes();
     expect(dao.merge((AlertDefinitionEntity) anyObject())).andReturn(entity).anyTimes();
     replay(dao);
-    
+
     requestProps = new HashMap<String, Object>();
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME, "c1");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_INTERVAL, "1");
@@ -244,15 +251,15 @@ public class AlertDefinitionResourceProviderTest {
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SERVICE_NAME, "HDFS");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE, "METRIC");
     request = PropertyHelper.getUpdateRequest(requestProps, null);
-    
+
     provider.updateResources(request, p);
 
     Assert.assertFalse(oldHash.equals(entity.getHash()));
     Assert.assertFalse(oldName.equals(entity.getDefinitionName()));
-    
+
     verify(amc, clusters, cluster, dao);
   }
-  
+
   @Test
   public void testDeleteResources() throws Exception {
     AmbariManagementController amc = createMock(AmbariManagementController.class);
@@ -265,9 +272,9 @@ public class AlertDefinitionResourceProviderTest {
     Capture<AlertDefinitionEntity> entityCapture = new Capture<AlertDefinitionEntity>();
     dao.create(capture(entityCapture));
     expectLastCall();
-    
+
     replay(amc, clusters, cluster, dao);
-    
+
     AlertDefinitionResourceProvider provider = createProvider(amc);
 
     Map<String, Object> requestProps = new HashMap<String, Object>();
@@ -276,43 +283,43 @@ public class AlertDefinitionResourceProviderTest {
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_NAME, "my_def");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SERVICE_NAME, "HDFS");
     requestProps.put(AlertDefinitionResourceProvider.ALERT_DEF_SOURCE_TYPE, "METRIC");
-    
+
     Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
 
     provider.createResources(request);
 
     Assert.assertTrue(entityCapture.hasCaptured());
-    AlertDefinitionEntity entity = entityCapture.getValue(); 
+    AlertDefinitionEntity entity = entityCapture.getValue();
     Assert.assertNotNull(entity);
-    
+
     Predicate p = new PredicateBuilder().property(
         AlertDefinitionResourceProvider.ALERT_DEF_ID).equals("1").and().property(
             AlertDefinitionResourceProvider.ALERT_DEF_CLUSTER_NAME).equals("c1").toPredicate();
     // everything is mocked, there is no DB
     entity.setDefinitionId(Long.valueOf(1));
-    
+
     resetToStrict(dao);
     expect(dao.findById(1L)).andReturn(entity).anyTimes();
     dao.remove(capture(entityCapture));
     expectLastCall();
     replay(dao);
-    
+
     provider.deleteResources(p);
-    
+
     AlertDefinitionEntity entity1 = entityCapture.getValue();
     Assert.assertEquals(Long.valueOf(1), entity1.getDefinitionId());
-    
+
     verify(amc, clusters, cluster, dao);
-    
+
   }
-  
+
   private AlertDefinitionResourceProvider createProvider(AmbariManagementController amc) {
     return new AlertDefinitionResourceProvider(
         PropertyHelper.getPropertyIds(Resource.Type.AlertDefinition),
         PropertyHelper.getKeyPropertyIds(Resource.Type.AlertDefinition),
         amc);
   }
-  
+
   private List<AlertDefinitionEntity> getMockEntities() {
     AlertDefinitionEntity entity = new AlertDefinitionEntity();
     entity.setClusterId(Long.valueOf(1L));
@@ -320,13 +327,12 @@ public class AlertDefinitionResourceProviderTest {
     entity.setDefinitionId(Long.valueOf(1L));
     entity.setDefinitionName("my_def");
     entity.setEnabled(true);
-    entity.setHash("tmphash");
+    entity.setHash(DEFINITION_UUID);
     entity.setScheduleInterval(Integer.valueOf(2));
     entity.setServiceName(null);
     entity.setSourceType("metric");
     entity.setSource("{'jmx': 'beanName/attributeName', 'host': '{{aa:123445}}'}");
-    
+
     return Arrays.asList(entity);
   }
-  
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
index f2ddcd7..d621a9b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/AlertDefinitionDAOTest.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.TimeZone;
 import java.util.UUID;
 
+import org.apache.ambari.server.controller.RootServiceResponseFactory;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
@@ -41,7 +42,6 @@ import org.apache.ambari.server.state.MaintenanceState;
 import org.apache.ambari.server.state.NotificationState;
 import org.apache.ambari.server.state.alert.Scope;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -65,7 +65,7 @@ public class AlertDefinitionDAOTest {
   OrmTestHelper helper;
 
   /**
-   * 
+   *
    */
   @Before
   public void setup() {
@@ -78,7 +78,9 @@ public class AlertDefinitionDAOTest {
     helper = injector.getInstance(OrmTestHelper.class);
     clusterId = helper.createCluster();
 
-    for (int i = 0; i < 8; i++) {
+    // create 8 HDFS alerts
+    int i = 0;
+    for (; i < 8; i++) {
       AlertDefinitionEntity definition = new AlertDefinitionEntity();
       definition.setDefinitionName("Alert Definition " + i);
       definition.setServiceName("HDFS");
@@ -91,6 +93,58 @@ public class AlertDefinitionDAOTest {
       definition.setSourceType("SCRIPT");
       dao.create(definition);
     }
+
+    // create 2 HDFS with components
+    for (; i < 10; i++) {
+      AlertDefinitionEntity definition = new AlertDefinitionEntity();
+      definition.setDefinitionName("Alert Definition " + i);
+      definition.setServiceName("HDFS");
+
+      if (i == 9) {
+        definition.setComponentName("NAMENODE");
+      } else {
+        definition.setComponentName("DATANODE");
+      }
+
+      definition.setClusterId(clusterId);
+      definition.setHash(UUID.randomUUID().toString());
+      definition.setScheduleInterval(60);
+      definition.setScope(Scope.SERVICE);
+      definition.setSource("Source " + i);
+      definition.setSourceType("SCRIPT");
+      dao.create(definition);
+    }
+
+    // create 2 host scoped
+    for (; i < 12; i++) {
+      AlertDefinitionEntity definition = new AlertDefinitionEntity();
+      definition.setDefinitionName("Alert Definition " + i);
+      definition.setServiceName("OOZIE");
+      definition.setComponentName("OOZIE_SERVER");
+      definition.setClusterId(clusterId);
+      definition.setHash(UUID.randomUUID().toString());
+      definition.setScheduleInterval(60);
+      definition.setScope(Scope.HOST);
+      definition.setSource("Source " + i);
+      definition.setSourceType("SCRIPT");
+      dao.create(definition);
+    }
+
+    // create 3 agent alerts
+    for (; i < 15; i++) {
+      AlertDefinitionEntity definition = new AlertDefinitionEntity();
+      definition.setDefinitionName("Alert Definition " + i);
+      definition.setServiceName(RootServiceResponseFactory.Services.AMBARI.name());
+      definition.setComponentName(RootServiceResponseFactory.Components.AMBARI_AGENT.name());
+      definition.setClusterId(clusterId);
+      definition.setHash(UUID.randomUUID().toString());
+      definition.setScheduleInterval(60);
+      definition.setScope(Scope.HOST);
+      definition.setSource("Source " + i);
+      definition.setSourceType("SCRIPT");
+      dao.create(definition);
+    }
+
   }
 
   @After
@@ -100,40 +154,77 @@ public class AlertDefinitionDAOTest {
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testFindByName() {
     List<AlertDefinitionEntity> definitions = dao.findAll();
-    Assert.assertNotNull(definitions);
+    assertNotNull(definitions);
     AlertDefinitionEntity definition = definitions.get(2);
     AlertDefinitionEntity retrieved = dao.findByName(
         definition.getClusterId(), definition.getDefinitionName());
 
-    Assert.assertEquals(definition, retrieved);
+    assertEquals(definition, retrieved);
   }
 
   /**
-   * 
+   *
    */
   @Test
   public void testFindAll() {
     List<AlertDefinitionEntity> definitions = dao.findAll();
-    Assert.assertNotNull(definitions);
-    Assert.assertEquals(8, definitions.size());
+    assertNotNull(definitions);
+    assertEquals(15, definitions.size());
   }
 
   /**
-   * 
+   *
    */
   @Test
-  public void findById() {
+  public void testFindById() {
     List<AlertDefinitionEntity> definitions = dao.findAll();
-    Assert.assertNotNull(definitions);
+    assertNotNull(definitions);
     AlertDefinitionEntity definition = definitions.get(2);
     AlertDefinitionEntity retrieved = dao.findById(definition.getDefinitionId());
+    assertEquals(definition, retrieved);
+  }
+
+  /**
+   *
+   */
+  @Test
+  public void testFindByService() {
+    List<AlertDefinitionEntity> definitions = dao.findByService(clusterId,
+        "HDFS");
+
+    assertNotNull(definitions);
+    assertEquals(10, definitions.size());
+
+    definitions = dao.findByService(clusterId, "YARN");
+    assertNotNull(definitions);
+    assertEquals(0, definitions.size());
+  }
 
-    Assert.assertEquals(definition, retrieved);
+  /**
+   *
+   */
+  @Test
+  public void testFindByServiceComponent() {
+    List<AlertDefinitionEntity> definitions = dao.findByServiceComponent(
+        clusterId, "OOZIE", "OOZIE_SERVER");
+
+    assertNotNull(definitions);
+    assertEquals(2, definitions.size());
+  }
+
+  /**
+   *
+   */
+  @Test
+  public void testFindAgentScoped() {
+    List<AlertDefinitionEntity> definitions = dao.findAgentScoped(clusterId);
+    assertNotNull(definitions);
+    assertEquals(3, definitions.size());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/ambari/blob/84b988bc/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertDefinitionHashTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertDefinitionHashTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertDefinitionHashTest.java
new file mode 100644
index 0000000..937417a
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/alerts/AlertDefinitionHashTest.java
@@ -0,0 +1,224 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.state.alerts;
+
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expect;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import junit.framework.TestCase;
+
+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.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.alert.AlertDefinitionHash;
+import org.apache.ambari.server.state.alert.Scope;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.util.Modules;
+
+/**
+ * Tests for {@link AlertDefinitionHash}.
+ */
+public class AlertDefinitionHashTest extends TestCase {
+
+  private AlertDefinitionHash m_hash;
+  private Clusters m_mockClusters;
+  private Cluster m_mockCluster;
+  private AlertDefinitionDAO m_mockDao;
+  private Injector m_injector;
+
+  private static final String CLUSTERNAME = "cluster1";
+  private static final String HOSTNAME = "c6401.ambari.apache.org";
+
+  /**
+   *
+   */
+  @Override
+  @Before
+  protected void setUp() throws Exception {
+    super.setUp();
+
+    m_injector = Guice.createInjector(Modules.override(
+        new InMemoryDefaultTestModule()).with(new MockModule()));
+
+    m_mockClusters = m_injector.getInstance(Clusters.class);
+    m_mockCluster = m_injector.getInstance(Cluster.class);
+    m_mockDao = m_injector.getInstance(AlertDefinitionDAO.class);
+
+    // add HDFS/NN
+    List<ServiceComponentHost> serviceComponentHosts = new ArrayList<ServiceComponentHost>();
+    ServiceComponentHost sch = EasyMock.createNiceMock(ServiceComponentHost.class);
+    expect(sch.getServiceName()).andReturn("HDFS").anyTimes();
+    expect(sch.getServiceComponentName()).andReturn("NAMENODE").anyTimes();
+    expect(sch.getHostName()).andReturn(HOSTNAME).anyTimes();
+    EasyMock.replay(sch);
+    serviceComponentHosts.add(sch);
+
+    // add HDFS/DN
+    sch = EasyMock.createNiceMock(ServiceComponentHost.class);
+    expect(sch.getServiceName()).andReturn("HDFS").anyTimes();
+    expect(sch.getServiceComponentName()).andReturn("DATANODE").anyTimes();
+    expect(sch.getHostName()).andReturn(HOSTNAME).anyTimes();
+    EasyMock.replay(sch);
+    serviceComponentHosts.add(sch);
+
+    Map<String, ServiceComponentHost> mapComponentHosts = new HashMap<String, ServiceComponentHost>();
+    ServiceComponentHost host = EasyMock.createNiceMock(ServiceComponentHost.class);
+    expect(host.getHostName()).andReturn(HOSTNAME).anyTimes();
+    mapComponentHosts.put(HOSTNAME, host);
+
+    Map<String, ServiceComponent> serviceComponents = new HashMap<String, ServiceComponent>();
+    ServiceComponent namenode = EasyMock.createNiceMock(ServiceComponent.class);
+    expect(namenode.getServiceComponentHosts()).andReturn(mapComponentHosts).anyTimes();
+    expect(namenode.isMasterComponent()).andReturn(true).anyTimes();
+    serviceComponents.put("NAMENODE", namenode);
+
+    // create HDFS for the cluster
+    Map<String, Service> services = new HashMap<String, Service>();
+    String hdfsName = "HDFS";
+    Service hdfs = EasyMock.createNiceMock(Service.class);
+    expect(hdfs.getName()).andReturn("HDFS").anyTimes();
+    expect(hdfs.getServiceComponents()).andReturn(serviceComponents).anyTimes();
+    services.put(hdfsName, hdfs);
+
+    // replay
+    EasyMock.replay(hdfs, host, namenode);
+
+    // Clusters mock
+    expect(m_mockClusters.getCluster((String) anyObject())).andReturn(
+        m_mockCluster).atLeastOnce();
+
+    // cluster mock
+    expect(m_mockCluster.getClusterId()).andReturn(Long.valueOf(1)).anyTimes();
+    expect(m_mockCluster.getClusterName()).andReturn(CLUSTERNAME).anyTimes();
+    expect(m_mockCluster.getServices()).andReturn(services).anyTimes();
+    expect(
+        m_mockCluster.getServiceComponentHosts(EasyMock.anyObject(String.class))).andReturn(
+        serviceComponentHosts).anyTimes();
+
+    AlertDefinitionEntity hdfsService = new AlertDefinitionEntity();
+    hdfsService.setDefinitionId(1L);
+    hdfsService.setClusterId(1L);
+    hdfsService.setHash(UUID.randomUUID().toString());
+    hdfsService.setServiceName("HDFS");
+    hdfsService.setComponentName("NAMENODE");
+    hdfsService.setScope(Scope.SERVICE);
+
+    AlertDefinitionEntity hdfsHost = new AlertDefinitionEntity();
+    hdfsHost.setDefinitionId(2L);
+    hdfsHost.setClusterId(1L);
+    hdfsHost.setHash(UUID.randomUUID().toString());
+    hdfsHost.setServiceName("HDFS");
+    hdfsHost.setComponentName("DATANODE");
+    hdfsHost.setScope(Scope.HOST);
+
+    AlertDefinitionEntity agentScoped = new AlertDefinitionEntity();
+    agentScoped.setDefinitionId(3L);
+    agentScoped.setClusterId(1L);
+    agentScoped.setHash(UUID.randomUUID().toString());
+    agentScoped.setServiceName("AMBARI");
+    agentScoped.setComponentName("AMBARI_AGENT");
+    agentScoped.setScope(Scope.HOST);
+
+    EasyMock.expect(
+        m_mockDao.findByServiceMaster(EasyMock.anyInt(),
+            (Set<String>) EasyMock.anyObject())).andReturn(
+        Collections.singletonList(hdfsService)).anyTimes();
+
+    EasyMock.expect(
+        m_mockDao.findByServiceComponent(EasyMock.anyInt(),
+            EasyMock.anyObject(String.class), EasyMock.anyObject(String.class))).andReturn(
+        Collections.singletonList(hdfsHost)).anyTimes();
+
+    EasyMock.expect(m_mockDao.findAgentScoped(EasyMock.anyInt())).andReturn(
+        Collections.singletonList(agentScoped)).anyTimes();
+
+    EasyMock.replay(m_mockClusters, m_mockCluster, m_mockDao);
+    m_hash = m_injector.getInstance(AlertDefinitionHash.class);
+  }
+
+  /**
+   *
+   */
+  @Override
+  @After
+  protected void tearDown() throws Exception {
+    super.tearDown();
+  }
+
+  /**
+   * Test method for {@link org.apache.ambari.server.state.alert.AlertDefinitionHash#getHash(java.lang.String, java.lang.String)}.
+   */
+  @Test
+  public void testGetHash() {
+    String hash = m_hash.getHash(CLUSTERNAME, HOSTNAME);
+    assertNotNull(hash);
+    assertNotSame(AlertDefinitionHash.NULL_MD5_HASH, hash);
+    assertEquals(hash, m_hash.getHash(CLUSTERNAME, HOSTNAME));
+  }
+
+  /**
+   * Test method for {@link org.apache.ambari.server.state.alert.AlertDefinitionHash#getAlertDefinitions(java.lang.String, java.lang.String)}.
+   */
+  @Test
+  public void testGetAlertDefinitions() {
+    Set<AlertDefinitionEntity> definitions = m_hash.getAlertDefinitions(
+        CLUSTERNAME, HOSTNAME);
+
+    assertEquals(3, definitions.size());
+  }
+
+  /**
+   *
+   */
+  private class MockModule implements Module {
+    /**
+     *
+     */
+    @Override
+    public void configure(Binder binder) {
+      binder.bind(Clusters.class).toInstance(
+          EasyMock.createNiceMock(Clusters.class));
+      binder.bind(Cluster.class).toInstance(
+          EasyMock.createNiceMock(Cluster.class));
+      binder.bind(AlertDefinitionDAO.class).toInstance(
+          EasyMock.createNiceMock(AlertDefinitionDAO.class));
+    }
+  }
+}


[4/6] git commit: AMBARI-6852. Views: views list from API is not respecting privileges.

Posted by jo...@apache.org.
AMBARI-6852. Views: views list from API is not respecting privileges.


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

Branch: refs/heads/branch-alerts-dev
Commit: f3bd5cc8cec545b3513c8442d42957b5fea71cd3
Parents: 2a617da
Author: Siddharth Wagle <sw...@hortonworks.com>
Authored: Wed Aug 13 12:42:32 2014 -0700
Committer: Siddharth Wagle <sw...@hortonworks.com>
Committed: Wed Aug 13 12:43:32 2014 -0700

----------------------------------------------------------------------
 .../internal/ViewResourceProvider.java          | 41 ++++++++++++++++----
 1 file changed, 33 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f3bd5cc8/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java
index b76c8e0..6a83793 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ViewResourceProvider.java
@@ -28,6 +28,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.orm.entities.ViewEntity;
+import org.apache.ambari.server.orm.entities.ViewInstanceEntity;
 import org.apache.ambari.server.view.ViewRegistry;
 
 import java.util.Collections;
@@ -63,7 +64,7 @@ public class ViewResourceProvider extends AbstractResourceProvider {
     propertyIds.add(VIEW_NAME_PROPERTY_ID);
   }
 
-  
+
   // ----- Constructors ------------------------------------------------------
 
   /**
@@ -73,12 +74,12 @@ public class ViewResourceProvider extends AbstractResourceProvider {
     super(propertyIds, keyPropertyIds);
   }
 
-  
+
   // ----- ResourceProvider --------------------------------------------------
 
   @Override
-  public RequestStatus createResources(Request request) 
-      throws SystemException, UnsupportedPropertyException, 
+  public RequestStatus createResources(Request request)
+      throws SystemException, UnsupportedPropertyException,
              ResourceAlreadyExistsException, NoSuchParentResourceException {
     throw new UnsupportedOperationException("Not yet supported.");
   }
@@ -102,11 +103,13 @@ public class ViewResourceProvider extends AbstractResourceProvider {
 
       for (ViewEntity viewDefinition : viewRegistry.getDefinitions()){
         if (viewName == null || viewName.equals(viewDefinition.getCommonName())) {
-          Resource resource = new ResourceImpl(Resource.Type.View);
+          if (includeDefinition(viewDefinition, true)) {
+            Resource resource = new ResourceImpl(Resource.Type.View);
 
-          setResourceProperty(resource, VIEW_NAME_PROPERTY_ID, viewDefinition.getCommonName(), requestedIds);
+            setResourceProperty(resource, VIEW_NAME_PROPERTY_ID, viewDefinition.getCommonName(), requestedIds);
 
-          resources.add(resource);
+            resources.add(resource);
+          }
         }
       }
     }
@@ -130,7 +133,29 @@ public class ViewResourceProvider extends AbstractResourceProvider {
     return keyPropertyIds;
   }
 
-  
+  /**
+   * Determine whether or not the given view definition resource should be included
+   * based on the permissions granted to the current user.
+   *
+   * @param definitionEntity  the view definition entity
+   * @param readOnly        indicate whether or not this is for a read only operation
+   *
+   * @return true if the view instance should be included based on the permissions of the current user
+   */
+  private boolean includeDefinition(ViewEntity definitionEntity, boolean readOnly) {
+
+    ViewRegistry viewRegistry = ViewRegistry.getInstance();
+
+    boolean allowed = false;
+
+    for (ViewInstanceEntity instanceEntity: definitionEntity.getInstances()) {
+      allowed |= viewRegistry.checkPermission(instanceEntity, readOnly);
+    }
+
+    return allowed;
+  }
+
+
   // ----- AbstractResourceProvider ------------------------------------------
 
   @Override


[2/6] git commit: AMBARI-6849 Admin: remove "roles" prop from users. (Buzhor Denys via ababiichuk)

Posted by jo...@apache.org.
AMBARI-6849 Admin: remove "roles" prop from users. (Buzhor Denys via ababiichuk)


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

Branch: refs/heads/branch-alerts-dev
Commit: efc6eee8d9f272ad389924584abb8f21073ecd15
Parents: 1eaf6a9
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Wed Aug 13 19:01:59 2014 +0300
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Wed Aug 13 19:02:17 2014 +0300

----------------------------------------------------------------------
 .../app/assets/data/users/privileges.json       | 14 ++++
 .../app/assets/data/users/privileges_admin.json | 14 ++++
 .../controllers/global/cluster_controller.js    | 48 +++++++++--
 ambari-web/app/mappers/users_mapper.js          | 20 +++--
 ambari-web/app/models/user.js                   | 17 ++--
 ambari-web/app/router.js                        | 87 ++++++++++++--------
 ambari-web/app/utils/ajax/ajax.js               | 12 +++
 ambari-web/app/views/main/admin/user/create.js  | 15 +---
 ambari-web/app/views/main/admin/user/edit.js    | 15 +---
 ambari-web/test/mappers/users_mapper_test.js    |  7 +-
 .../test/views/main/admin/user/create_test.js   | 25 ------
 .../test/views/main/admin/user/edit_test.js     | 25 ------
 12 files changed, 167 insertions(+), 132 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/assets/data/users/privileges.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/users/privileges.json b/ambari-web/app/assets/data/users/privileges.json
new file mode 100644
index 0000000..a461206
--- /dev/null
+++ b/ambari-web/app/assets/data/users/privileges.json
@@ -0,0 +1,14 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges?PrivilegeInfo/principal_name=admin&fields=PrivilegeInfo/*",
+  "items" : [
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/1",
+      "PrivilegeInfo" : {
+        "permission_name" : "AMBARI.ADMIN",
+        "principal_name" : "admin",
+        "principal_type" : "USER",
+        "privilege_id" : 1
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/assets/data/users/privileges_admin.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/users/privileges_admin.json b/ambari-web/app/assets/data/users/privileges_admin.json
new file mode 100644
index 0000000..a461206
--- /dev/null
+++ b/ambari-web/app/assets/data/users/privileges_admin.json
@@ -0,0 +1,14 @@
+{
+  "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges?PrivilegeInfo/principal_name=admin&fields=PrivilegeInfo/*",
+  "items" : [
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/privileges/1",
+      "PrivilegeInfo" : {
+        "permission_name" : "AMBARI.ADMIN",
+        "principal_name" : "admin",
+        "principal_type" : "USER",
+        "privilege_id" : 1
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/controllers/global/cluster_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/cluster_controller.js b/ambari-web/app/controllers/global/cluster_controller.js
index 77bcccd..ba1006c 100644
--- a/ambari-web/app/controllers/global/cluster_controller.js
+++ b/ambari-web/app/controllers/global/cluster_controller.js
@@ -259,7 +259,6 @@ App.ClusterController = Em.Controller.extend({
       return;
     }
     var clusterUrl = this.getUrl('/data/clusters/cluster.json', '?fields=Clusters');
-    var usersUrl = App.get('testMode') ? '/data/users/users.json' : App.get('apiPrefix') + '/users/?fields=*';
     var racksUrl = "/data/racks/racks.json";
 
 
@@ -292,13 +291,7 @@ App.ClusterController = Em.Controller.extend({
       });
     }
 
-    App.HttpClient.get(usersUrl, App.usersMapper, {
-      complete: function (jqXHR, textStatus) {
-        self.updateLoadStatus('users');
-      }
-    }, function (jqXHR, textStatus) {
-      self.updateLoadStatus('users');
-    });
+    this.loadUsersInfo();
 
     /**
      * Order of loading:
@@ -433,6 +426,45 @@ App.ClusterController = Em.Controller.extend({
     console.warn('can\'t get ambari properties');
   },
 
+  /**
+   * Load info about users.
+   **/
+  loadUsersInfo: function() {
+    return App.ajax.send({
+      name: 'users.all',
+      sender: this,
+      success: 'loadUsersSuccess',
+      error: 'loadUsersError'
+    });
+  },
+
+  loadUsersSuccess: function(data) {
+    App.ajax.send({
+      name: 'users.privileges',
+      sender: this,
+      data: {
+        users: data
+      },
+      success: 'loadUsersPrivilegesSuccess'
+    });
+  },
+
+  loadUsersError: function() {
+    this.updateLoadStatus('users');
+  },
+  /**
+   * Load privileges, check relations between user and privilege,
+   * map users using <code>App.usersMappper</code>.
+   **/
+  loadUsersPrivilegesSuccess: function(data, opt, params) {
+    params.users.items.forEach(function(user) {
+      user.privileges = {};
+      user.privileges.items = data.items.filterProperty('PrivilegeInfo.principal_name', user.Users.user_name);
+    });
+    App.usersMapper.map(params.users);
+    this.updateLoadStatus('users');
+  },
+
   updateClusterData: function () {
     var testUrl = App.get('isHadoop2Stack') ? '/data/clusters/HDP2/cluster.json' : '/data/clusters/cluster.json';
     var clusterUrl = this.getUrl(testUrl, '?fields=Clusters');

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/mappers/users_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/users_mapper.js b/ambari-web/app/mappers/users_mapper.js
index f85f682..19b3098 100644
--- a/ambari-web/app/mappers/users_mapper.js
+++ b/ambari-web/app/mappers/users_mapper.js
@@ -23,22 +23,32 @@ App.usersMapper = App.QuickDataMapper.create({
   config : {
     id : 'Users.user_name',
     user_name : 'Users.user_name',
-    roles : 'Users.roles',
     is_ldap: 'Users.ldap_user',
-    admin: 'Users.admin'
+    admin: 'Users.admin',
+    permissions: 'permissions'
   },
   map: function (json) {
     var self = this;
     json.items.forEach(function (item) {
       var result= [];
       if(!App.User.find().someProperty("userName", item.Users.user_name)) {
-        item.Users.admin = self.isAdmin(item.Users.roles);
+        item.permissions = [];
+        if (!!Em.get(item.privileges, 'items.length')) {
+          item.permissions = item.privileges.items.mapProperty('PrivilegeInfo.permission_name');
+        }
+        item.Users.admin = self.isAdmin(item.permissions);
         result.push(self.parseIt(item, self.config));
         App.store.loadMany(self.get('model'), result);
       }
     });
   },
-  isAdmin: function(roles) {
-    return (roles.indexOf("admin") >= 0);
+
+  /**
+   * Check if user is admin.
+   * @param {Array} permissionList
+   * @return {Boolean}
+   **/
+  isAdmin: function(permissionList) {
+    return permissionList.indexOf('AMBARI.ADMIN') > -1 || permissionList.indexOf('CLUSTER.OPERATOR') > -1;
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/models/user.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/user.js b/ambari-web/app/models/user.js
index d1f1b9a..d8729e2 100644
--- a/ambari-web/app/models/user.js
+++ b/ambari-web/app/models/user.js
@@ -24,7 +24,6 @@ App.User = DS.Model.extend({
   id:function(){
     return this.get('userName');
   }.property('userName'),
-  roles:DS.attr('string'),
   isLdap:DS.attr('boolean'),
   type: function(){
     if(this.get('isLdap')){
@@ -33,7 +32,17 @@ App.User = DS.Model.extend({
     return 'Local';
   }.property('isLdap'),
   auditItems:DS.hasMany('App.ServiceAudit'),
-  admin: DS.attr('boolean')
+  admin: DS.attr('boolean'),
+  /**
+   * List of permissions assigned to user
+   *  Available permissions:
+   *    AMBARI.ADMIN
+   *    CLUSTER.READ
+   *    CLUSTER.OPERATE
+   *    VIEW.USE
+   * @property {Array} permissions
+   **/
+  permissions: DS.attr('array')
 });
 
 App.EditUserForm = App.Form.extend({
@@ -48,7 +57,6 @@ App.EditUserForm = App.Form.extend({
     { name:"new_password", displayName:"New Password", displayType:"password",  isRequired: false },
     { name:"new_passwordRetype", displayName:"Retype New Password", displayType:"password", isRequired: false },
     { name:"admin", displayName:"Admin", displayType:"checkbox", isRequired:false },
-    { name:"roles", displayName:"Role", isRequired:false, isHidden:true },
     { name:"isLdap", displayName:"Type", isRequired:false, isHidden:true }
   ],
   fields:[],
@@ -116,8 +124,7 @@ App.CreateUserForm = App.Form.extend({
     { name:"userName", displayName:"Username", toLowerCase: function(){var v = this.get('value'); this.set('value', v.toLowerCase())}.observes('value') },
     { name:"password", displayName:"Password", displayType:"password", isRequired: true },
     { name:"passwordRetype", displayName:"Retype Password", displayType:"password", validator:"passwordRetype", isRequired: true },
-    { name:"admin", displayName:"Admin", displayType:"checkbox", isRequired:false, defaultValue: true},
-    { name:"roles", displayName:"Role", isRequired:false, isHidden:true }
+    { name:"admin", displayName:"Admin", displayType:"checkbox", isRequired:false, defaultValue: true}
   ],
   fields:[],
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/router.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/router.js b/ambari-web/app/router.js
index 505b760..8bc0cf3 100644
--- a/ambari-web/app/router.js
+++ b/ambari-web/app/router.js
@@ -155,6 +155,37 @@ App.Router = Em.Router.extend({
     return App.db.getUser();
   },
 
+  /**
+   * Get user privileges.
+   *
+   * @param {String} userName
+   * @returns {$.Deferred}
+   **/
+  getUserPrivileges: function(userName) {
+    return App.ajax.send({
+      name: 'router.user.privileges',
+      sender: this,
+      data: {
+        userName: userName
+      },
+      success: 'getUserPrivilegesSuccess'
+    });
+  },
+
+  getUserPrivilegesSuccess: function() {},
+
+  setUserLoggedIn: function(userName) {
+    var controller = this.get('loginController'),
+        self = this;
+    this.setAuthenticated(true);
+    this.setLoginName(userName);
+    this.setUser(App.User.find().findProperty('id', userName));
+    this.getSection(function(route){
+      self.transitionTo(route);
+      controller.postLogin(true,true);
+    });
+  },
+
   login: function () {
     var controller = this.get('loginController');
     var loginName = controller.get('loginName').toLowerCase();
@@ -191,33 +222,30 @@ App.Router = Em.Router.extend({
 
   loginSuccessCallback: function(data, opt, params) {
     console.log('login success');
-    var d = data;
-    var isAdmin = data.Users.roles.indexOf('admin') >= 0;
+    var isAdmin = false;
     var self = this;
-    if (isAdmin) {
-      App.set('isAdmin', true);
-      var controller = this.get('loginController');
-      this.setAuthenticated(true);
-      this.setLoginName(params.loginName);
+    this.getUserPrivileges(data.Users.user_name).done(function(privileges) {
+      data.privileges = privileges;
       App.usersMapper.map({"items": [data]});
-      this.setUser(App.User.find().findProperty('id', params.loginName));
-      this.getSection(function(route){
-        self.transitionTo(route);
-        controller.postLogin(true,true);
-      });
-    }
-    else {
-      App.ajax.send({
-        name: 'router.login2',
-        sender: this,
-        data: {
-          loginName: params.loginName,
-          loginData: data
-        },
-        success: 'login2SuccessCallback',
-        error: 'login2ErrorCallback'
-      });
-    }
+      isAdmin = App.usersMapper.isAdmin(privileges.items.mapProperty('PrivilegeInfo.permission_name'));
+      if (isAdmin) {
+        App.set('isAdmin', true);
+        self.setUserLoggedIn(params.loginName);
+        return true;
+      }
+      else {
+        return App.ajax.send({
+          name: 'router.login2',
+          sender: self,
+          data: {
+            loginName: params.loginName,
+            loginData: data
+          },
+          success: 'login2SuccessCallback',
+          error: 'login2ErrorCallback'
+        });
+      }
+    });
   },
 
   loginErrorCallback: function(request, ajaxOptions, error, opt) {
@@ -234,16 +262,9 @@ App.Router = Em.Router.extend({
 
   login2SuccessCallback: function (clusterResp, opt, params) {
     var controller = this.get('loginController');
-    var self = this;
     if (clusterResp.items.length) {
-      this.setAuthenticated(true);
-      this.setLoginName(params.loginName);
       App.usersMapper.map({"items": [params.loginData]});
-      this.setUser(App.User.find().findProperty('id', params.loginName));
-      this.getSection(function(route){
-        self.transitionTo(route);
-        controller.postLogin(true,true);
-      });
+      this.setUserLoggedIn(params.loginName);
     }
     else {
       controller.set('errorMessage', Em.I18n.t('router.hadoopClusterNotSetUp'));

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 0de25d4..64a3fcc 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -1332,6 +1332,18 @@ var urls = {
       };
     }
   },
+  'users.all': {
+    real: '/users/?fields=*',
+    mock: '/data/users/users.json'
+  },
+  'users.privileges': {
+    real: '/privileges?fields=*',
+    mock: '/data/users/privileges.json'
+  },
+  'router.user.privileges': {
+    real: '/privileges?PrivilegeInfo/principal_name={userName}&fields=*',
+    mock: '/data/users/privileges_{userName}.json'
+  },
   'router.login2': {
     'real': '/clusters',
     'mock': '/data/clusters/info.json'

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/views/main/admin/user/create.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/user/create.js b/ambari-web/app/views/main/admin/user/create.js
index d6f33d9..12d5607 100644
--- a/ambari-web/app/views/main/admin/user/create.js
+++ b/ambari-web/app/views/main/admin/user/create.js
@@ -46,8 +46,6 @@ App.MainAdminUserCreateView = Em.View.extend({
     var form = this.get("userForm");
     if (!form.isValid())  return false;
 
-    this.identifyRoles(form);
-
     return !!App.ajax.send({
       name: 'admin.user.create',
       sender: this,
@@ -56,8 +54,7 @@ App.MainAdminUserCreateView = Em.View.extend({
         form: form,
         data: {
           Users: {
-            password: form.getField("password").get('value'),
-            roles: form.getField("roles").get('value')
+            password: form.getField("password").get('value')
           }
         }
       },
@@ -67,16 +64,6 @@ App.MainAdminUserCreateView = Em.View.extend({
   },
 
   /**
-   * identify roles of user by admin checkbox
-   * @param form
-   */
-  identifyRoles: function (form) {
-    var roles = (form.getField("admin").get('value') === true) ? 'admin,user' : 'user';
-    form.getField("roles").set("value", roles);
-    return roles;
-  },
-
-  /**
    * Success-callback for create user request
    * @param {object} data
    * @param {object} opts

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/app/views/main/admin/user/edit.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/user/edit.js b/ambari-web/app/views/main/admin/user/edit.js
index 64211ea..738de30 100644
--- a/ambari-web/app/views/main/admin/user/edit.js
+++ b/ambari-web/app/views/main/admin/user/edit.js
@@ -42,9 +42,7 @@ App.MainAdminUserEditView = Em.View.extend({
     var form = this.get("userForm");
     if (!form.isValid()) return false;
 
-    var Users = {
-      roles: this.identifyRoles(form)
-    };
+    var Users = {};
 
     this.setPassword(Users, form);
 
@@ -79,17 +77,6 @@ App.MainAdminUserEditView = Em.View.extend({
   },
 
   /**
-   * identify roles of user by admin checkbox
-   * @param form
-   * @return {String}
-   */
-  identifyRoles: function (form) {
-    var roles = (form.getField("admin").get('value') === true) ? 'admin,user' : 'user';
-    form.getField("roles").set("value", roles);
-    return roles;
-  },
-
-  /**
    * Success callback for edit user request
    * @param {object} data
    * @param {object} opt

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/test/mappers/users_mapper_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mappers/users_mapper_test.js b/ambari-web/test/mappers/users_mapper_test.js
index 6db6f48..20c6bae 100644
--- a/ambari-web/test/mappers/users_mapper_test.js
+++ b/ambari-web/test/mappers/users_mapper_test.js
@@ -26,9 +26,10 @@ describe('App.usersMapper', function () {
 
   describe('#isAdmin', function() {
     var tests = [
-      {i:'user,admin',e:true,m:'has admin role'},
-      {i:'admin,user',e:true,m:'has admin role'},
-      {i:'user',e:false,m:'doesn\'t have admin role'}
+      {i:["AMBARI.ADMIN"],e:true,m:'has admin role'},
+      {i:["CLUSTER.READ", "AMBARI.ADMIN"],e:true,m:'has admin role'},
+      {i:["VIEW.USE"],e:false,m:'doesn\'t have admin role'},
+      {i:["CLUSTER.OPERATOR"],e:true,m:'has admin role'}
     ];
     tests.forEach(function(test) {
       it(test.m, function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/test/views/main/admin/user/create_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/admin/user/create_test.js b/ambari-web/test/views/main/admin/user/create_test.js
index 672e25b..bd5b88c 100644
--- a/ambari-web/test/views/main/admin/user/create_test.js
+++ b/ambari-web/test/views/main/admin/user/create_test.js
@@ -44,40 +44,15 @@ describe('App.MainAdminUserCreateView', function () {
     });
     it('form is valid', function () {
       view.set('userForm.mockIsValid', true);
-      sinon.stub(view, 'identifyRoles', Em.K);
       sinon.stub(App.ajax, 'send', Em.K);
 
       expect(view.create()).to.be.true;
-      expect(view.identifyRoles.calledOnce).to.be.true;
       expect(App.ajax.send.calledOnce).to.be.true;
 
-      view.identifyRoles.restore();
       App.ajax.send.restore();
     });
   });
 
-  describe('#identifyRoles()', function () {
-    var mock = Em.Object.create();
-    var form = Em.Object.create({
-      getField: function () {
-        return mock;
-      }
-    });
-
-    it('admin is false', function () {
-      mock.set('value', false);
-
-      expect(view.identifyRoles(form)).to.equal('user');
-      expect(mock.get('value')).to.equal('user');
-    });
-    it('admin is true', function () {
-      mock.set('value', true);
-
-      expect(view.identifyRoles(form)).to.equal('admin,user');
-      expect(mock.get('value')).to.equal('admin,user');
-    });
-  });
-
   describe('#createUserSuccessCallback()', function () {
 
     it('', function () {

http://git-wip-us.apache.org/repos/asf/ambari/blob/efc6eee8/ambari-web/test/views/main/admin/user/edit_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/admin/user/edit_test.js b/ambari-web/test/views/main/admin/user/edit_test.js
index c82fbc1..21cdb2b 100644
--- a/ambari-web/test/views/main/admin/user/edit_test.js
+++ b/ambari-web/test/views/main/admin/user/edit_test.js
@@ -55,16 +55,13 @@ describe('App.MainAdminUserEditView', function () {
       sinon.stub(view.get('userForm'), 'isValid', function () {
         return true;
       });
-      sinon.stub(view, 'identifyRoles', Em.K);
       sinon.stub(view, 'setPassword', Em.K);
 
 
       expect(view.edit()).to.be.true;
       expect(App.ajax.send.calledOnce).to.be.true;
-      expect(view.identifyRoles.calledOnce).to.be.true;
       expect(view.setPassword.calledOnce).to.be.true;
 
-      view.identifyRoles.restore();
       view.setPassword.restore();
     });
   });
@@ -109,28 +106,6 @@ describe('App.MainAdminUserEditView', function () {
     });
   });
 
-  describe('#identifyRoles()', function () {
-    var mock = Em.Object.create();
-    var form = Em.Object.create({
-      getField: function () {
-        return mock;
-      }
-    });
-
-    it('admin is false', function () {
-      mock.set('value', false);
-
-      expect(view.identifyRoles(form)).to.equal('user');
-      expect(mock.get('value')).to.equal('user');
-    });
-    it('admin is true', function () {
-      mock.set('value', true);
-
-      expect(view.identifyRoles(form)).to.equal('admin,user');
-      expect(mock.get('value')).to.equal('admin,user');
-    });
-  });
-
   describe('#editUserSuccessCallback()', function () {
     it('', function () {
       var params = {


[3/6] git commit: AMBARI-6841. Config History: implement UI tweaks.(xiwang)

Posted by jo...@apache.org.
AMBARI-6841. Config History: implement UI tweaks.(xiwang)


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

Branch: refs/heads/branch-alerts-dev
Commit: 2a617da83e2483f3733b57cf2600df34784888a6
Parents: efc6eee
Author: Xi Wang <xi...@apache.org>
Authored: Tue Aug 12 18:51:36 2014 -0700
Committer: Xi Wang <xi...@apache.org>
Committed: Wed Aug 13 11:19:11 2014 -0700

----------------------------------------------------------------------
 .../main/dashboard/config_history_controller.js |   1 +
 ambari-web/app/messages.js                      |   6 +-
 ambari-web/app/models/service_config_version.js |   6 +-
 ambari-web/app/styles/application.less          | 126 +++++++++----------
 .../common/configs/compare_property.hbs         |   6 +-
 .../common/configs/config_history_flow.hbs      |  48 +++----
 .../templates/main/dashboard/config_history.hbs |  15 ++-
 .../views/common/configs/config_history_flow.js |   4 +-
 .../views/main/dashboard/config_history_view.js |   1 -
 9 files changed, 108 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/controllers/main/dashboard/config_history_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/dashboard/config_history_controller.js b/ambari-web/app/controllers/main/dashboard/config_history_controller.js
index 936634a..aeb3fbc 100644
--- a/ambari-web/app/controllers/main/dashboard/config_history_controller.js
+++ b/ambari-web/app/controllers/main/dashboard/config_history_controller.js
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+var App = require('app');
 var customDatePopup = require('/views/common/custom_date_popup');
 
 App.MainConfigHistoryController = Em.ArrayController.extend(App.TableServerMixin, {

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index eb2d0d2..75ef3fc 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1974,16 +1974,18 @@ Em.I18n.translations = {
   'dashboard.services.zookeeper.server' : 'ZooKeeper Server',
 
   'dashboard.configHistory.title': 'Configs',
-  'dashboard.configHistory.table.version.title' : 'Service: version',
+  'dashboard.configHistory.table.version.title' : 'Service',
   'dashboard.configHistory.table.modified.title' : 'Modified',
   'dashboard.configHistory.table.empty' : 'No history to display',
+  'dashboard.configHistory.table.version.versionText' : 'V{0}',
   'dashboard.configHistory.table.filteredHostsInfo': '{0} of {1} versions showing',
   'dashboard.configHistory.info-bar.authoredOn': 'authored on',
   'dashboard.configHistory.info-bar.changesToHandle': 'Changes to handle',
   'dashboard.configHistory.info-bar.showMore': 'Show more',
   'dashboard.configHistory.info-bar.save.popup.title': 'Save Configuration',
   'dashboard.configHistory.info-bar.save.popup.placeholder': 'What did you change?',
-  'dashboard.configHistory.info-bar.revert.button': 'Revert to this version',
+  'dashboard.configHistory.info-bar.revert.button': 'Make current',
+
 
   'timeRange.presets.1hour':'1h',
   'timeRange.presets.12hour':'12h',

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/models/service_config_version.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/service_config_version.js b/ambari-web/app/models/service_config_version.js
index f11859c..4ff3057 100644
--- a/ambari-web/app/models/service_config_version.js
+++ b/ambari-web/app/models/service_config_version.js
@@ -35,9 +35,9 @@ App.ServiceConfigVersion = DS.Model.extend({
     var length = this.get('isCurrent') ? 20 : 40;
     return (typeof this.get('notes') === 'string') ? this.get('notes').slice(0, length) : "";
   }.property('notes', 'isCurrent'),
-  serviceVersion: function () {
-    return this.get('serviceName') + ': ' + this.get('version');
-  }.property('serviceName', 'version'),
+  versionText: function () {
+    return Em.I18n.t('dashboard.configHistory.table.version.versionText').format(this.get('version'));
+  }.property('version'),
   modifiedDate: function () {
     return dateUtil.dateFormat(this.get('appliedTime'));
   }.property('createTime'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index bab0daa..5479c1d 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -4913,67 +4913,41 @@ ul.inline li {
 }
 
 #config_history_flow {
-  margin-bottom: 100px;
+  margin-bottom: 80px;
   .version-slider {
     width: 100%;
     height: 100px;
     margin: 10px 0;
     .flow-element {
-      width: 20%;
+      width: 18%;
       height: 100%;
       .box {
         position: relative;
-        width: 73%;
-        height: 100%;
-        background-color: #f1f1f1;
-        border: 1px solid #000000;
-        font-size: 0.9em;
-        .top-right-label {
-          font-size: @default-font-size;
-          background-color: white;
-          position: absolute;
-          top: 0;
-          left: 0;
+        width: 72%;
+        height: 90%;
+        background-color: #ffffff;
+        border: 1px solid #dddddd;
+        font-size: @default-font-size;
+        .top-label {
           min-width: 20px;
-          border: solid #000000;
-          border-width: 0 1px 1px 0;
           padding: 5px;
-          text-align: center;
-        }
-        .date {
-          margin-left: 35px;
-          line-height: 30px;
-          white-space: nowrap;
         }
         .content {
           padding: 0 5px;
-          line-height: 16px;
-        }
-        .current-label {
-          position: absolute;
-          border-top: 1px solid black;
-          bottom: 0;
-          left: 0;
-          width: 100%;
           text-align: center;
-          font-weight: bold;
-          border-radius: 0;
-          padding: 0;
-          font-size: 14px;
-          line-height: 20px;
+          color: #555555;
         }
       }
       .displayed {
-        background-color: #dcdcdc;
+        background-color: #eeeeee;
+        border: 1px solid #dddddd;
       }
       .arrow-box {
-        width: 25%;
-        height: 100%;
+        width: 20%;
         margin-left: 1px;
-        .big-arrow-right {
-          height: 100%;
-          background-size: 100% 25%;
-          background-position: 50% center;
+        margin-top: 20px;
+        .icon-arrow-right {
+          color: #c3c3c3;
         }
       }
     }
@@ -4984,45 +4958,50 @@ ul.inline li {
       .box {
         width: 100%;
       }
-      width: 15%;
+      width: 13%;
     }
 
-    .arrow-right {
-      margin: 20px -15px 20px 10px;
-      border-top: 30px solid transparent;
-      border-bottom: 30px solid transparent;
-    }
-    .arrow-left {
-      margin: 20px 10px 20px -15px;
-      border-top: 30px solid transparent;
-      border-bottom: 30px solid transparent;
-    }
-    .visibleArrow {
-      border-right-color: black;
-      border-left-color: black;
+    .icon-chevron-box {
+      margin-top: 25px;
+      width: 5%;
+      .icon-chevron-right,
+      .icon-chevron-left{
+       color: #c3c3c3;
+      }
+      .icon-chevron-left:hover,
+      .icon-chevron-right:hover{
+        color: #808080;
+      }
     }
   }
   .version-info-bar {
-    height: 60px;
-    border: 2px solid #a9a9a9;
-    background-color: #dcdcdc;
+    background-image: -moz-linear-gradient(top, @top-nav-bg-color-from, @top-nav-bg-color-to);
+    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@top-nav-bg-color-from), to(@top-nav-bg-color-to));
+    background-image: -webkit-linear-gradient(top, @top-nav-bg-color-from, @top-nav-bg-color-to);
+    background-image: -o-linear-gradient(top, @top-nav-bg-color-from, @top-nav-bg-color-to);
+    background-image: linear-gradient(to bottom, @top-nav-bg-color-from, @top-nav-bg-color-to);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr= @top-nav-bg-color-from, endColorstr=@top-nav-bg-color-to); //for IE9-
+    -webkit-box-shadow: inset 0 0 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+    -moz-box-shadow: inset 0 0 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+    box-shadow: inset 0 0 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
     margin: 5px 0;
     padding: 5px;
     position: fixed;
-    width: 52%;
     z-index: 2;
-    min-width: 743px;
+    width: 747px;
 
     .label-wrapper {
       line-height: 30px;
       margin-left: 10px;
+      color: #d3d3d3;
       .label {
         font-size: 14px;
         padding: 5px;
       }
     }
     .dropdown-menu {
-      min-width: 660px;
+      min-width: 400px;
+      margin-top: 4px;
       li {
         line-height: 30px;
       }
@@ -6038,11 +6017,26 @@ i.icon-asterisks {
     }
   }
 }
-
-
-
-
-
+  #config_history_flow {
+    .version-slider {
+      .flow-element {
+        .box {
+          width: 75%;
+        }
+        .arrow-box {
+          margin-left: 5px;
+        }
+      }
+      .first {
+        .box {
+          width: 100%;
+        }
+      }
+    }
+    .version-info-bar {
+      width: 960px;
+    }
+  }
   .summary-metric-graphs {
     [class*="span"] {
       float: left;

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/templates/common/configs/compare_property.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/configs/compare_property.hbs b/ambari-web/app/templates/common/configs/compare_property.hbs
index 6e1a691..f2b2a45 100644
--- a/ambari-web/app/templates/common/configs/compare_property.hbs
+++ b/ambari-web/app/templates/common/configs/compare_property.hbs
@@ -19,10 +19,6 @@
 <div {{bindAttr class=":control-group :overrideField"}}>
   {{view view.serviceConfigProperty.compareConfig.viewClass serviceConfigBinding="view.serviceConfigProperty.compareConfig"}}
   {{#unless view.serviceConfigProperty.compareConfig.isMock}}
-    <span>
-      <strong><i class="icon-asterisks">&#10037;</i>{{view.serviceConfigProperty.compareConfig.serviceVersion.author}}</strong>&nbsp;
-        {{t dashboard.configHistory.info-bar.authoredOn}}&nbsp;
-      <strong>{{view.serviceConfigProperty.compareConfig.serviceVersion.modifiedDate}}</strong>
-    </span>
+    <span class="label label-info">{{view.serviceConfigProperty.compareConfig.serviceVersion.versionText}}</span>
   {{/unless}}
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/templates/common/configs/config_history_flow.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/configs/config_history_flow.hbs b/ambari-web/app/templates/common/configs/config_history_flow.hbs
index 70acde3..7f80c7a 100644
--- a/ambari-web/app/templates/common/configs/config_history_flow.hbs
+++ b/ambari-web/app/templates/common/configs/config_history_flow.hbs
@@ -19,24 +19,26 @@
 
 <div id="config_history_flow">
   <div class="version-slider">
-      <div {{bindAttr class=":pull-left :arrow-left :visibleArrow view.showLeftArrow::hide"}} {{action shiftBack target="view"}}></div>
-      {{#each serviceVersion in view.visibleServiceVersion}}
-          <div {{bindAttr class=":flow-element :pull-left serviceVersion.first:first"}}>
-              <div class="arrow-box pull-left"><div class="big-arrow-right"></div></div>
-              <div {{bindAttr class=":box :pull-right serviceVersion.isDisplayed:displayed"}}>
-                  <div class="top-right-label">{{serviceVersion.version}}</div>
-                  <p class="date">{{serviceVersion.shortModifiedDate}}</p>
-                  <p class="content">{{serviceVersion.author}}:&nbsp;{{serviceVersion.briefNotes}}</p>
-                  {{#if serviceVersion.isCurrent}}
-                    <div class="current-label label label-success">
-                      {{t common.latest}}
-                      <i {{bindAttr class=":icon-refresh :restart-required-service view.displayedServiceVersion.isRestartRequired::hidden"}}></i>
-                    </div>
-                  {{/if}}
-              </div>
+    <div {{bindAttr class=":icon-chevron-box :pull-left view.showLeftArrow::hide"}} {{action shiftBack target="view"}}><i class="icon-chevron-left icon-3x"></i></div>
+    {{#each serviceVersion in view.visibleServiceVersion}}
+      <div {{bindAttr class=":flow-element :pull-left serviceVersion.first:first"}}>
+        <div class="arrow-box pull-left"><i class="icon-arrow-right icon-3x"></i></div>
+        <div {{bindAttr class=":box :pull-right serviceVersion.isDisplayed:displayed"}}>
+          <div class="top-label">
+            <span class="label label-info">{{serviceVersion.versionText}}</span>
+            {{#if serviceVersion.isCurrent}}
+              <span class="label label-success">
+                {{t common.current}}
+                <i {{bindAttr class=":icon-refresh :restart-required-service view.displayedServiceVersion.isRestartRequired::hidden"}}></i>
+              </span>
+            {{/if}}
           </div>
-      {{/each}}
-      <div {{bindAttr class=":arrow-right :visibleArrow view.showRightArrow::hide"}} {{action shiftForward target="view"}}></div>
+          <div class="content">{{serviceVersion.author}}</div>
+          <div class="content">{{serviceVersion.shortModifiedDate}}</div>
+        </div>
+      </div>
+    {{/each}}
+    <div {{bindAttr class=":icon-chevron-box :pull-right view.showRightArrow::hide"}} {{action shiftForward target="view"}}><i class="icon-chevron-right icon-3x"></i></div>
   </div>
   <div class="version-info-bar">
     <div class="row-fluid">
@@ -49,10 +51,9 @@
               {{#each serviceVersion in view.dropDownList}}
                   <li class="pointer dropdown-submenu">
                       <div class="row-fluid">
-                          <div class="span2">{{t common.version}}:&nbsp;{{serviceVersion.version}}</div>
-                          <div class="span3">{{serviceVersion.modifiedDate}}</div>
-                          <div class="span2">{{serviceVersion.author}}</div>
-                          <div class="span4"><span class="ellipsis">{{t dashboard.configHistory.info-bar.changesToHandle}}</span></div>
+                          <div class="span2">{{serviceVersion.versionText}}</div>
+                          <div class="span6">{{serviceVersion.modifiedDate}}</div>
+                          <div class="span3">{{serviceVersion.author}}</div>
                           <div class="pull-right"><i class="icon-caret-right"></i></div>
                       </div>
                       <ul class="dropdown-menu">
@@ -73,7 +74,10 @@
             </ul>
         </div>
         <div class="label-wrapper span8">
-            <span {{bindAttr class=":label view.displayedServiceVersion.isCurrent:label-success"}}>{{t common.latest}}: {{view.displayedServiceVersion.version}}</span>
+            <span class="label label-info">{{view.displayedServiceVersion.versionText}}</span>
+            {{#if view.displayedServiceVersion.isCurrent}}
+              <span class="label label-success">{{t common.current}}</span>
+            {{/if}}
             <strong>{{view.displayedServiceVersion.author}}</strong>&nbsp;{{t dashboard.configHistory.info-bar.authoredOn}}&nbsp;<strong>{{view.displayedServiceVersion.modifiedDate}}</strong>
         </div>
         <div class="pull-right">

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/templates/main/dashboard/config_history.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/dashboard/config_history.hbs b/ambari-web/app/templates/main/dashboard/config_history.hbs
index 6306078..dc7dd22 100644
--- a/ambari-web/app/templates/main/dashboard/config_history.hbs
+++ b/ambari-web/app/templates/main/dashboard/config_history.hbs
@@ -38,11 +38,16 @@
           {{#if view.pageContent}}
             {{#each item in view.pageContent}}
                 <tr>
-                    <td class="first"><a {{action goToServiceConfigs item.serviceName}}>
-                      {{item.serviceVersion}}
-                      {{#if item.isCurrent}}&nbsp;({{t common.latest}}){{/if}}
-                        <i {{bindAttr class=":icon-refresh :restart-required-service item.isRestartRequired::hidden"}}></i>
-                    </a></td>
+                    <td class="first">
+                      <a {{action goToServiceConfigs item.serviceName}}>
+                      {{item.serviceName}}
+                      </a>
+                      <span class="label label-info">{{item.versionText}}</span>
+                      {{#if item.isCurrent}}
+                        <span class="label label-success">{{t common.current}}</span>
+                      {{/if}}
+                      <i {{bindAttr class=":icon-refresh :restart-required-service item.isRestartRequired::hidden"}}></i>
+                    </td>
                     <td>{{item.modifiedDate}}</td>
                     <td>{{item.author}}</td>
                     <td>{{item.notes}}</td>

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/views/common/configs/config_history_flow.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/configs/config_history_flow.js b/ambari-web/app/views/common/configs/config_history_flow.js
index 9521eab..2b305b5 100644
--- a/ambari-web/app/views/common/configs/config_history_flow.js
+++ b/ambari-web/app/views/common/configs/config_history_flow.js
@@ -101,7 +101,9 @@ App.ConfigHistoryFlowView = Em.View.extend({
     var startIndex = 0;
 
     serviceVersions.setEach('isDisplayed', false);
-    serviceVersions.findProperty('isCurrent').set('isDisplayed', true);
+    if (serviceVersions.findProperty('isCurrent')) {
+      serviceVersions.findProperty('isCurrent').set('isDisplayed', true);
+    }
 
     if (serviceVersions.length > 0) {
       if (serviceVersions.length > this.VERSIONS_IN_FLOW) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/2a617da8/ambari-web/app/views/main/dashboard/config_history_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/dashboard/config_history_view.js b/ambari-web/app/views/main/dashboard/config_history_view.js
index 1c6105b..8c68477 100644
--- a/ambari-web/app/views/main/dashboard/config_history_view.js
+++ b/ambari-web/app/views/main/dashboard/config_history_view.js
@@ -151,7 +151,6 @@ App.MainConfigHistoryView = App.TableView.extend({
 
   updateFilter: function (iColumn, value, type) {
     var self = this;
-
     this.set('controller.resetStartIndex', false);
     this.saveFilterConditions(iColumn, value, type, false);
     if (!this.get('filteringComplete')) {