You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2016/12/13 13:39:27 UTC

ambari git commit: AMBARI-19189 Add host component should show configs being changed in recommendation popup modal. (ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk 69825b9ff -> e148cb5a2


AMBARI-19189 Add host component should show configs being changed in recommendation popup modal. (ababiichuk)


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

Branch: refs/heads/trunk
Commit: e148cb5a2ab5f1a64b0ba5c8c8ad01d396bd57ac
Parents: 69825b9
Author: ababiichuk <ab...@hortonworks.com>
Authored: Tue Dec 13 15:12:25 2016 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Tue Dec 13 15:12:25 2016 +0200

----------------------------------------------------------------------
 ambari-web/app/controllers/main/host/details.js | 273 ++++++++++++++-----
 ambari-web/app/messages.js                      |   5 +-
 .../modal_popups/dependent_configs_list.hbs     |   7 +-
 .../details/addComponentWithConfigsChanges.hbs  |  27 ++
 .../dependent_configs_list_popup.js             |  54 ++--
 .../test/controllers/main/host/details_test.js  |  35 ++-
 6 files changed, 296 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e148cb5a/ambari-web/app/controllers/main/host/details.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js
index ecb3219..3a5dca5 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -73,6 +73,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
    */
   isOozieServerAddable: true,
 
+  isConfigsLoaded: false,
+
   /**
    * Open dashboard page
    * @method routeHome
@@ -536,6 +538,55 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
     }
   },
 
+  showAddComponentConfirmation: function (componentName, callbackName, primary) {
+    var self = this,
+      componentDisplayName = App.format.role(componentName, false),
+      manualKerberosWarning = App.get('router.mainAdminKerberosController.isManualKerberos') ?
+        Em.I18n.t('hosts.host.manualKerberosWarning') : '',
+      commonMessage = Em.I18n.t('hosts.host.addComponent.msg').format(componentDisplayName),
+      configs = {
+        groups: [],
+        propertiesToChange: []
+      };
+    this.loadConfigs(callbackName, configs);
+    App.ModalPopup.show({
+      header: Em.I18n.t('popup.confirmation.commonHeader'),
+      controller: self,
+      hasPropertiesToChange: false,
+      classNameBindings: ['hasPropertiesToChange:common-modal-wrapper', 'hasPropertiesToChange:modal-full-width'],
+      modalDialogClasses: function () {
+        return this.get('hasPropertiesToChange') ? ['modal-lg'] : [];
+      }.property('hasPropertiesToChange'),
+      primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/host/details/addComponentWithConfigsChanges'),
+        commonMessage: commonMessage,
+        manualKerberosWarning: manualKerberosWarning,
+        propertiesToChange: configs.propertiesToChange,
+        setPopupSize: function () {
+          this.set('parentView.hasPropertiesToChange', !!this.get('propertiesToChange.length'));
+        }.observes('propertiesToChange.length')
+      }),
+      disablePrimary: Em.computed.not('controller.isConfigsLoaded'),
+      onPrimary: function () {
+        this._super();
+        configs.propertiesToChange.forEach(function (property) {
+          var value = property.saveRecommended ? property.recommendedValue : property.initialValue,
+            filename = property.propertyFileName;
+          if (configs.groups.length) {
+            var group = configs.groups.find(function (item) {
+              return item.properties.hasOwnProperty(filename);
+            });
+            group.properties[filename][property.propertyName] = value;
+          }
+        });
+        if (primary) {
+          primary.call(self, configs.groups);
+        }
+      }
+    });
+  },
+
   /**
    * add component as <code>addComponent<code> method but perform
    * kdc sessionstate if cluster is secure;
@@ -577,33 +628,34 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
 
     switch (componentName) {
       case 'ZOOKEEPER_SERVER':
-        returnFunc = App.showConfirmationPopup(function () {
-          self.installHostComponentCall(self.get('content.hostName'), component)
-        }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
+        returnFunc = self.showAddComponentConfirmation(componentName, null, function (groups) {
+          this.saveConfigsBatch(groups, componentName, hostName);
+        });
         break;
       case 'HIVE_METASTORE':
-        returnFunc = App.showConfirmationPopup(function () {
-          self.set('hiveMetastoreHost', hostName);
-          self.loadConfigs("loadHiveConfigs");
-        }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
+        self.set('hiveMetastoreHost', hostName);
+        returnFunc = self.showAddComponentConfirmation(componentName, 'loadHiveConfigs', function (groups) {
+          this.saveConfigsBatch(groups, componentName, hostName);
+          this.set('addHiveServer', false);
+        });
         break;
       case 'WEBHCAT_SERVER':
-        returnFunc = App.showConfirmationPopup(function () {
-          self.set('webhcatServerHost', hostName);
-          self.loadConfigs("loadWebHCatConfigs");
-        }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
+        self.set('webhcatServerHost', hostName);
+        returnFunc = self.showAddComponentConfirmation(componentName, 'loadWebHCatConfigs', function (groups) {
+          this.saveConfigsBatch(groups, componentName, hostName);
+        });
         break;
       case 'NIMBUS':
-        returnFunc = App.showConfirmationPopup(function () {
-          self.set('nimbusHost', hostName);
-          self.loadConfigs("loadStormConfigs");
-        }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
+        self.set('nimbusHost', hostName);
+        returnFunc = self.showAddComponentConfirmation(componentName, 'loadStormConfigs', function (groups) {
+          this.saveConfigsBatch(groups, componentName, hostName);
+        });
         break;
       case 'RANGER_KMS_SERVER':
-        returnFunc = App.showConfirmationPopup(function () {
-          self.set('rangerKMSServerHost', hostName);
-          self.loadConfigs("loadRangerConfigs");
-        }, Em.I18n.t('hosts.host.addComponent.' + componentName) + manualKerberosWarning);
+        self.set('rangerKMSServerHost', hostName);
+        returnFunc = self.showAddComponentConfirmation(componentName, 'loadRangerConfigs', function (groups) {
+          this.saveConfigsBatch(groups, componentName, hostName);
+        });
         break;
       case 'JOURNALNODE':
         returnFunc = App.showConfirmationPopup(function () {
@@ -770,14 +822,17 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * Success callback for Storm load configs request
    * @param {object} data
+   * @param {object} opt
+   * @param {object} params
    * @method loadStormConfigs
    */
-  loadStormConfigs: function (data) {
+  loadStormConfigs: function (data, opt, params) {
     App.ajax.send({
       name: 'admin.get.all_configurations',
       sender: this,
       data: {
-        urlParams: '(type=storm-site&tag=' + data.Clusters.desired_configs['storm-site'].tag + ')'
+        urlParams: '(type=storm-site&tag=' + data.Clusters.desired_configs['storm-site'].tag + ')',
+        configs: params.configs
       },
       success: 'onLoadStormConfigs'
     });
@@ -786,9 +841,10 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * Update zk configs
    * @param {object} configs
+   * @param {object} configsForReview
    * @method updateZkConfigs
    */
-  updateZkConfigs: function (configs) {
+  updateZkConfigs: function (configs, configsForReview) {
     var portValue = configs['zoo.cfg'] && Em.get(configs['zoo.cfg'], 'clientPort');
     var zkPort = typeof portValue === 'undefined' ? '2181' : portValue;
     var infraSolrZnode = configs['infra-solr-env'] ? Em.get(configs['infra-solr-env'], 'infra_solr_znode') : '/ambari-solr';
@@ -813,13 +869,25 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
     Em.keys(configs).forEach(function(fileName) {
       var properties = configs[fileName];
       Em.keys(properties).forEach(function(propertyName) {
-        var propertyDef = {
-          fileName: fileName,
-          name: propertyName,
-          value: properties[propertyName]
-        };
+        var currentValue = properties[propertyName],
+          propertyDef = {
+            fileName: fileName,
+            name: propertyName,
+            value: currentValue
+          };
         var configProperty = initializer.initialValue(propertyDef, hostComponentsTopology, dependencies);
         initializer.updateSiteObj(configs[fileName], configProperty);
+        if (configsForReview && currentValue !== propertyDef.value) {
+          var service = App.config.get('serviceByConfigTypeMap')[fileName];
+          configsForReview.propertiesToChange.pushObject({
+            propertyFileName: fileName,
+            propertyName: propertyName,
+            serviceDisplayName: service && service.get('displayName'),
+            initialValue: currentValue,
+            recommendedValue: propertyDef.value,
+            saveRecommended: true
+          });
+        }
       });
     });
   },
@@ -846,9 +914,11 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * update and save Storm related configs to server
    * @param {object} data
+   * @param {object} opt
+   * @param {object} params
    * @method onLoadStormConfigs
    */
-  onLoadStormConfigs: function (data) {
+  onLoadStormConfigs: function (data, opt, params) {
     var nimbusHost = this.get('nimbusHost'),
       stormNimbusHosts = this.getStormNimbusHosts(),
       configs = {},
@@ -859,7 +929,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       attributes[item.type] = item.properties_attributes || {};
     }, this);
 
-    this.updateZkConfigs(configs);
+    this.updateZkConfigs(configs, params.configs);
 
     configs['storm-site']['nimbus.seeds'] = JSON.stringify(stormNimbusHosts).replace(/"/g, "'");
     var groups = [
@@ -872,15 +942,22 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
         }
       }
     ];
-    this.saveConfigsBatch(groups, 'NIMBUS', nimbusHost);
+    if (params.configs) {
+      params.configs.groups = groups;
+      this.set('isConfigsLoaded', true);
+    } else {
+      this.saveConfigsBatch(groups, 'NIMBUS', nimbusHost);
+    }
   },
 
   /**
    * Success callback for load configs request
    * @param {object} data
-   * @method loadHiveConfigs
+   * @param {object} opt
+   * @param {object} params
+   * @method loadWebHCatConfigs
    */
-  loadWebHCatConfigs: function (data) {
+  loadWebHCatConfigs: function (data, opt, params) {
     return App.ajax.send({
       name: 'admin.get.all_configurations',
       sender: this,
@@ -891,7 +968,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
           '(type=webhcat-site&tag=' + data.Clusters.desired_configs['webhcat-site'].tag + ')',
           '(type=hive-env&tag=' + data.Clusters.desired_configs['hive-env'].tag + ')',
           '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')'
-        ].join('|')
+        ].join('|'),
+        configs: params.configs
       },
       success: 'onLoadHiveConfigs'
     });
@@ -900,9 +978,11 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * Success callback for load configs request
    * @param {object} data
+   * @param {object} opt
+   * @param {object} params
    * @method loadHiveConfigs
    */
-  loadHiveConfigs: function (data) {
+  loadHiveConfigs: function (data, opt, params) {
     return App.ajax.send({
       name: 'admin.get.all_configurations',
       sender: this,
@@ -912,7 +992,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
           '(type=webhcat-site&tag=' + data.Clusters.desired_configs['webhcat-site'].tag + ')',
           '(type=hive-env&tag=' + data.Clusters.desired_configs['hive-env'].tag + ')',
           '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')'
-        ].join('|')
+        ].join('|'),
+        configs: params.configs
       },
       success: 'onLoadHiveConfigs'
     });
@@ -961,12 +1042,24 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
     ['hive-site', 'webhcat-site', 'hive-env', 'core-site'].forEach(function(fileName) {
       if (configs[fileName]) {
         Em.keys(configs[fileName]).forEach(function(propertyName) {
-          var propertyDef = {
-            fileName: fileName,
-            name: propertyName,
-            value: configs[fileName][propertyName]
-          };
+          var currentValue = configs[fileName][propertyName],
+            propertyDef = {
+              fileName: fileName,
+              name: propertyName,
+              value: currentValue
+            };
           configs[fileName][propertyName] = Em.get(initializer.initialValue(propertyDef, localDB, dependencies), 'value');
+          if (params.configs && propertyDef.value !== currentValue) {
+            var service = App.config.get('serviceByConfigTypeMap')[fileName];
+            params.configs.propertiesToChange.pushObject({
+              propertyFileName: fileName,
+              propertyName: propertyName,
+              serviceDisplayName: service && service.get('displayName'),
+              initialValue: currentValue,
+              recommendedValue: propertyDef.value,
+              saveRecommended: true
+            });
+          }
         });
       }
     });
@@ -995,12 +1088,17 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
         }
       }
     ];
-    var params = [groups];
-    var componentName = this.get('addHiveServer') ? 'HIVE_SERVER' : (hiveMetastoreHost ? 'HIVE_METASTORE' : 'WEBHCAT_SERVER');
-    var host = webhcatServerHost || hiveMetastoreHost;
-    params.pushObjects([componentName, host]);
-    this.saveConfigsBatch.apply(this, params);
-    this.set('addHiveServer', false);
+    if (params.configs) {
+      params.configs.groups = groups;
+      this.set('isConfigsLoaded', true);
+    } else {
+      var args = [groups];
+      var componentName = this.get('addHiveServer') ? 'HIVE_SERVER' : (hiveMetastoreHost ? 'HIVE_METASTORE' : 'WEBHCAT_SERVER');
+      var host = webhcatServerHost || hiveMetastoreHost;
+      args.pushObjects([componentName, host]);
+      this.saveConfigsBatch.apply(this, args);
+      this.set('addHiveServer', false);
+    }
   },
 
   /**
@@ -1116,14 +1214,17 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * Success callback for load configs request
    * @param {object} data
-   * @method loadHiveConfigs
+   * @param {object} opt
+   * @param {object} params
+   * @method loadRangerConfigs
    */
-  loadRangerConfigs: function (data) {
+  loadRangerConfigs: function (data, opt, params) {
     App.ajax.send({
       name: 'admin.get.all_configurations',
       sender: this,
       data: {
-        urlParams: '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')|(type=hdfs-site&tag=' + data.Clusters.desired_configs['hdfs-site'].tag + ')|(type=kms-env&tag=' + data.Clusters.desired_configs['kms-env'].tag + ')'
+        urlParams: '(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')|(type=hdfs-site&tag=' + data.Clusters.desired_configs['hdfs-site'].tag + ')|(type=kms-env&tag=' + data.Clusters.desired_configs['kms-env'].tag + ')',
+        configs: params.configs
       },
       success: 'onLoadRangerConfigs'
     });
@@ -1132,12 +1233,25 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * update and save Hive hive.metastore.uris config to server
    * @param {object} data
-   * @method onLoadHiveConfigs
+   * @param {object} opt
+   * @param {object} params
+   * @method onLoadRangerConfigs
    */
-  onLoadRangerConfigs: function (data) {
+  onLoadRangerConfigs: function (data, opt, params) {
+    var properties = [
+      {
+        type: 'core-site',
+        name: 'hadoop.security.key.provider.path'
+      },
+      {
+        type: 'hdfs-site',
+        name: 'dfs.encryption.key.provider.uri'
+      }
+    ];
     var hostToInstall = this.get('rangerKMSServerHost');
     var rkmsHosts = this.getRangerKMSServerHosts();
     var rkmsPort = data.items.findProperty('type', 'kms-env').properties['kms_port'];
+    var newValue = 'kms://http@' + rkmsHosts.join(';') + ':' + rkmsPort + '/kms';
     var coreSiteConfigs = data.items.findProperty('type', 'core-site');
     var hdfsSiteConfigs = data.items.findProperty('type', 'hdfs-site');
     var groups = [
@@ -1153,9 +1267,28 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       }
     ];
 
-    coreSiteConfigs.properties['hadoop.security.key.provider.path'] = 'kms://http@' + rkmsHosts.join(';') + ':' + rkmsPort + '/kms';
-    hdfsSiteConfigs.properties['dfs.encryption.key.provider.uri'] = 'kms://http@' + rkmsHosts.join(';') + ':' + rkmsPort + '/kms';
-    this.saveConfigsBatch(groups, 'RANGER_KMS_SERVER', hostToInstall);
+    properties.forEach(function (property) {
+      var typeConfigs = data.items.findProperty('type', property.type).properties,
+        currentValue = typeConfigs[property.name];
+      if (params.configs && currentValue !== newValue) {
+        var service = App.config.get('serviceByConfigTypeMap')[property.type];
+        params.configs.propertiesToChange.pushObject({
+          propertyFileName: property.type,
+          propertyName: property.name,
+          serviceDisplayName: service && service.get('displayName'),
+          initialValue: currentValue,
+          recommendedValue: newValue,
+          saveRecommended: true
+        });
+      }
+      typeConfigs[property.name] = newValue;
+    });
+    if (params.configs) {
+      params.configs.groups = groups;
+      this.set('isConfigsLoaded', true);
+    } else {
+      this.saveConfigsBatch(groups, 'RANGER_KMS_SERVER', hostToInstall);
+    }
   },
 
   /**
@@ -1273,7 +1406,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       this.removeObserver('App.router.backgroundOperationsController.serviceTimestamp', this, this.checkZkConfigs);
       setTimeout(function () {
         self.updateStormConfigs();
-        var callback =   function () {
+        var callback = function () {
           self.loadConfigs();
         };
         self.isServiceMetricsLoaded(callback);
@@ -1287,12 +1420,16 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
    * This is required to make sure that service metrics API determining the HA state of components is loaded
    * @method loadConfigs
    */
-  loadConfigs: function (callback) {
+  loadConfigs: function (callback, configs) {
+    this.set('isConfigsLoaded', false);
     App.ajax.send({
       name: 'config.tags',
       sender: this,
       success: callback ? callback : 'loadConfigsSuccessCallback',
-      error: 'onLoadConfigsErrorCallback'
+      error: 'onLoadConfigsErrorCallback',
+      data: {
+        configs: configs
+      }
     });
   },
 
@@ -1307,16 +1444,19 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * Success callback for load configs request
    * @param {object} data
-   * @method adConfigsSuccessCallback
+   * @param {object} opt
+   * @param {object} params
+   * @method loadConfigsSuccessCallback
    */
-  loadConfigsSuccessCallback: function (data) {
+  loadConfigsSuccessCallback: function (data, opt, params) {
     var urlParams = this.constructConfigUrlParams(data);
     if (urlParams.length > 0) {
       App.ajax.send({
         name: 'reassign.load_configs',
         sender: this,
         data: {
-          urlParams: urlParams.join('|')
+          urlParams: urlParams.join('|'),
+          configs: params.configs
         },
         success: 'saveZkConfigs'
       });
@@ -1366,9 +1506,11 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * save new ZooKeeper configs to server
    * @param {object} data
+   * @param {object} opt
+   * @param {object} params
    * @method saveZkConfigs
    */
-  saveZkConfigs: function (data) {
+  saveZkConfigs: function (data, opt, params) {
     var configs = {};
     var attributes = {};
     data.items.forEach(function (item) {
@@ -1376,7 +1518,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
       attributes[item.type] = item.properties_attributes || {};
     }, this);
 
-    this.updateZkConfigs(configs);
+    this.updateZkConfigs(configs, params.configs);
     var groups = [
       {
         properties: {
@@ -1450,7 +1592,12 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
         }
       );
     }
-    this.saveConfigsBatch(groups, 'ZOOKEEPER_SERVER');
+    if (params.configs) {
+      params.configs.groups = groups;
+      this.set('isConfigsLoaded', true);
+    } else {
+      this.saveConfigsBatch(groups, 'ZOOKEEPER_SERVER');
+    }
   },
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/e148cb5a/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 26f2ce5..24ff1fc 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -463,7 +463,8 @@ Em.I18n.translations = {
   'popup.invalid.KDC.admin.password': 'Admin password',
 
   'popup.dependent.configs.header': 'Dependent Configurations',
-  'popup.dependent.configs.title': 'Based on your configuration changes, Ambari is recommending the following dependent configuration changes. <br/> Ambari will update all checked configuration changes to the <b>Recommended Value</b>. Uncheck any configuration to retain the <b>Current Value</b>.',
+  'popup.dependent.configs.title.recommendation': 'Based on your configuration changes, Ambari is recommending the following dependent configuration changes.',
+  'popup.dependent.configs.title.values': 'Ambari will update all checked configuration changes to the <b>Recommended Value</b>. Uncheck any configuration to retain the <b>Current Value</b>.',
   'popup.dependent.configs.table.saveProperty': 'Save property',
   'popup.dependent.configs.table.initValue': 'Initial value',
   'popup.dependent.configs.table.currentValue': 'Current Value',
@@ -2669,7 +2670,7 @@ Em.I18n.translations = {
   'hosts.host.zooKeeper.configs.save.note': 'This configuration is created by ambari while installing/deleting zookeeper component on a host',
   'hosts.host.addComponent.securityNote':'You are running your cluster in secure mode. You must set up the keytab for {0} on {1} before you proceed. Otherwise, the component will not be able to start properly.',
   'hosts.host.addComponent.popup.confirm':'Confirm Add',
-  'hosts.host.manualKerberosWarning': '<br/><strong>Because Kerberos has been manually installed on the cluster, you will have to create/distribute principals and keytabs when this operation is finished.</strong>',
+  'hosts.host.manualKerberosWarning': '<strong>Because Kerberos has been manually installed on the cluster, you will have to create/distribute principals and keytabs when this operation is finished.</strong>',
   'hosts.host.deleteComponent.popup.deleteNimbus':'Deleting <i>Storm Nimbus</i> will reconfigure <b>nimbus.seeds</b>, <b>topology.min.replication.count</b>, <b>topology.max.replication.wait.time.sec</b> properties if they are defined.',
   'hosts.host.storm.configs.save.note': 'This configuration is created by ambari while installing/deleting storm component on a host',
   'hosts.host.datanode.decommission':'Decommission DataNode',

http://git-wip-us.apache.org/repos/asf/ambari/blob/e148cb5a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
index d117dd1..a1f53ef 100644
--- a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
+++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
@@ -17,7 +17,10 @@
 }}
 
 <div class="alert alert-warning">
-  {{t popup.dependent.configs.title}}
+  {{#if view.isAfterRecommendation}}
+    <div>{{t popup.dependent.configs.title.recommendation}}</div>
+  {{/if}}
+  <div>{{t popup.dependent.configs.title.values}}</div>
 </div>
 <span id="config-dependencies" class="limited-height-2">
   <table class="table table-hover">
@@ -39,7 +42,7 @@
     </tr>
     </thead>
     <tbody>
-    {{#each recommendation in view.parentView.recommendations}}
+    {{#each recommendation in view.recommendations}}
       <tr {{bindAttr class="recommendation.saveRecommended:active"}}>
         <td class="config-dependency-name">{{recommendation.propertyName}}</td>
         <td class="config-dependency-service">{{recommendation.serviceDisplayName}}</td>

http://git-wip-us.apache.org/repos/asf/ambari/blob/e148cb5a/ambari-web/app/templates/main/host/details/addComponentWithConfigsChanges.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/details/addComponentWithConfigsChanges.hbs b/ambari-web/app/templates/main/host/details/addComponentWithConfigsChanges.hbs
new file mode 100644
index 0000000..d62715d
--- /dev/null
+++ b/ambari-web/app/templates/main/host/details/addComponentWithConfigsChanges.hbs
@@ -0,0 +1,27 @@
+{{!
+* 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.
+}}
+
+{{#if controller.isConfigsLoaded}}
+  {{view.commonMessage}}
+  {{#if view.propertiesToChange.length}}
+    {{view App.DependentConfigsListView isAfterRecommendation=false recommendationsBinding="view.propertiesToChange"}}
+  {{/if}}
+  {{{view.manualKerberosWarning}}}
+{{else}}
+  {{view App.SpinnerView}}
+{{/if}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/e148cb5a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
index 728634e..1aa46eb 100644
--- a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
+++ b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
@@ -18,6 +18,31 @@
 
 var App = require('app');
 
+App.DependentConfigsListView = Em.View.extend({
+  templateName: require('templates/common/modal_popups/dependent_configs_list'),
+  isAfterRecommendation: true,
+  recommendations: [],
+  toggleAll: App.CheckboxView.extend({
+    didInsertElement: function () {
+      this.set('parentView.toggleAllId', this.get('elementId'));
+      this.updateCheckbox();
+    },
+    click: function () {
+      Em.run.next(this, 'updateSaveRecommended');
+    },
+    updateCheckboxObserver: function () {
+      Em.run.once(this, 'updateCheckbox');
+    }.observes('parentView.parentView.recommendations.@each.saveRecommended'),
+
+    updateCheckbox: function() {
+      this.set('checked', !(this.get('parentView.parentView.recommendations') || []).someProperty('saveRecommended', false));
+    },
+    updateSaveRecommended: function() {
+      this.get('parentView.parentView.recommendations').setEach('saveRecommended', this.get('checked'));
+    }
+  })
+});
+
 /**
  * Show confirmation popup
  * @param {[Object]} recommendations
@@ -32,38 +57,17 @@ App.showDependentConfigsPopup = function (recommendations, primary, secondary) {
     header: Em.I18n.t('popup.dependent.configs.header'),
     classNames: ['common-modal-wrapper','modal-full-width'],
     modalDialogClasses: ['modal-lg'],
-    recommendations: recommendations,
     secondaryClass: 'cancel-button',
-    bodyClass: Em.View.extend({
-      templateName: require('templates/common/modal_popups/dependent_configs_list'),
-      toggleAllId: '',
-      toggleAll: App.CheckboxView.extend({
-        didInsertElement: function () {
-          this.set('parentView.toggleAllId', this.get('elementId'));
-          this.updateCheckbox();
-        },
-        click: function () {
-          Em.run.next(this, 'updateSaveRecommended');
-        },
-        updateCheckboxObserver: function () {
-          Em.run.once(this, 'updateCheckbox');
-        }.observes('parentView.parentView.recommendations.@each.saveRecommended'),
-
-        updateCheckbox: function() {
-          this.set('checked', !(this.get('parentView.parentView.recommendations') || []).someProperty('saveRecommended', false));
-        },
-        updateSaveRecommended: function() {
-          this.get('parentView.parentView.recommendations').setEach('saveRecommended', this.get('checked'));
-        }
-      })
+    bodyClass: App.DependentConfigsListView.extend({
+      recommendations: recommendations
     }),
     saveChanges: function() {
-      this.get('recommendations').forEach(function (c) {
+      recommendations.forEach(function (c) {
         Em.set(c, 'saveRecommendedDefault', Em.get(c, 'saveRecommended'));
       })
     },
     discardChanges: function() {
-      this.get('recommendations').forEach(function(c) {
+      recommendations.forEach(function(c) {
         Em.set(c, 'saveRecommended', Em.get(c, 'saveRecommendedDefault'));
       });
     },

http://git-wip-us.apache.org/repos/asf/ambari/blob/e148cb5a/ambari-web/test/controllers/main/host/details_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js
index 9a56ab2..5dc13a9 100644
--- a/ambari-web/test/controllers/main/host/details_test.js
+++ b/ambari-web/test/controllers/main/host/details_test.js
@@ -501,10 +501,10 @@ describe('App.MainHostDetailsController', function () {
 
   describe('#addComponent()', function () {
     beforeEach(function () {
-      sinon.spy(App, "showConfirmationPopup");
       sinon.stub(controller, "addClientComponent", Em.K);
       sinon.stub(controller, "installHostComponentCall", Em.K);
       sinon.stub(controller, "checkComponentDependencies", Em.K);
+      sinon.stub(controller, "showAddComponentConfirmation", Em.K);
       controller.set('content', {
         hostComponents: [Em.Object.create({
           componentName: "HDFS_CLIENT"
@@ -516,10 +516,10 @@ describe('App.MainHostDetailsController', function () {
     });
 
     afterEach(function () {
-      App.showConfirmationPopup.restore();
       controller.addClientComponent.restore();
       controller.installHostComponentCall.restore();
       controller.checkComponentDependencies.restore();
+      controller.showAddComponentConfirmation.restore();
     });
 
     it('add ZOOKEEPER_SERVER', function () {
@@ -529,7 +529,7 @@ describe('App.MainHostDetailsController', function () {
         })
       };
       controller.addComponent(event);
-      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      expect(controller.showAddComponentConfirmation.calledOnce).to.be.true;
     });
     it('add WEBHCAT_SERVER', function () {
       var event = {
@@ -538,7 +538,7 @@ describe('App.MainHostDetailsController', function () {
         })
       };
       controller.addComponent(event);
-      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      expect(controller.showAddComponentConfirmation.calledOnce).to.be.true;
     });
     it('add slave component', function () {
       var event = {
@@ -647,12 +647,15 @@ describe('App.MainHostDetailsController', function () {
             tag: 'tag'
           }
         }
-      }});
+      }}, null, {
+        configs: {}
+      });
       var args = testHelpers.findAjaxRequest('name', 'admin.get.all_configurations');
       expect(args[0]).exists;
       expect(args[0].sender).to.be.eql(controller);
       expect(args[0].data).to.be.eql({
-        urlParams: '(type=storm-site&tag=tag)'
+        urlParams: '(type=storm-site&tag=tag)',
+        configs: {}
       });
     });
   });
@@ -673,7 +676,7 @@ describe('App.MainHostDetailsController', function () {
       sinon.stub(controller, 'updateZkConfigs', Em.K);
       sinon.stub(controller, 'saveConfigsBatch', Em.K);
       controller.set('nimbusHost', 'host2');
-      controller.onLoadStormConfigs(data);
+      controller.onLoadStormConfigs(data, null, {});
     });
     afterEach(function () {
       controller.getStormNimbusHosts.restore();
@@ -718,12 +721,15 @@ describe('App.MainHostDetailsController', function () {
             tag: 'tag'
           }
         }
-      }});
+      }}, null, {
+        configs: {}
+      });
       var args = testHelpers.findAjaxRequest('name', 'admin.get.all_configurations');
       expect(args[0]).exists;
       expect(args[0].sender).to.be.eql(controller);
       expect(args[0].data).to.be.eql({
-        urlParams: '(type=hive-site&tag=tag)|(type=webhcat-site&tag=tag)|(type=hive-env&tag=tag)|(type=core-site&tag=tag)'
+        urlParams: '(type=hive-site&tag=tag)|(type=webhcat-site&tag=tag)|(type=hive-env&tag=tag)|(type=core-site&tag=tag)',
+        configs: {}
       });
     });
   });
@@ -742,12 +748,15 @@ describe('App.MainHostDetailsController', function () {
             tag: 'tag'
           }
         }
-      }});
+      }}, null, {
+        configs: {}
+      });
       var args = testHelpers.findAjaxRequest('name', 'admin.get.all_configurations');
       expect(args[0]).exists;
       expect(args[0].sender).to.be.eql(controller);
       expect(args[0].data).to.be.eql({
-        urlParams: '(type=core-site&tag=tag)|(type=hdfs-site&tag=tag)|(type=kms-env&tag=tag)'
+        urlParams: '(type=core-site&tag=tag)|(type=hdfs-site&tag=tag)|(type=kms-env&tag=tag)',
+        configs: {}
       });
     });
   });
@@ -1075,7 +1084,7 @@ describe('App.MainHostDetailsController', function () {
         ];
       });
 
-      controller.saveZkConfigs(data);
+      controller.saveZkConfigs(data, null, {});
       this.groups = controller.saveConfigsBatch.args[0][0];
     });
     afterEach(function () {
@@ -3440,7 +3449,7 @@ describe('App.MainHostDetailsController', function () {
         beforeEach(function () {
           controller.set('rangerKMSServerHost', item.hostToInstall);
           sinon.stub(controller, 'getRangerKMSServerHosts').returns(item.kmsHosts);
-          controller.onLoadRangerConfigs(data);
+          controller.onLoadRangerConfigs(data, null, {});
         });
 
         it('saveConfigsBatch is called with valid arguments', function () {