You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ak...@apache.org on 2013/11/12 19:45:45 UTC

git commit: AMBARI-3751. Load config_groups from API when in config pages. (akovalenko)

Updated Branches:
  refs/heads/trunk 8372bbff4 -> 6f7d033d7


AMBARI-3751. Load config_groups from API when in config pages. (akovalenko)


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

Branch: refs/heads/trunk
Commit: 6f7d033d7ce6446d5e2c8c58128ca8b639fb7019
Parents: 8372bbf
Author: Aleksandr Kovalenko <ol...@ukr.net>
Authored: Tue Nov 12 20:34:18 2013 +0200
Committer: Aleksandr Kovalenko <ol...@ukr.net>
Committed: Tue Nov 12 20:34:18 2013 +0200

----------------------------------------------------------------------
 .../assets/data/clusters/tags_and_groups.json   |  95 ++++++++++++
 .../controllers/main/host/configs_service.js    |  28 ++--
 .../controllers/main/service/info/configs.js    | 152 ++++++++++++-------
 ambari-web/app/messages.js                      |   1 +
 ambari-web/app/models/service_config.js         |   6 +-
 .../common/configs/overriddenProperty.hbs       |   6 +-
 .../templates/common/configs/service_config.hbs |  11 +-
 ambari-web/app/utils/ajax.js                    |   4 +
 ambari-web/app/utils/config.js                  |  21 ++-
 .../common/configs/overriddenProperty_view.js   |  32 +---
 10 files changed, 231 insertions(+), 125 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/assets/data/clusters/tags_and_groups.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/clusters/tags_and_groups.json b/ambari-web/app/assets/data/clusters/tags_and_groups.json
new file mode 100644
index 0000000..606d07b
--- /dev/null
+++ b/ambari-web/app/assets/data/clusters/tags_and_groups.json
@@ -0,0 +1,95 @@
+{
+  "href" : "http://ec2-23-20-184-220.compute-1.amazonaws.com:8080/api/v1/clusters/tdk?fields=Clusters",
+  "Clusters" : {
+    "cluster_name" : "tdk",
+    "cluster_id" : 1,
+    "version" : "HDP-1.3.0",
+    "desired_configs" : {
+      "mapred-site" : {
+        "tag" : "version1"
+      },
+      "hbase-site" : {
+        "tag" : "version1"
+      },
+      "global" : {
+        "tag" : "version1"
+      },
+      "hdfs-site" : {
+        "tag" : "version1"
+      },
+      "mapred-queue-acls" : {
+        "tag" : "version1"
+      },
+      "webhcat-site" : {
+        "tag" : "version1"
+      },
+      "oozie-site" : {
+        "tag" : "version1"
+      },
+      "hive-site" : {
+        "tag" : "version1"
+      },
+      "hue-site" : {
+        "tag" : "version1"
+      },
+      "capacity-scheduler" : {
+        "tag" : "version1"
+      },
+      "core-site" : {
+        "tag" : "version1"
+      }
+    }
+  },
+  "config_groups" : [
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/config_groups/10",
+      "ConfigGroup" : {
+        "cluster_name" : "mycluster",
+        "description" : "test config group",
+        "desired_configs" : [
+          {
+            "tag" : "test1",
+            "type" : "mapred-site",
+            "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/configurations?type=mapred-site&tag=test_hdfs"
+          }
+        ],
+        "group_name" : "HDFS test group",
+        "hosts" : [
+          {
+            "host_name" : "c6401.ambari.apache.org",
+            "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/hosts/c6401.ambari.apache.org"
+          },
+          {
+            "host_name" : "c6402.ambari.apache.org",
+            "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/hosts/c6402.ambari.apache.org"
+          }
+        ],
+        "id" : 10,
+        "tag" : "HDFS"
+      }
+    },
+    {
+      "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/config_groups/11",
+      "ConfigGroup" : {
+        "cluster_name" : "mycluster",
+        "description" : "test config group",
+        "desired_configs" : [
+          {
+            "tag" : "test_hbase",
+            "type" : "hbase-site",
+            "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/configurations?type=hbase-site&tag=test_hbase"
+          }
+        ],
+        "group_name" : "HBASE test group",
+        "hosts" : [
+          {
+            "host_name" : "c6402.ambari.apache.org",
+            "href" : "http://c6401.ambari.apache.org:8080/api/v1/clusters/mycluster/hosts/c6402.ambari.apache.org"
+          }
+        ],
+        "id" : 11,
+        "tag" : "HBASE"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/controllers/main/host/configs_service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/configs_service.js b/ambari-web/app/controllers/main/host/configs_service.js
index 76935a1..3b1414a 100644
--- a/ambari-web/app/controllers/main/host/configs_service.js
+++ b/ambari-web/app/controllers/main/host/configs_service.js
@@ -20,6 +20,7 @@ var App = require('app');
 App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.extend({
   name: 'mainHostServiceConfigsController',
   host: null,
+  isHostsConfigsPage: true,
   /**
    * On load function
    */
@@ -69,25 +70,26 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
    * This method will *not load* the overridden properties. However it will
    * replace the value shown for properties which this host has override for.
    */
-  loadServiceConfigHostsOverrides: function (serviceConfigs, loadedHostToOverrideSiteToTagMap) {
+  loadServiceConfigHostsOverrides: function (serviceConfigs, loadedGroupToOverrideSiteToTagMap, configGroups) {
     var thisHostName = this.get('host.hostName');
     var configKeyToConfigMap = {};
     serviceConfigs.forEach(function (item) {
       configKeyToConfigMap[item.name] = item;
     });
-    var typeTagToHostMap = {};
+    var typeTagToGroupMap = {};
     var urlParams = [];
-    for ( var hostname in loadedHostToOverrideSiteToTagMap) {
-      if (hostname === thisHostName) {
-        var overrideTypeTags = loadedHostToOverrideSiteToTagMap[hostname];
-        for ( var type in overrideTypeTags) {
+    for (var group in loadedGroupToOverrideSiteToTagMap) {
+      var groupObj = configGroups.findProperty('name', group);
+      if (groupObj.get('hosts').contains(thisHostName)) {
+        var overrideTypeTags = loadedGroupToOverrideSiteToTagMap[group];
+        for (var type in overrideTypeTags) {
           var tag = overrideTypeTags[type];
-          typeTagToHostMap[type + "///" + tag] = hostname;
+          typeTagToGroupMap[type + "///" + tag] = groupObj;
           urlParams.push('(type=' + type + '&tag=' + tag + ')');
         }
       }
     }
-    this.set('typeTagToHostMap', typeTagToHostMap);
+    this.set('typeTagToGroupMap', typeTagToGroupMap);
     this.set('configKeyToConfigMap', configKeyToConfigMap);
     if (urlParams.length > 0) {
       App.ajax.send({
@@ -102,10 +104,10 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
     }
   },
   loadServiceConfigHostsOverridesSuccessCallback: function (data) {
-    var typeTagToHostMap = this.get('typeTagToHostMap');
+    var typeTagToGroupMap = this.get('typeTagToGroupMap');
     var configKeyToConfigMap = this.get('configKeyToConfigMap');
     data.items.forEach(function (config) {
-      var hostname = typeTagToHostMap[config.type + "///" + config.tag];
+      var group = typeTagToGroupMap[config.type + "///" + config.tag];
       var properties = config.properties;
       for ( var prop in properties) {
         var serviceConfig = configKeyToConfigMap[prop];
@@ -126,11 +128,11 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
         }
         if (serviceConfig) {
           // Value of this property is different for this host.
-          console.log("loadServiceConfigHostsOverrides(" + hostname + "): [" + hostname + "] OVERRODE(" + serviceConfig.name + "): " + serviceConfig.value + " -> " + hostOverrideValue);
+          console.log("loadServiceConfigHostsOverrides(" + group + "): [" + group + "] OVERRODE(" + serviceConfig.name + "): " + serviceConfig.value + " -> " + hostOverrideValue);
           serviceConfig.value = hostOverrideValue;
           serviceConfig.defaultValue = hostOverrideValue;
           serviceConfig.isOriginalSCP = false;
-          serviceConfig.selectedHostOptions = [hostname];
+          serviceConfig.group = group;
         }
       }
     });
@@ -139,4 +141,4 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
   loadServiceConfigHostsOverridesErrorCallback: function (request, ajaxOptions, error) {
     console.log("TRACE: error code status is: " + request.status);
   }
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/controllers/main/service/info/configs.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js
index 29f6a94..abea470 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -21,6 +21,7 @@ require('controllers/wizard/slave_component_groups_controller');
 
 App.MainServiceInfoConfigsController = Em.Controller.extend({
   name: 'mainServiceInfoConfigsController',
+  isHostsConfigsPage: false,
   dataIsLoaded: false,
   stepConfigs: [], //contains all field properties that are viewed in this service
   selectedService: null,
@@ -69,7 +70,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
    *
    * @see savedHostToOverrideSiteToTagMap
    */
-  loadedHostToOverrideSiteToTagMap: {},
+  loadedGroupToOverrideSiteToTagMap: {},
 
   /**
    * During page load time the cluster level site to tag
@@ -149,7 +150,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     this.get('globalConfigs').clear();
     this.get('uiConfigs').clear();
     this.get('customConfig').clear();
-    this.set('loadedHostToOverrideSiteToTagMap', {});
+    this.set('loadedGroupToOverrideSiteToTagMap', {});
     this.set('savedHostToOverrideSiteToTagMap', {});
     this.set('savedSiteNameToServerServiceConfigDataMap', {});
     if (this.get('serviceConfigTags')) {
@@ -242,7 +243,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
    */
   loadServiceConfigs: function () {
     App.ajax.send({
-      name: 'config.tags',
+      name: 'config.tags_and_groups',
       sender: this,
       data: {
         serviceName: this.get('content.serviceName'),
@@ -256,53 +257,84 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     var serviceConfigsDef = params.serviceConfigsDef;
     var serviceName = this.get('content.serviceName');
     console.debug("loadServiceConfigs(): data=", data);
-    var allConfigGroups = [];
     // Create default configuration group
+    var defaultConfigGroupHosts = App.Host.find().mapProperty('hostName');
+    var selectedConfigGroup;
+    var siteToTagMap = {};
+    for (var site in data.Clusters.desired_configs) {
+      if (serviceConfigsDef.sites.indexOf(site) > -1) {
+        siteToTagMap[site] = data.Clusters.desired_configs[site].tag;
+      }
+    }
+    this.loadedClusterSiteToTagMap = siteToTagMap;
+    //parse loaded config groups
+    if (App.supports.hostOverrides) {
+      var configGroups = [];
+      if (data.config_groups.length) {
+        data.config_groups.forEach(function (item) {
+          item = item.ConfigGroup;
+          var groupHosts = item.hosts.mapProperty('host_name');
+          var newConfigGroup = App.ConfigGroup.create({
+            name: item.group_name,
+            description: item.description,
+            isDefault: false,
+            parentConfigGroup: null,
+            service: App.Service.find().findProperty('serviceName', item.tag),
+            hosts: groupHosts,
+            configSiteTags: []
+          });
+          groupHosts.forEach(function (host) {
+            defaultConfigGroupHosts = defaultConfigGroupHosts.without(host);
+          }, this);
+          item.desired_configs.forEach(function (config) {
+            newConfigGroup.configSiteTags.push(App.ConfigSiteTag.create({
+              site: config.type,
+              tag: config.tag
+            }));
+          }, this);
+          // select default selected group for hosts page
+          if (!selectedConfigGroup && this.get('isHostsConfigsPage') && newConfigGroup.get('hosts').contains(this.get('host.hostName')) && this.get('content.serviceName') === item.tag) {
+            selectedConfigGroup = newConfigGroup;
+          }
+          configGroups.push(newConfigGroup);
+        }, this);
+      }
+      this.set('configGroups', configGroups);
+    }
     var defaultConfigGroup = App.ConfigGroup.create({
       name: "Default",
       description: "Default cluster level " + serviceName + " configuration",
       isDefault: true,
+      hosts: defaultConfigGroupHosts,
       parentConfigGroup: null,
       service: this.get('content'),
       configSiteTags: []
     });
-    var configSiteTags = defaultConfigGroup.configSiteTags;
-    for (var site in data.Clusters.desired_configs) {
-      if (serviceConfigsDef.sites.indexOf(site) > -1) {
-        configSiteTags.push(App.ConfigSiteTag.create({
-          site: site,
-          tag: data.Clusters.desired_configs[site].tag
-        }));
-      }
+    if (!selectedConfigGroup) {
+      selectedConfigGroup = defaultConfigGroup;
     }
-    
-    allConfigGroups.push(defaultConfigGroup);
-    this.configGroups = allConfigGroups;
-    this.set('selectedConfigGroup', defaultConfigGroup);
+    this.get('configGroups').push(defaultConfigGroup);
+    this.set('selectedConfigGroup', selectedConfigGroup);
   },
-  
-  loadConfigGroup: function () {
+
+  onConfigGroupChange: function () {
+    this.get('stepConfigs').clear();
     var selectedConfigGroup = this.get('selectedConfigGroup');
-    var serviceName = selectedConfigGroup.get('service.serviceName');
-    this.loadedClusterSiteToTagMap = {};
-    //STEP 1: handle tags from JSON data
-    var selectedSiteTags = selectedConfigGroup.get('configSiteTags');
-    selectedSiteTags.forEach( function(siteTag) {
-      var site = siteTag.get('site');
-      var tag = siteTag.get('tag');
-      this.loadedClusterSiteToTagMap[site] = tag;
-//      var overrides = data.Clusters.desired_configs[site].host_overrides;
-//      if (overrides) {
-//        overrides.forEach(function (override) {
-//          var hostname = override.host_name;
-//          var tag = override.tag;
-//          if (!this.loadedHostToOverrideSiteToTagMap[hostname]) {
-//            this.loadedHostToOverrideSiteToTagMap[hostname] = {};
-//          }
-//          this.loadedHostToOverrideSiteToTagMap[hostname][site] = tag;
-//        }, this);
-//      }
-    }, this);
+    var serviceName = this.get('content.serviceName');
+    //STEP 1: handle tags from JSON data for host overrides
+    this.loadedGroupToOverrideSiteToTagMap = {};
+    if (App.supports.hostOverrides) {
+      var configGroupsWithOverrides = selectedConfigGroup.get('isDefault') && !this.get('isHostsConfigsPage') ? this.get('configGroups') : [selectedConfigGroup];
+      configGroupsWithOverrides.forEach(function (item) {
+        var groupName = item.get('name');
+        this.loadedGroupToOverrideSiteToTagMap[groupName] = {};
+        item.get('configSiteTags').forEach(function (siteTag) {
+          var site = siteTag.get('site');
+          var tag = siteTag.get('tag');
+          this.loadedGroupToOverrideSiteToTagMap[groupName][site] = tag;
+        }, this);
+      }, this);
+    }
     //STEP 2: Create an array of objects defining tag names to be polled and new tag names to be set after submit
     this.setServiceConfigTags(this.loadedClusterSiteToTagMap);
     //STEP 3: Load advanced configs from server
@@ -330,7 +362,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
 
     var allConfigs = this.get('globalConfigs').concat(configs);
     //STEP 9: Load and add host override configs
-    this.loadServiceConfigHostsOverrides(allConfigs, this.loadedHostToOverrideSiteToTagMap);
+    this.loadServiceConfigHostsOverrides(allConfigs, this.loadedGroupToOverrideSiteToTagMap, this.get('configGroups'));
     var restartData = this.loadActualConfigsAndCalculateRestarts();
     //STEP 10: creation of serviceConfig object which contains configs for current service
     var serviceConfig = App.config.createServiceConfig(serviceName);
@@ -348,11 +380,11 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     this.checkForSecureConfig(this.get('selectedService'));
     this.set('dataIsLoaded', true);
   }.observes('selectedConfigGroup'),
-  
-  loadServiceConfigHostsOverrides: function(allConfigs, loadedHostToOverrideSiteToTagMap) {
-    App.config.loadServiceConfigHostsOverrides(allConfigs, loadedHostToOverrideSiteToTagMap);
+
+  loadServiceConfigHostsOverrides: function (allConfigs, loadedGroupToOverrideSiteToTagMap, configGroups) {
+    App.config.loadServiceConfigHostsOverrides(allConfigs, loadedGroupToOverrideSiteToTagMap, configGroups);
   },
-  
+
   /**
    * Changes format from Object to Array
    *
@@ -491,7 +523,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     var localDB = this.getInfoForDefaults();
     var recommendedDefaults = {};
     var s = this.get('serviceConfigsData').findProperty('serviceName', this.get('content.serviceName'));
-
+    var defaultGroupSelected = this.get('selectedConfigGroup.isDefault');
     var defaults = [];
     if (s.defaultsProviders) {
       s.defaultsProviders.forEach(function(defaultsProvider) {
@@ -557,25 +589,20 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
           }
         }
 
-        // serviceConfigProperty.serviceConfig = componentConfig;
-        if (App.get('isAdmin')) {
-          serviceConfigProperty.set('isEditable', serviceConfigProperty.get('isReconfigurable'));
-        } else {
-          serviceConfigProperty.set('isEditable', false);
-        }
-
         console.log("config result", serviceConfigProperty);
       } else {
         serviceConfigProperty.set('isVisible', false);
       }
       if (overrides != null) {
         for (var overridenValue in overrides) {
-          var hostsArray = overrides[overridenValue];
           var newSCP = App.ServiceConfigProperty.create(_serviceConfigProperty);
           newSCP.set('value', overridenValue);
           newSCP.set('isOriginalSCP', false); // indicated this is overridden value,
           newSCP.set('parentSCP', serviceConfigProperty);
-          newSCP.set('selectedHostOptions', Ember.A(hostsArray));
+          if (App.supports.hostOverrides && defaultGroupSelected) {
+            newSCP.set('group', overrides[overridenValue]);
+            newSCP.set('isEditable', false);
+          }
           var parentOverridesArray = serviceConfigProperty.get('overrides');
           if (parentOverridesArray == null) {
             parentOverridesArray = Ember.A([]);
@@ -585,6 +612,16 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
           console.debug("createOverrideProperty(): Added:", newSCP, " to main-property:", serviceConfigProperty)
         }
       }
+      // serviceConfigProperty.serviceConfig = componentConfig;
+      if (App.supports.hostOverrides) {
+        serviceConfigProperty.set('isEditable', defaultGroupSelected && !this.get('isHostsConfigsPage'));
+      } else {
+        if (App.get('isAdmin')) {
+          serviceConfigProperty.set('isEditable', serviceConfigProperty.get('isReconfigurable'));
+        } else {
+          serviceConfigProperty.set('isEditable', false);
+        }
+      }
       componentConfig.configs.pushObject(serviceConfigProperty);
       serviceConfigProperty.validate();
     }, this);
@@ -839,7 +876,6 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
     if (!result.flag) {
       result.message = Em.I18n.t('services.service.config.failSaveConfig');
     } else {
-      result.flag = result.flag && this.doPUTHostOverridesConfigurationSites();
       if (!result.flag) {
         result.message = Em.I18n.t('services.service.config.failSaveConfigHostExceptions');
       }
@@ -1272,8 +1308,8 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
       }
     }
     // Now cleanup removed overrides
-    for (var loadedHost in this.loadedHostToOverrideSiteToTagMap) {
-      for (var loadedSiteName in this.loadedHostToOverrideSiteToTagMap[loadedHost]) {
+    for (var loadedHost in this.loadedGroupToOverrideSiteToTagMap) {
+      for (var loadedSiteName in this.loadedGroupToOverrideSiteToTagMap[loadedHost]) {
         if (!(savedHostSiteArray.contains(loadedHost + "///" + loadedSiteName))) {
           // This host-site combination was loaded, but not saved.
           // Meaning it is not needed anymore. Hence send a DELETE command.
@@ -1285,7 +1321,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
               Hosts: {
                 desired_config: {
                   type: loadedSiteName,
-                  tag: this.loadedHostToOverrideSiteToTagMap[loadedHost][loadedSiteName],
+                  tag: this.loadedGroupToOverrideSiteToTagMap[loadedHost][loadedSiteName],
                   selected: false
                 }
               }
@@ -1646,7 +1682,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
       secondary: null
     });
   },
-  
+
   selectConfigGroup: function (event) {
     this.set('selectedConfigGroup', event.context);
   }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index f92d2b7..8523f86 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -155,6 +155,7 @@ Em.I18n.translations = {
   'common.important': 'Important',
   'common.allServices':'All Services',
   'common.move':'Move',
+  'common.change': 'Change',
 
   'requestInfo.installComponents':'Install Components',
   'requestInfo.installServices':'Install Services',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/models/service_config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/service_config.js b/ambari-web/app/models/service_config.js
index c014e76..a410b7c 100644
--- a/ambari-web/app/models/service_config.js
+++ b/ambari-web/app/models/service_config.js
@@ -154,6 +154,7 @@ App.ServiceConfigProperty = Ember.Object.extend({
   parentSCP: null, // This is the main SCP which is overridden by this. Set only when isOriginalSCP is false.
   selectedHostOptions : null, // contain array of hosts configured with overridden value
   overrides : null,
+  group: null, // Contain group related to this property. Set only when isOriginalSCP is false.
   isUserProperty: null, // This property was added by user. Hence they get removal actions etc.
   isOverridable: true,
   error: false,
@@ -696,11 +697,6 @@ App.ServiceConfigProperty = Ember.Object.extend({
       var isOriginalSCP = this.get('isOriginalSCP');
       var parentSCP = this.get('parentSCP');
       if (!isOriginalSCP) {
-        var hosts = this.get('selectedHostOptions');
-        if(hosts==null || hosts.get('length')<1){
-          this.set('errorMessage', 'Select hosts to apply exception to');
-          isError = true;
-        }
         if (!isError && parentSCP != null) {
           if (value === parentSCP.get('value')) {
             this.set('errorMessage', 'Host exceptions must have different value');

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/templates/common/configs/overriddenProperty.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/configs/overriddenProperty.hbs b/ambari-web/app/templates/common/configs/overriddenProperty.hbs
index c7d9e35..e2b8961 100644
--- a/ambari-web/app/templates/common/configs/overriddenProperty.hbs
+++ b/ambari-web/app/templates/common/configs/overriddenProperty.hbs
@@ -20,8 +20,10 @@
     {{! Here serviceConfigBinding should ideally be serviceConfigPropertyBinding }}
     <div {{bindAttr class="overriddenSCP.errorMessage:error: :control-group :overrideField"}}>
       {{view overriddenSCP.viewClass serviceConfigBinding="overriddenSCP" categoryConfigsBinding="view.categoryConfigs"}}
+      {{#if overriddenSCP.group}}
+        <a class="action" {{action selectConfigGroup overriddenSCP.group target="controller"}}>{{overriddenSCP.group.name}}</a>
+      {{/if}}
       {{#if overriddenSCP.isEditable}}
-	      {{view view.hostsCountView overriddenSCPBinding="overriddenSCP"}}
 	      {{#if isNotDefaultValue}}
 	        <a class="action" {{action "doRestoreDefaultValue" this target="view" }} ><i class="icon-undo"></i>Undo</a>
 	      {{/if}}
@@ -29,4 +31,4 @@
 	      <span class="help-inline">{{overriddenSCP.errorMessage}}</span>
 	    {{/if}}
     </div>
-{{/each}}
\ No newline at end of file
+{{/each}}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/templates/common/configs/service_config.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/configs/service_config.hbs b/ambari-web/app/templates/common/configs/service_config.hbs
index 5199bd4..88392b0 100644
--- a/ambari-web/app/templates/common/configs/service_config.hbs
+++ b/ambari-web/app/templates/common/configs/service_config.hbs
@@ -49,8 +49,8 @@
 	<div class="alert alert-info">
 	  {{t common.group}}&nbsp; 
 	  <span class="btn-group">
-		  <button class="btn">{{selectedConfigGroup.name}}</button>
-		  <button class="btn dropdown-toggle" data-toggle="dropdown">
+		  <button {{bindAttr disabled="controller.isHostsConfigsPage"}} class="btn">{{selectedConfigGroup.name}}</button>
+		  <button {{bindAttr disabled="controller.isHostsConfigsPage"}} class="btn dropdown-toggle" data-toggle="dropdown">
 		    <span class="caret"></span>
 		  </button>
 		  <ul class="dropdown-menu">
@@ -58,12 +58,15 @@
 		    {{#each configGroup in configGroups}}
            <li>
              <a href="#" {{action "selectConfigGroup" configGroup target="controller"}}>
-               {{configGroup.name}}
+               {{configGroup.name}} ({{configGroup.hosts.length}})
              </a>
            </li>
         {{/each}}
 		  </ul>
 		</span>
+    {{#if controller.isHostsConfigsPage}}
+      &nbsp;<a href="">{{t common.change}}</a>
+    {{/if}}
      <div class="pull-right">
        {{view App.FilterComboboxView filterBinding="view.filter" columnsBinding="view.columns" }}
      </div>
@@ -151,4 +154,4 @@
     {{/view}}
     {{/if}}
   {{/each}}
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index 959c65a..f2c47bd 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -197,6 +197,10 @@ var urls = {
     'real': '/clusters/{clusterName}?fields=Clusters/desired_configs',
     'mock': '/data/clusters/cluster.json'
   },
+  'config.tags_and_groups': {
+    'real': '/clusters/{clusterName}?fields=Clusters/desired_configs,config_groups/*',
+    'mock': '/data/clusters/tags_and_groups.json'
+  },
   'config.tags.sync': {
     'real': '/clusters/{clusterName}?fields=Clusters/desired_configs',
     'mock': '/data/clusters/cluster.json',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/utils/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js
index 3008a10..7d35b77 100644
--- a/ambari-web/app/utils/config.js
+++ b/ambari-web/app/utils/config.js
@@ -723,18 +723,18 @@ App.config = Em.Object.create({
    *
    *
    */
-  loadServiceConfigHostsOverrides: function (serviceConfigs, loadedHostToOverrideSiteToTagMap) {
+  loadServiceConfigHostsOverrides: function (serviceConfigs, loadedGroupToOverrideSiteToTagMap, configGroups) {
     var configKeyToConfigMap = {};
     serviceConfigs.forEach(function (item) {
       configKeyToConfigMap[item.name] = item;
     });
-    var typeTagToHostMap = {};
+    var typeTagToGroupMap = {};
     var urlParams = [];
-    for (var hostname in loadedHostToOverrideSiteToTagMap) {
-      var overrideTypeTags = loadedHostToOverrideSiteToTagMap[hostname];
+    for (var group in loadedGroupToOverrideSiteToTagMap) {
+      var overrideTypeTags = loadedGroupToOverrideSiteToTagMap[group];
       for (var type in overrideTypeTags) {
         var tag = overrideTypeTags[type];
-        typeTagToHostMap[type + "///" + tag] = hostname;
+        typeTagToGroupMap[type + "///" + tag] = configGroups.findProperty('name', group);
         urlParams.push('(type=' + type + '&tag=' + tag + ')');
       }
     }
@@ -746,7 +746,7 @@ App.config = Em.Object.create({
         data: {
           params: params,
           configKeyToConfigMap: configKeyToConfigMap,
-          typeTagToHostMap: typeTagToHostMap
+          typeTagToGroupMap: typeTagToGroupMap
         },
         success: 'loadServiceConfigHostsOverridesSuccess'
       });
@@ -756,7 +756,7 @@ App.config = Em.Object.create({
     console.debug("loadServiceConfigHostsOverrides: Data=", data);
     data.items.forEach(function (config) {
       App.config.loadedConfigurationsCache[config.type + "_" + config.tag] = config.properties;
-      var hostname = params.typeTagToHostMap[config.type + "///" + config.tag];
+      var group = params.typeTagToGroupMap[config.type + "///" + config.tag];
       var properties = config.properties;
       for (var prop in properties) {
         var serviceConfig = params.configKeyToConfigMap[prop];
@@ -781,11 +781,8 @@ App.config = Em.Object.create({
           if (!(overrides in serviceConfig)) {
             serviceConfig.overrides = {};
           }
-          if (!(hostOverrideValue in serviceConfig.overrides)) {
-            serviceConfig.overrides[hostOverrideValue] = [];
-          }
-          console.log("loadServiceConfigHostsOverrides(): [" + hostname + "] OVERRODE(" + serviceConfig.name + "): " + serviceConfig.value + " -> " + hostOverrideValue);
-          serviceConfig.overrides[hostOverrideValue].push(hostname);
+          console.log("loadServiceConfigHostsOverrides(): [" + group + "] OVERRODE(" + serviceConfig.name + "): " + serviceConfig.value + " -> " + hostOverrideValue);
+          serviceConfig.overrides[hostOverrideValue] = group;
         }
       }
     });

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/6f7d033d/ambari-web/app/views/common/configs/overriddenProperty_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/configs/overriddenProperty_view.js b/ambari-web/app/views/common/configs/overriddenProperty_view.js
index 674123e..c772033 100644
--- a/ambari-web/app/views/common/configs/overriddenProperty_view.js
+++ b/ambari-web/app/views/common/configs/overriddenProperty_view.js
@@ -36,35 +36,5 @@ App.ServiceConfigView.SCPOverriddenRowsView = Ember.View.extend({
     var overrides = this.get('serviceConfigProperty.overrides');
     overrides = overrides.without(scpToBeRemoved);
     this.set('serviceConfigProperty.overrides', overrides);
-  },
-  
-  hostsCountView: Em.View.extend({
-    classNames: ['overridden-hosts-view'],
-    template: Ember.Handlebars.compile("<a class=\"action overriden-hosts-link\" href=\"#\" {{action showOverrideWindow overriddenSCP controller target=\"view.parentView\" }} rel=\"tooltip\" {{bindAttr data-original-title=\"view.hostsList\"}} >{{view.overriddenSCP.selectedHostOptions.length}} hosts </a>"),
-    overriddenSCP: null,
-    didInsertElement: function () {
-      var links = $(".overriden-hosts-link");
-      console.log(links);
-      links.tooltip({html:true, placement:"right"});
-    },
-    /**
-     * New line separated list of hosts
-     * @type String
-     */
-    hostsList: function () {
-      var tooltip = "<ul>";
-      var hosts = this.get('overriddenSCP.selectedHostOptions');
-      if (hosts != null) {
-        hosts.forEach(function (host) {
-          var hostObj = App.Host.find(host);
-          if (hostObj != null) {
-            host = hostObj.get('publicHostName');
-          }
-          tooltip += ("<li>" + host + "</li>");
-        });
-      }
-      tooltip += "</ul>";
-      return tooltip;
-    }.property('overriddenSCP', 'overriddenSCP.selectedHostOptions')
-  })
+  }
 });