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 2015/03/20 17:13:45 UTC

ambari git commit: AMBARI-10156 Editing configs with depended-by configs should trigger call to /recommendations. (ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk ea8e32ff1 -> b17747ec5


AMBARI-10156 Editing configs with depended-by configs should trigger call to /recommendations. (ababiichuk)


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

Branch: refs/heads/trunk
Commit: b17747ec509d970fa0b873579c668fcd2ff0194c
Parents: ea8e32f
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Fri Mar 20 16:59:23 2015 +0200
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Fri Mar 20 16:59:23 2015 +0200

----------------------------------------------------------------------
 .../configuration_dependencies.json             |  87 ++++++++
 .../data/stacks/HDP-2.2/configurations.json     |  16 ++
 .../controllers/global/cluster_controller.js    |   2 +-
 .../global/configuration_controller.js          |   2 +-
 .../controllers/main/service/info/configs.js    | 219 ++++++++++++++++---
 .../mappers/configs/config_versions_mapper.js   |  19 ++
 ambari-web/app/messages.js                      |   7 +
 ambari-web/app/mixins/common/serverValidator.js |  85 ++++++-
 .../app/models/configs/config_property.js       |  17 ++
 .../configs/config_recommendation_popup.hbs     |  68 ------
 .../config_recommendation_popup.hbs             |  68 ++++++
 .../modal_popups/dependent_configs_list.hbs     |  29 +++
 ambari-web/app/utils/ajax/ajax.js               |  18 +-
 ambari-web/app/utils/config.js                  |  18 +-
 ambari-web/app/views.js                         |   1 +
 .../dependent_configs_list_popup.js             |  66 ++++++
 .../main/service/info/config_test.js            |  44 ++++
 17 files changed, 654 insertions(+), 112 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/assets/data/configurations/recommendations/configuration_dependencies.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/configurations/recommendations/configuration_dependencies.json b/ambari-web/app/assets/data/configurations/recommendations/configuration_dependencies.json
new file mode 100644
index 0000000..6c11827
--- /dev/null
+++ b/ambari-web/app/assets/data/configurations/recommendations/configuration_dependencies.json
@@ -0,0 +1,87 @@
+{
+  "resources": [
+    {
+      "href": "http://c6405:8080/api/v1/stacks/HDP/versions/2.2/recommendations/5",
+      "hosts": [
+        "c6406.ambari.apache.org",
+        "c6405.ambari.apache.org"
+      ],
+      "services": [
+        "HDFS",
+        "MAPREDUCE2",
+        "ZOOKEEPER",
+        "YARN"
+      ],
+      "Recommendation": {
+        "id": 5
+      },
+      "Versions": {
+        "stack_name": "HDP",
+        "stack_version": "2.2"
+      },
+      "recommendations": {
+        "blueprint": {
+          "host_groups": [
+            {
+              "components": [
+                {
+                  "name": "DATANODE"
+                }
+              ],
+              "name": "host-group-1"
+            },
+            {
+              "components": [
+                {
+                  "name": "DATANODE"
+                }
+              ],
+              "name": "host-group-2"
+            }
+          ],
+          "configurations": {
+            "mapred-site": {
+              "properties": {
+                "mapreduce.map.java.opts": "-Xmx546m1",
+                "mapreduce.map.memory.mb": "6821",
+                "mapreduce.reduce.java.opts": "-Xmx546m1",
+                "mapreduce.reduce.memory.mb": "6821",
+                "mapreduce.task.io.sort.mb": "2731",
+                "yarn.app.mapreduce.am.command-opts": "-Xmx546m -Dhdp.version=${hdp.version}1",
+                "yarn.app.mapreduce.am.resource.mb": "6821"
+              }
+            },
+            "yarn-site": {
+              "properties": {
+                "yarn.nodemanager.resource.cpu-vcores": "11",
+                "yarn.nodemanager.resource.memory-mb": "2048",
+                "yarn.scheduler.maximum-allocation-mb": "20481",
+                "yarn.scheduler.minimum-allocation-mb": "6821"
+              }
+            }
+          }
+        },
+        "blueprint_cluster_binding": {
+          "host_groups": [
+            {
+              "hosts": [
+                {
+                  "name": "c6405.ambari.apache.org"
+                }
+              ],
+              "name": "host-group-1"
+            },
+            {
+              "hosts": [
+                {
+                  "name": "c6406.ambari.apache.org"
+                }
+              ],
+              "name": "host-group-2"
+            }
+          ]
+        }
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/assets/data/stacks/HDP-2.2/configurations.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/stacks/HDP-2.2/configurations.json b/ambari-web/app/assets/data/stacks/HDP-2.2/configurations.json
index 3751ec9..b4ee154 100644
--- a/ambari-web/app/assets/data/stacks/HDP-2.2/configurations.json
+++ b/ambari-web/app/assets/data/stacks/HDP-2.2/configurations.json
@@ -7725,6 +7725,12 @@
             "property_description" : "By default, Tez uses the java options from map tasks. Use this property to override that value.",
             "property_name" : "hive.tez.container.size",
             "property_type" : [ ],
+            "property_depended_by": [
+              {
+                "property_type": "hdfs-site",
+                "property_name": "dfs.replication.max"
+              }
+            ],
             "property_value" : "682",
             "service_name" : "HIVE",
             "stack_name" : "HDP",
@@ -16759,6 +16765,16 @@
             "property_description" : "\n      The minimum allocation for every container request at the RM,\n      in MBs. Memory requests lower than this won't take effect,\n      and the specified value will get allocated at minimum.\n    ",
             "property_name" : "yarn.scheduler.minimum-allocation-mb",
             "property_type" : [ ],
+            "property_depended_by": [
+              {
+                "property_type": "hive-site",
+                "property_name": "hive.tez.container.size"
+              },
+              {
+                "property_type": "mapred-site",
+                "property_name": "mapreduce.map.memory.mb"
+              }
+            ],
             "property_value" : "512",
             "service_name" : "YARN",
             "stack_name" : "HDP",

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/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 ba3db08..52310cf 100644
--- a/ambari-web/app/controllers/global/cluster_controller.js
+++ b/ambari-web/app/controllers/global/cluster_controller.js
@@ -280,7 +280,7 @@ App.ClusterController = Em.Controller.extend({
         });
 
         updater.updateServiceMetric(function () {
-
+          App.config.loadConfigsFromStack(App.Service.find().mapProperty('serviceName'));
           updater.updateComponentConfig(function () {
             self.updateLoadStatus('componentConfigs');
           });

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/controllers/global/configuration_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/configuration_controller.js b/ambari-web/app/controllers/global/configuration_controller.js
index b1d3ff7..9270fac 100644
--- a/ambari-web/app/controllers/global/configuration_controller.js
+++ b/ambari-web/app/controllers/global/configuration_controller.js
@@ -84,7 +84,7 @@ App.ConfigurationController = Em.Controller.extend({
       jqXhr.done(function (data) {
         configTags = data.Clusters.desired_configs;
         tags.forEach(function (_tag) {
-          if (_tag.siteName && !_tag.tagName) {
+          if (_tag.siteName && configTags[_tag.siteName] && !_tag.tagName) {
             _tag.tagName = configTags[_tag.siteName].tag;
           }
         }, self);

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/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 ab86623..6ee3e66 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -65,6 +65,14 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
   }.property('selectedVersion', 'content.serviceName', 'dataIsLoaded'),
 
   /**
+   * @type {[Object]} that will contain items like
+   *{
+   *  "type": "yarn-site",
+   *  "name": "yarn.nodemanager.resource.memory-mb"
+   *}
+   */
+  changedConfigWithDependencies: [],
+  /**
    * @type {boolean}
    */
   canEdit: function () {
@@ -101,12 +109,6 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
    * }
    */
   loadedClusterSiteToTagMap: {},
-  /**
-   * Holds the actual base service-config server data uploaded.
-   * This is used by the host-override mechanism to update host
-   * specific values.
-   */
-  savedSiteNameToServerServiceConfigDataMap: {},
 
   isSubmitDisabled: function () {
     return (!(this.get('stepConfigs').everyProperty('errorCount', 0)) || this.get('isApplyingChanges'));
@@ -224,7 +226,6 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
     this.get('uiConfigs').clear();
     this.set('loadedGroupToOverrideSiteToTagMap', {});
     this.set('serviceConfigVersionNote', '');
-    this.set('savedSiteNameToServerServiceConfigDataMap', {});
     if (this.get('serviceConfigTags')) {
       this.set('serviceConfigTags', null);
     }
@@ -322,6 +323,73 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
   },
 
   /**
+   * this method defines dependent file names for configs
+   * and load them to model
+   * @method loadDependentConfigs
+   */
+  loadDependentConfigs: function() {
+    /**
+     * filter out configs for current service with
+     * <code>propertyDependedBy<code>
+     * @type {Array}
+     */
+    var serviceStackProperties = App.StackConfigProperty.find().filter(function(stackProperty) {
+      return stackProperty.get('serviceName') === this.get('content.serviceName') && stackProperty.get('propertyDependedBy.length') > 0
+    }, this);
+
+    /**
+     * defines what fileNames should UI load
+     */
+    serviceStackProperties.forEach(function(serviceStackProperty) {
+      this.calculateDependentFileNames(serviceStackProperty);
+    }, this);
+
+    var serviceConfigsToLoad = this.getServiceNamesForConfigs();
+
+    /**
+     * load serviceConfigVersion
+     * by serviceName that has dependent properties
+     */
+    if (serviceConfigsToLoad.length > 0) {
+      App.config.loadConfigCurrentVersions(serviceConfigsToLoad);
+    }
+  },
+
+  /**
+   * get required fileNames that has dependencies
+   * @returns {string[]}
+   */
+  getServiceNamesForConfigs: function() {
+    return App.StackService.find().filter(function(s) {
+      for (var i = 0; i < this.get('dependentFileNames.length'); i++) {
+        if (Object.keys(s.get('configTypes')).contains(App.config.getConfigTagFromFileName(this.get('dependentFileNames')[i])))
+          return true;
+      }
+      return false;
+    }, this).mapProperty('serviceName').concat(this.get('content.serviceName'));
+  },
+
+  /**
+   * dependent file names for configs
+   */
+  dependentFileNames: [],
+
+  /**
+   * defines file names for configs
+   * @param {App.StackConfigProperty} stackProperty
+   */
+  calculateDependentFileNames: function(stackProperty) {
+    if (stackProperty.get('propertyDependedBy.length') > 0) {
+      stackProperty.get('propertyDependedBy').forEach(function(dependent) {
+        if (!this.get('dependentFileNames').contains(dependent.type)) {
+          this.get('dependentFileNames').push(dependent.type);
+        }
+        this.calculateDependentFileNames(App.StackConfigProperty.find(dependent.name + "_" + dependent.type));
+      }, this);
+    }
+  },
+
+  /**
    * get service config versions of current service
    */
   loadServiceConfigVersions: function () {
@@ -917,6 +985,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
     this.set('versionLoaded', true);
     this.set('hash', this.getHash());
     this.set('isInit', false);
+    this.loadDependentConfigs();
   },
 
   /**
@@ -1173,6 +1242,63 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
   },
 
   /**
+   *
+   * @param modifiedProperties
+   * @param configs
+   * @returns {Array}
+   */
+  generateModifiedConfigs: function(modifiedProperties, configs) {
+
+    var modifiedConfigs = modifiedProperties
+      // get file names and add file names that was modified, for example after property removing
+      .mapProperty('filename').concat(this.get('modifiedFileNames')).uniq()
+      // get configs by filename
+      .map(function(fileName) {
+        return configs.filterProperty('filename', fileName);
+      });
+
+    /**
+     * make same operations to find what configs need to be saved
+     * for configs from model
+     * @type {Ember.Enumerable}
+     */
+    var modifiedFileNames = App.ConfigProperty.find().filter(function(cp) {
+      return cp.get('isNotDefaultValue') && this.get('dependentFileNames').contains(cp.get('fileName'));
+    }, this).mapProperty('fileName').uniq();
+
+    /**
+     * create default ServiceConfigProperty objects from model
+     * and pushing properties that need to be saved to modified configs
+     * @type {Array}
+     */
+    App.ConfigProperty.find().filter(function(cp) {
+      return modifiedFileNames.contains(cp.get('fileName'));
+    }).forEach(function(configFromModel) {
+      var configData = {
+        name: configFromModel.get('name'),
+        displayName: configFromModel.get('stackConfigProperty.displayName'),
+        serviceName: configFromModel.get('stackConfigProperty.serviceName'),
+        value: configFromModel.get('value'),
+        defaultValue: configFromModel.get('defaultValue'),
+        filename: App.config.getOriginalFileName(configFromModel.get('fileName')),
+        isFinal: configFromModel.get('isFinal')
+      };
+      if (configFromModel.get('stackConfigProperty.serviceName') === this.get('content.serviceName')) {
+        var confObject = modifiedConfigs.findProperty('name', configFromModel.get('name'));
+        if (confObject && configFromModel.get('allowSave')) {
+          confObject.set('value', configFromModel.get('value'));
+        } else {
+          modifiedConfigs.push(App.ServiceConfigProperty.create(configData));
+        }
+      } else {
+        modifiedConfigs.push(App.ServiceConfigProperty.create(configData));
+      }
+    }, this);
+
+    return modifiedConfigs;
+  },
+
+  /**
    * Save changed configs and config groups
    */
   saveConfigs: function () {
@@ -1184,28 +1310,62 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
       if (this.get('content.serviceName') === 'YARN') {
         configs = App.config.textareaIntoFileConfigs(configs, 'capacity-scheduler.xml');
       }
-      var modifiedConfigs = configs
-        // get only modified and created configs
-        .filter(function(config) { return config.get('isNotDefaultValue') || config.get('isNotSaved'); })
-        // get file names and add file names that was modified, for example after property removing
-        .mapProperty('filename').concat(this.get('modifiedFileNames')).uniq()
-        // get configs by filename
-        .map(function(fileName) {
-          return configs.filterProperty('filename', fileName);
-        });
-      if (!!modifiedConfigs.length) {
-        // concatenate results
-        modifiedConfigs = modifiedConfigs.reduce(function(current, prev) { return current.concat(prev); });
-      }
 
-      // save modified original configs that have no group
-      this.saveSiteConfigs(modifiedConfigs.filter(function(config) { return !config.get('group'); }));
+      /**
+       * generates list of properties that was chabged
+       * @type {Array}
+       */
+      var modifiedProperties = configs
+        // get only modified and created configs
+        .filter(function(config) { return config.get('isNotDefaultValue') || config.get('isNotSaved'); });
 
       /**
-       * First we put cluster configurations, which automatically creates /configurations
-       * resources. Next we update host level overrides.
+       * check if some of properties that was changed has dependencies;
        */
-      this.doPUTClusterConfigurations();
+      modifiedProperties.forEach(function(p) {
+        /**
+         * step configs don't have <code>propertyDependedBy<code> property
+         * so we need to look in <code>stackConfigProperty<code>;
+         * use <code>App.ConfigProperty<code> that has link to <code>stackConfigProperty<code>
+         */
+        var cfgFromModel = App.ConfigProperty.find().find(function(cp) {
+          return cp.get('name') === p.get('name') && cp.get('fileName') === p.get('filename');
+        }, this);
+
+        if (cfgFromModel) {
+
+          cfgFromModel.set('defaultValue', p.get('value'));
+          /**
+           * generates <code>changedConfigWithDependencies<code>
+           * this array will be send for recommendations as <code>changed_configurations<code>
+           */
+          if (cfgFromModel.get('stackConfigProperty.propertyDependedBy.length') > 0) {
+            this.get('changedConfigWithDependencies').push({
+              "type": cfgFromModel.get('fileName'),
+              "name": cfgFromModel.get("name")
+            });
+          }
+        }
+      }, this);
+
+      this.getRecommendationsForDependencies(this.get('changedConfigWithDependencies')).done(function() {
+
+        var modifiedConfigs = self.generateModifiedConfigs(modifiedProperties, configs);
+
+        if (!!modifiedConfigs.length) {
+          // concatenate results
+          modifiedConfigs = modifiedConfigs.reduce(function(current, prev) { return current.concat(prev); });
+        }
+        // save modified original configs that have no group
+        self.saveSiteConfigs(modifiedConfigs.filter(function(config) { return !config.get('group'); }));
+
+        /**
+         * First we put cluster configurations, which automatically creates /configurations
+         * resources. Next we update host level overrides.
+         */
+        self.doPUTClusterConfigurations();
+      });
+
     } else {
       var overridenConfigs = [];
       var groupHosts = [];
@@ -1866,6 +2026,12 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
   doPUTClusterConfigurations: function () {
     this.set('saveConfigsFlag', true);
     var serviceConfigTags = this.get('serviceConfigTags');
+    /**
+     * adding config tags for dependentConfigs
+     */
+    for (var i = 0; i < this.get('dependentFileNames.length'); i++) {
+      serviceConfigTags.pushObject({siteName: this.get('dependentFileNames')[i]});
+    }
     this.setNewTagNames(serviceConfigTags);
     var siteNameToServerDataMap = {};
     var configsToSave = [];
@@ -1882,7 +2048,6 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
     } else {
       this.onDoPUTClusterConfigurations();
     }
-    this.set("savedSiteNameToServerServiceConfigDataMap", siteNameToServerDataMap);
   },
 
   /**
@@ -1904,7 +2069,7 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorM
         }
         break;
       default:
-        var filename = (App.config.get('filenameExceptions').contains(siteName)) ? siteName : siteName + '.xml';
+        var filename = App.config.getOriginalFileName(siteName);
         if (filename === 'mapred-queue-acls.xml') {
           return null;
         }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/mappers/configs/config_versions_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/configs/config_versions_mapper.js b/ambari-web/app/mappers/configs/config_versions_mapper.js
index 1ae43dc..b470320 100644
--- a/ambari-web/app/mappers/configs/config_versions_mapper.js
+++ b/ambari-web/app/mappers/configs/config_versions_mapper.js
@@ -40,6 +40,7 @@ App.configVersionsMapper = App.QuickDataMapper.create({
     var configVersions = [];
     var itemIds = {};
     var serviceToHostMap = {};
+    var requestedProperties = [];
 
     if (json && json.items) {
       json.items.forEach(function (item, index) {
@@ -71,6 +72,8 @@ App.configVersionsMapper = App.QuickDataMapper.create({
 
               var property = {
                 id: key  + '_' + type + '_' + item.service_config_version,
+                name: key,
+                file_name: App.config.getOriginalFileName(type),
                 config_version_id: parsedItem.id,
                 stack_config_property_id: key  + '_' + type
               };
@@ -78,6 +81,7 @@ App.configVersionsMapper = App.QuickDataMapper.create({
               property.is_final = property.default_is_final = !!item.properties_attributes && item.properties_attributes.final[key] === "true";
 
               properties.push(property);
+              requestedProperties.push(property.id);
             }
           }, this);
         }
@@ -117,6 +121,21 @@ App.configVersionsMapper = App.QuickDataMapper.create({
       });
       App.store.commit();
       App.store.loadMany(this.get('model'), configVersions);
+
+      this.deleteUnusedProperties(requestedProperties);
     }
+  },
+
+
+  /**
+   * delete unused Properties
+   * @param requestedProperties
+   */
+  deleteUnusedProperties: function(requestedProperties) {
+    App.ConfigProperty.find().filterProperty('id').forEach(function(p) {
+      if (!requestedProperties.contains(p.get('id'))) {
+        this.deleteRecord(p);
+      }
+    }, this);
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 1730b62..866f9bb 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -249,6 +249,7 @@ Em.I18n.translations = {
   'common.dismiss': "Dismiss",
   'common.stdout': "stdout",
   'common.stderr': "stderr",
+  'common.fileName': 'File name',
 
   'models.alert_instance.tiggered.verbose': "Occured on {0} <br> Checked on {1}",
   'models.alert_definition.triggered.verbose': "Occured on {0}",
@@ -332,6 +333,12 @@ Em.I18n.translations = {
   'popup.invalid.KDC.admin.principal': 'Admin principal',
   'popup.invalid.KDC.admin.password': 'Admin password',
 
+  'popup.dependent.configs.header': 'Dependent Properties',
+  'popup.dependent.configs.title': 'Properties that was changed has dependent properties. It\'s recommended to update these properties!',
+  'popup.dependent.configs.table.saveProperty': 'Save property',
+  'popup.dependent.configs.table.currentValue': 'Current value',
+  'popup.dependent.configs.table.recommendedValue': 'Recommended value',
+
   'login.header':'Sign in',
   'login.username':'Username',
   'login.loginButton':'Sign in',

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/mixins/common/serverValidator.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/serverValidator.js b/ambari-web/app/mixins/common/serverValidator.js
index 4e774b0..98fe968 100644
--- a/ambari-web/app/mixins/common/serverValidator.js
+++ b/ambari-web/app/mixins/common/serverValidator.js
@@ -118,13 +118,16 @@ App.ServerValidatorMixin = Em.Mixin.create({
       return $.Deferred().resolve();
     }
     return App.ajax.send({
-      'name': 'wizard.step7.loadrecommendations.configs',
+      'name': 'config.recommendations',
       'sender': this,
       'data': {
         stackVersionUrl: App.get('stackVersionURL'),
-        hosts: this.get('hostNames'),
-        services: this.get('serviceNames'),
-        recommendations: this.get('hostGroups')
+        dataToSend: {
+          recommend: 'configurations',
+          hosts: this.get('hostNames'),
+          services: this.get('serviceNames'),
+          recommendations: this.get('hostGroups')
+        }
       },
       'success': 'loadRecommendationsSuccess',
       'error': 'loadRecommendationsError'
@@ -132,6 +135,78 @@ App.ServerValidatorMixin = Em.Mixin.create({
   },
 
   /**
+   *
+   * @param changedConfigs
+   */
+  getRecommendationsForDependencies: function(changedConfigs) {
+    var dfd = $.Deferred();
+    if (Em.isArray(changedConfigs) && changedConfigs.length > 0) {
+      var recommendations = this.get('hostGroups');
+      recommendations.blueprint.configurations = blueprintUtils.buildConfigsJSON(this.get('services'), this.get('stepConfigs'));
+
+      var dataToSend = {
+        recommend: 'configurations',
+        hosts: this.get('hostNames'),
+        services: this.get('serviceNames'),
+        recommendations: recommendations
+      };
+      if (App.get('supports.enhancedConfigs')) {
+        dataToSend.recommend = 'configuration-dependencies';
+        dataToSend.changed_configurations = changedConfigs;
+      }
+      App.ajax.send({
+        name: 'config.recommendations',
+        sender: this,
+        data: {
+          stackVersionUrl: App.get('stackVersionURL'),
+          dataToSend: dataToSend,
+          dfd: dfd
+        },
+        success: 'dependenciesSuccess',
+        error: 'dependenciesError'
+      });
+    } else {
+      dfd.resolve();
+    }
+    return dfd.promise();
+  },
+
+  /**
+   *
+   * @param data
+   * @param opt
+   * @param params
+   */
+  dependenciesSuccess: function(data, opt, params) {
+    Em.assert('invalid data', data && data.resources[0] && Em.get(data.resources[0], 'recommendations.blueprint.configurations'));
+    var configs = data.resources[0].recommendations.blueprint.configurations;
+    var currentProperties = App.ConfigProperty.find().filterProperty('configVersion.isCurrent').filterProperty('configVersion.groupId', -1);
+    for (var key in configs) {
+      for (var propertyName in configs[key].properties) {
+        var property = currentProperties.findProperty('name', propertyName)
+        if (property) {
+          property.set('value', configs[key].properties[propertyName]);
+        }
+      }
+    }
+    var configsToShow = currentProperties.filterProperty('isNotDefaultValue');
+    App.showDependentConfigsPopup(configsToShow, params.dfd);
+  },
+
+  /**
+   *
+   * @param jqXHR
+   * @param ajaxOptions
+   * @param error
+   * @param opt
+   * @param params
+   */
+  dependenciesError: function(jqXHR, ajaxOptions, error, opt, params) {
+    App.ajax.defaultErrorHandler(jqXHR, opt.url, opt.method, jqXHR.status);
+    params.dfd.reject();
+  },
+
+  /**
    * @method loadRecommendationsSuccess
    * success callback after loading recommendations
    * (used only during install)
@@ -280,7 +355,7 @@ App.ServerValidatorMixin = Em.Mixin.create({
         },
         bodyClass: Em.View.extend({
           controller: self,
-          templateName: require('templates/common/configs/config_recommendation_popup')
+          templateName: require('templates/common/modal_popups/config_recommendation_popup')
         })
       });
     } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/models/configs/config_property.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/configs/config_property.js b/ambari-web/app/models/configs/config_property.js
index 6be05d8..929e63e 100644
--- a/ambari-web/app/models/configs/config_property.js
+++ b/ambari-web/app/models/configs/config_property.js
@@ -26,6 +26,17 @@ App.ConfigProperty = DS.Model.extend({
   id: DS.attr('string'),
 
   /**
+   * config property name
+   * @property {string}
+   */
+  name: DS.attr('string'),
+
+  /**
+   * config property name
+   * @property {string}
+   */
+  fileName: DS.attr('string'),
+  /**
    * value of property
    * by default is same as <code>defaultValue<code>
    * @property {string}
@@ -109,6 +120,12 @@ App.ConfigProperty = DS.Model.extend({
   isHiddenByFilter: DS.attr('boolean', {defaultValue: false}),
 
   /**
+   * properties with this flag set to false will not be saved
+   * @property {boolean}
+   */
+  allowSave: DS.attr('boolean', {defaultValue: true}),
+
+  /**
    * Don't show "Undo" for hosts on Installer Step7
    * if value is true
    * @property {boolean}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/templates/common/configs/config_recommendation_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/configs/config_recommendation_popup.hbs b/ambari-web/app/templates/common/configs/config_recommendation_popup.hbs
deleted file mode 100644
index 54fec3a..0000000
--- a/ambari-web/app/templates/common/configs/config_recommendation_popup.hbs
+++ /dev/null
@@ -1,68 +0,0 @@
-{{!
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance
-* with the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-}}
-
-<p>{{t installer.step7.popup.validation.warning.body}}</p>
-<div id="config-validation-warnings" class="limited-height-2">
-  <table class="table no-borders">
-    <thead>
-    <tr>
-      <th>{{t common.service}}</th>
-      <th>{{t common.property}}</th>
-      <th>{{t common.value}}</th>
-      <th>{{t common.description}}</th>
-    </tr>
-    </thead>
-    <tbody>
-      {{#each service in stepConfigs}}
-        {{#each property in service.configs}}
-          {{#if property.warn}}
-            <tr>
-              <td>{{property.serviceName}}</td>
-              <td>{{property.name}}</td>
-              <td>{{property.value}}</td>
-              <td>
-                <div class="property-message">{{property.warnMessage}}</div>
-                <div class="property-description">{{property.description}}</div>
-              </td>
-            </tr>
-          {{/if}}
-          {{#if property.error}}
-            {{#if property.isVisible}}
-              <tr>
-                <td>{{property.serviceName}}</td>
-                <td>{{property.name}}</td>
-                <td>{{property.value}}</td>
-                <td>
-                  <div class="property-message">{{property.errorMessage}}</div>
-                  <div class="property-description">{{property.description}}</div>
-                </td>
-              </tr>
-            {{/if}}
-          {{/if}}
-        {{/each}}
-      {{/each}}
-      {{#each message in configValidationGlobalMessage}}
-        <tr>
-          <td>{{message.serviceName}}</td>
-          <td>{{message.propertyName}}</td>
-          <td colspan="2">{{message.warnMessage}} in {{message.filename}}</td>
-        </tr>
-      {{/each}}
-    </tbody>
-  </table>
-</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs b/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
new file mode 100644
index 0000000..54fec3a
--- /dev/null
+++ b/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
@@ -0,0 +1,68 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<p>{{t installer.step7.popup.validation.warning.body}}</p>
+<div id="config-validation-warnings" class="limited-height-2">
+  <table class="table no-borders">
+    <thead>
+    <tr>
+      <th>{{t common.service}}</th>
+      <th>{{t common.property}}</th>
+      <th>{{t common.value}}</th>
+      <th>{{t common.description}}</th>
+    </tr>
+    </thead>
+    <tbody>
+      {{#each service in stepConfigs}}
+        {{#each property in service.configs}}
+          {{#if property.warn}}
+            <tr>
+              <td>{{property.serviceName}}</td>
+              <td>{{property.name}}</td>
+              <td>{{property.value}}</td>
+              <td>
+                <div class="property-message">{{property.warnMessage}}</div>
+                <div class="property-description">{{property.description}}</div>
+              </td>
+            </tr>
+          {{/if}}
+          {{#if property.error}}
+            {{#if property.isVisible}}
+              <tr>
+                <td>{{property.serviceName}}</td>
+                <td>{{property.name}}</td>
+                <td>{{property.value}}</td>
+                <td>
+                  <div class="property-message">{{property.errorMessage}}</div>
+                  <div class="property-description">{{property.description}}</div>
+                </td>
+              </tr>
+            {{/if}}
+          {{/if}}
+        {{/each}}
+      {{/each}}
+      {{#each message in configValidationGlobalMessage}}
+        <tr>
+          <td>{{message.serviceName}}</td>
+          <td>{{message.propertyName}}</td>
+          <td colspan="2">{{message.warnMessage}} in {{message.filename}}</td>
+        </tr>
+      {{/each}}
+    </tbody>
+  </table>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/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
new file mode 100644
index 0000000..a924c26
--- /dev/null
+++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
@@ -0,0 +1,29 @@
+<div class="alert alert-warning">
+  {{t popup.dependent.configs.title}}
+</div>
+<div id="config-validation-warnings" class="limited-height-2">
+  <table class="table no-borders">
+    <thead>
+    <tr>
+      <th>{{t popup.dependent.configs.table.saveProperty}}</th>
+      <th>{{t common.property}}</th>
+      <th>{{t common.fileName}}</th>
+      <th>{{t common.service}}</th>
+      <th>{{t popup.dependent.configs.table.currentValue}}</th>
+      <th>{{t popup.dependent.configs.table.recommendedValue}}</th>
+    </tr>
+    </thead>
+    <tbody>
+    {{#each config in view.parentView.configs}}
+      <tr>
+        <td>{{view Em.Checkbox checkedBinding="config.allowSave"}}</td>
+        <td>{{config.name}}</td>
+        <td>{{config.fileName}}</td>
+        <td>{{config.stackConfigProperty.serviceName}}</td>
+        <td>{{config.defaultValue}}</td>
+        <td>{{config.value}}</td>
+      </tr>
+    {{/each}}
+    </tbody>
+  </table>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/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 146ced3..a3f92e8 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -630,7 +630,7 @@ var urls = {
   },
 
   'configs.stack_configs.load.services': {
-    'real': '{stackVersionUrl}/services?StackServices/service_name.in({serviceList})?fields=configurations/*,StackServices/config_types/*',
+    'real': '{stackVersionUrl}/services?StackServices/service_name.in({serviceList})&fields=configurations/*,StackServices/config_types/*',
     'mock': '/data/stacks/HDP-2.2/configurations.json'
   },
 
@@ -661,6 +661,10 @@ var urls = {
     'mock': '/data/configurations/config_versions.json'
   },
 
+  'configs.config_versions.load.current_versions': {
+    'real': '/clusters/{clusterName}/configurations/service_config_versions?service_name.in({serviceNames})&group_id=-1&is_current=true&fields=*',
+    'mock': '/data/configurations/config_versions.json'
+  },
 
 
   'service.load_config_groups': {
@@ -1804,18 +1808,14 @@ var urls = {
 
 
   // TODO: merge with wizard.loadrecommendations query
-  'wizard.step7.loadrecommendations.configs': {
+  'config.recommendations': {
     'real': '{stackVersionUrl}/recommendations',
-    'mock': '/data/stacks/HDP-2.1/recommendations_configs.json',
+    'mock': '/data/configurations/recommendations/configuration_dependencies.json',
+    //'mock': '/data/stacks/HDP-2.1/recommendations_configs.json',
     'type': 'POST',
     'format': function (data) {
       return {
-        data: JSON.stringify({
-          hosts: data.hosts,
-          services: data.services,
-          recommendations: data.recommendations,
-          recommend: "configurations"
-        })
+        data: JSON.stringify(data.dataToSend)
       }
     }
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/utils/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js
index 77a05dd..5e158cf 100644
--- a/ambari-web/app/utils/config.js
+++ b/ambari-web/app/utils/config.js
@@ -1336,7 +1336,7 @@ App.config = Em.Object.create({
   textareaIntoFileConfigs: function (configs, filename) {
     var complexConfigName = this.get('complexConfigsTemplate').findProperty('filename', filename).name;
     var configsTextarea = configs.findProperty('name', complexConfigName);
-    if (configsTextarea) {
+    if (configsTextarea && !App.get('testMode')) {
       var properties = configsTextarea.get('value').split('\n');
 
       properties.forEach(function (_property) {
@@ -1911,6 +1911,22 @@ App.config = Em.Object.create({
   },
 
   /**
+   *
+   * @param serviceNames
+   * @returns {$.ajax}
+   */
+  loadConfigCurrentVersions: function (serviceNames) {
+    Em.assert('serviceNames should be not empty array', Em.isArray(serviceNames) && serviceNames.length > 0);
+    return App.ajax.send({
+      name: 'configs.config_versions.load.current_versions',
+      sender: this,
+      data: {
+        serviceNames: serviceNames.join(",")
+      },
+      success: 'saveConfigVersionsToModel'
+    });
+  },
+  /**
    * generate ajax info
    * @param {string} [serviceName=null]
    * @param {number} [configGroupId=null]

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 090d4ba..49d2dde 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -33,6 +33,7 @@ require('views/common/modal_popups/prompt_popup');
 require('views/common/modal_popups/reload_popup');
 require('views/common/modal_popups/cluster_check_popup');
 require('views/common/modal_popups/invalid_KDC_popup');
+require('views/common/modal_popups/dependent_configs_list_popup');
 require('views/common/editable_list');
 require('views/common/rolling_restart_view');
 require('views/common/select_custom_date_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/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
new file mode 100644
index 0000000..035700d
--- /dev/null
+++ b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
@@ -0,0 +1,66 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+/**
+ * Show confirmation popup
+ * @param {[Object]} configs
+ * we use this parameter to defer saving configs before we make some decisions.
+ * @param {$.Deferred} dfd
+ * @return {App.ModalPopup}
+ */
+App.showDependentConfigsPopup = function (configs, dfd) {
+  if (!configs || configs.length === 0) {
+    dfd.resolve();
+  }
+  return App.ModalPopup.show({
+    encodeBody: false,
+    primary: Em.I18n.t('common.save'),
+    secondary: Em.I18n.t('common.cancel'),
+    third: Em.I18n.t('common.discard'),
+    header: Em.I18n.t('popup.dependent.configs.header'),
+    classNames: ['full-width-modal'],
+    configs: configs,
+    bodyClass: Em.View.extend({
+      templateName: require('templates/common/modal_popups/dependent_configs_list')
+    }),
+    onPrimary: function () {
+      this.hide();
+      configs.filterProperty('allowSave', false).forEach(function(c) {
+        c.set('value', c.get('defaultValue'));
+      });
+      dfd.resolve();
+    },
+    onSecondary: function () {
+      App.get('router.mainServiceInfoConfigsController').set('isApplyingChanges', false);
+      dfd.reject();
+      this.hide();
+    },
+    onThird: function () {
+      App.get('router.mainServiceInfoConfigsController').set('isApplyingChanges', false);
+      App.get('router.mainServiceInfoConfigsController').set('preSelectedConfigVersion', null);
+      App.get('router.mainServiceInfoConfigsController').onConfigGroupChange();
+      dfd.reject();
+      this.hide();
+    },
+    onClose:  function () {
+      this.onSecondary();
+    }
+  });
+};

http://git-wip-us.apache.org/repos/asf/ambari/blob/b17747ec/ambari-web/test/controllers/main/service/info/config_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/info/config_test.js b/ambari-web/test/controllers/main/service/info/config_test.js
index 23a5b1c..b299187 100644
--- a/ambari-web/test/controllers/main/service/info/config_test.js
+++ b/ambari-web/test/controllers/main/service/info/config_test.js
@@ -1304,4 +1304,48 @@ describe("App.MainServiceInfoConfigsController", function () {
     });
   });
 
+
+  describe('#calculateDependentFileNames()', function() {
+
+    beforeEach(function() {
+      mainServiceInfoConfigsController.set('dependentFileNames', []);
+      App.resetDsStoreTypeMap(App.StackConfigProperty);
+      App.StackConfigProperty.createRecord({id: 'name1_site1', propertyDependedBy: [{
+        type: 'site2',
+        name: 'name2'
+      }]});
+      App.StackConfigProperty.createRecord({id: 'name2_site2', propertyDependedBy: [{
+          type: 'site3',
+          name: 'name3'
+        },
+        {
+          type: 'site4',
+          name: 'name4'
+        }]
+      });
+      App.StackConfigProperty.createRecord({id: 'name3_site3', propertyDependedBy: []});
+      App.StackConfigProperty.createRecord({id: 'name4_site4', propertyDependedBy: []});
+    });
+
+    it('adds all file names that need to be loaded to dependentFileNames', function() {
+      mainServiceInfoConfigsController.calculateDependentFileNames(App.StackConfigProperty.find('name1_site1'));
+      expect(mainServiceInfoConfigsController.get('dependentFileNames').toArray()).to.eql(["site2", "site3", "site4"]);
+    });
+  });
+
+  describe('#getServiceNamesForConfigs()', function() {
+    it('returns serviceNames which configs need to be loaded', function() {
+      mainServiceInfoConfigsController.set('content', {});
+      mainServiceInfoConfigsController.set('content.serviceName', 'currentService');
+      mainServiceInfoConfigsController.set('dependentFileNames', ['site1', 'site2']);
+      App.resetDsStoreTypeMap(App.StackService);
+      App.StackService.createRecord({id: 'service1', serviceName: 'service1', configTypes: {'site1':'site1'}});
+      App.StackService.createRecord({id: 'service2', serviceName: 'service2', configTypes: {'site1':'site1', 'site2': 'site2'}});
+      App.StackService.createRecord({id: 'service3', serviceName: 'service3', configTypes: {'site3':'site3'}});
+      expect(mainServiceInfoConfigsController.getServiceNamesForConfigs()).to.eql(['service1', 'service2', 'currentService']);
+    });
+
+
+  });
+
 });