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 2014/08/19 21:10:11 UTC

git commit: AMBARI-6922 FE: Ambari installer and service config page should validate configs by calling /validations. (ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk 4644a826b -> 818dc161f


AMBARI-6922 FE: Ambari installer and service config page should validate configs by calling /validations. (ababiichuk)


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

Branch: refs/heads/trunk
Commit: 818dc161fe41b7f9b92e67042b3840e4a8158737
Parents: 4644a82
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Tue Aug 19 22:03:23 2014 +0300
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Tue Aug 19 22:10:08 2014 +0300

----------------------------------------------------------------------
 ambari-web/app/controllers/installer.js         |  32 ++-
 .../controllers/main/service/info/configs.js    |  20 +-
 .../app/controllers/wizard/step5_controller.js  |  18 +-
 .../app/controllers/wizard/step6_controller.js  |  13 +-
 .../app/controllers/wizard/step7_controller.js  |  56 +----
 ambari-web/app/messages.js                      |   4 +
 ambari-web/app/mixins.js                        |   1 +
 ambari-web/app/mixins/common/serverValidator.js | 239 +++++++++++++++++++
 ambari-web/app/models/service_config.js         |  15 +-
 ambari-web/app/models/stack_service.js          |   3 +
 ambari-web/app/routes/installer.js              |  11 +-
 ambari-web/app/utils/blueprint.js               |  81 +++++++
 ambari-web/app/utils/config.js                  |  20 +-
 ambari-web/test/utils/blueprint_test.js         | 137 +++++++++++
 14 files changed, 560 insertions(+), 90 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/controllers/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js
index 03c5afb..8b1fa1f 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -38,6 +38,15 @@ App.InstallerController = App.WizardController.extend({
     slaveGroupProperties: null,
     stacks: null,
     clients:[],
+    /**
+     * recommendations for host groups loaded from server
+     */
+    recommendations: null,
+    /**
+     * recommendationsHostGroups - current component assignment after 5 and 6 steps
+     * (uses for host groups validation and to load recommended configs)
+     */
+    recommendationsHostGroups: null,
     controllerName: 'installerController'
   }),
 
@@ -64,7 +73,10 @@ App.InstallerController = App.WizardController.extend({
     'stacksVersions',
     'currentStep',
     'serviceInfo',
-    'hostInfo'
+    'hostInfo',
+    'recommendations',
+    'recommendationsHostGroups',
+    'recommendationsConfigs'
   ],
 
   init: function () {
@@ -467,6 +479,18 @@ App.InstallerController = App.WizardController.extend({
     this.set("content.masterComponentHosts", masterComponentHosts);
   },
 
+  loadRecommendations: function() {
+    this.set("content.recommendations", this.getDBProperty('recommendations'));
+  },
+
+  loadCurrentHostGroups: function() {
+    this.set("content.recommendationsHostGroups", this.getDBProperty('recommendationsHostGroups'));
+  },
+
+  loadRecommendationsConfigs: function() {
+    App.router.set("wizardStep7Controller.recommendationsConfigs", this.getDBProperty('recommendationsConfigs'));
+  },
+
   /**
    * Load master component hosts data for using in required step controllers
    */
@@ -685,6 +709,7 @@ App.InstallerController = App.WizardController.extend({
         callback: function () {
           this.loadMasterComponentHosts();
           this.loadConfirmedHosts();
+          this.loadRecommendations();
         }
       }
     ],
@@ -694,6 +719,7 @@ App.InstallerController = App.WizardController.extend({
         callback: function () {
           this.loadSlaveComponentHosts();
           this.loadClients();
+          this.loadRecommendations();
         }
       }
     ],
@@ -703,6 +729,8 @@ App.InstallerController = App.WizardController.extend({
         callback: function () {
           this.loadServiceConfigGroups();
           this.loadServiceConfigProperties();
+          this.loadCurrentHostGroups();
+          this.loadRecommendationsConfigs();
         }
       }
     ]
@@ -739,7 +767,7 @@ App.InstallerController = App.WizardController.extend({
    * Clear loaded recommendations
    */
   clearRecommendations: function() {
-    this.set('recommendations', undefined)
+    this.set('content.recommendations', undefined)
   }
 });
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/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 a856d1e..a1b3677 100644
--- a/ambari-web/app/controllers/main/service/info/configs.js
+++ b/ambari-web/app/controllers/main/service/info/configs.js
@@ -21,7 +21,7 @@ require('controllers/wizard/slave_component_groups_controller');
 var batchUtils = require('utils/batch_scheduled_requests');
 var lazyLoading = require('utils/lazy_loading');
 
-App.MainServiceInfoConfigsController = Em.Controller.extend({
+App.MainServiceInfoConfigsController = Em.Controller.extend(App.ServerValidatorMixin, {
   name: 'mainServiceInfoConfigsController',
   isHostsConfigsPage: false,
   forceTransition: false,
@@ -1091,15 +1091,17 @@ App.MainServiceInfoConfigsController = Em.Controller.extend({
       (serviceName !== 'HDFS' && this.get('content.isStopped') === true) ||
       ((serviceName === 'HDFS') && this.get('content.isStopped') === true && (!App.Service.find().someProperty('id', 'MAPREDUCE') || App.Service.find('MAPREDUCE').get('isStopped')))) {
 
-      if (this.isDirChanged()) {
-        App.showConfirmationPopup(function () {
+      this.serverSideValidation().done(function() {
+        if (self.isDirChanged()) {
+          App.showConfirmationPopup(function () {
+            self.saveConfigs();
+          }, Em.I18n.t('services.service.config.confirmDirectoryChange').format(displayName), function () {
+            self.set('isApplyingChanges', false)
+          });
+        } else {
           self.saveConfigs();
-        }, Em.I18n.t('services.service.config.confirmDirectoryChange').format(displayName), function () {
-          self.set('isApplyingChanges', false)
-        });
-      } else {
-        this.saveConfigs();
-      }
+        }
+      });
     } else {
       status = 'started';
       if (this.get('content.serviceName') !== 'HDFS' || (this.get('content.serviceName') === 'HDFS' && !App.Service.find().someProperty('id', 'MAPREDUCE'))) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/controllers/wizard/step5_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step5_controller.js b/ambari-web/app/controllers/wizard/step5_controller.js
index b82fa1c..7df1b8b 100644
--- a/ambari-web/app/controllers/wizard/step5_controller.js
+++ b/ambari-web/app/controllers/wizard/step5_controller.js
@@ -212,11 +212,11 @@ App.WizardStep5Controller = Em.Controller.extend({
       return false;
     }
 
-    if (App.supports.serverRecommendValidate) {
+    if (App.get('supports.serverRecommendValidate')) {
       self.set('submitDisabled', true);
 
       // reset previous recommendations
-      App.router.set('installerController.recommendations', null);
+      this.set('content.recommendations', null);
 
       if (self.get('servicesMasters').length === 0) {
         return;
@@ -375,7 +375,7 @@ App.WizardStep5Controller = Em.Controller.extend({
     console.log("WizardStep5Controller: Loading step5: Assign Masters");
     this.clearStep();
     this.renderHostInfo();
-    if (App.supports.serverRecommendValidate ) {
+    if (App.get('supports.serverRecommendValidate')) {
       this.loadComponentsRecommendationsFromServer(this.loadStepCallback);
     } else {
       this.loadComponentsRecommendationsLocally(this.loadStepCallback);
@@ -478,7 +478,7 @@ App.WizardStep5Controller = Em.Controller.extend({
   loadComponentsRecommendationsFromServer: function(callback, includeMasters) {
     var self = this;
 
-    if (App.router.get('installerController.recommendations')) {
+    if (this.get('content.recommendations')) {
       // Don't do AJAX call if recommendations has been already received
       // But if user returns to previous step (selecting services), stored recommendations will be cleared in routers' next handler and AJAX call will be made again
       callback(self.createComponentInstallationObjects(), self);
@@ -523,7 +523,7 @@ App.WizardStep5Controller = Em.Controller.extend({
 
   /**
    * Create components for displaying component-host comboboxes in UI assign dialog
-   * expects installerController.recommendations will be filled with recommendations API call result
+   * expects content.recommendations will be filled with recommendations API call result
    * @return {Object[]}
    */
   createComponentInstallationObjects: function() {
@@ -538,7 +538,7 @@ App.WizardStep5Controller = Em.Controller.extend({
 
     var masterHosts = self.get('content.masterComponentHosts'); //saved to local storage info
     var selectedNotInstalledServices = self.get('content.services').filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName');
-    var recommendations = App.router.get('installerController.recommendations');
+    var recommendations = this.get('content.recommendations');
 
     var resultComponents = [];
     var multipleComponentHasBeenAdded = {};
@@ -616,7 +616,7 @@ App.WizardStep5Controller = Em.Controller.extend({
    * @method loadRecommendationsSuccessCallback
    */
   loadRecommendationsSuccessCallback: function (data) {
-    App.router.set('installerController.recommendations', data.resources[0].recommendations);
+    this.set('content.recommendations', data.resources[0].recommendations);
   },
 
   /**
@@ -1025,7 +1025,7 @@ App.WizardStep5Controller = Em.Controller.extend({
     // load recommendations with partial request
     self.loadComponentsRecommendationsFromServer(function() {
       // For validation use latest received recommendations because ir contains current master layout and recommended slave/client layout
-      self.validate(App.router.get('installerController.recommendations'), function() {
+      self.validate(self.get('content.recommendations'), function() {
         if (callback) {
           callback();
         }
@@ -1047,7 +1047,7 @@ App.WizardStep5Controller = Em.Controller.extend({
         }
       };
 
-      if (App.supports.serverRecommendValidate ) {
+    if (App.get('supports.serverRecommendValidate')) {
         self.recommendAndValidate(function() {
           goNextStepIfValid();
         });

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/controllers/wizard/step6_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js
index a74f020..78c1254 100644
--- a/ambari-web/app/controllers/wizard/step6_controller.js
+++ b/ambari-web/app/controllers/wizard/step6_controller.js
@@ -377,7 +377,7 @@ App.WizardStep6Controller = Em.Controller.extend({
     var clientHeaders = headers.findProperty('name', 'CLIENT');
     var slaveComponents = this.get('content.slaveComponentHosts');
     if (!slaveComponents) { // we are at this page for the first time
-      if (!App.supports.serverRecommendValidate) {
+      if (!App.get('supports.serverRecommendValidate')) {
         hostsObj.forEach(function (host) {
           var checkboxes = host.get('checkboxes');
           checkboxes.setEach('checked', !host.hasMaster);
@@ -393,7 +393,7 @@ App.WizardStep6Controller = Em.Controller.extend({
           lastHost.get('checkboxes').setEach('checked', true);
         }
       } else {
-        var recommendations = App.router.get('installerController.recommendations');
+        var recommendations = this.get('content.recommendations');
         // Get all host-component pairs from recommendations
         var componentHostPairs = recommendations.blueprint.host_groups.map(function (group) {
           return group.components.map(function (component) {
@@ -495,7 +495,7 @@ App.WizardStep6Controller = Em.Controller.extend({
 
   callValidation: function (successCallback) {
     var self = this;
-    if (App.supports.serverRecommendValidate) {
+    if (App.get('supports.serverRecommendValidate')) {
       self.callServerSideValidation(successCallback);
     } else {
       var res = self.callClientSideValidation();
@@ -540,7 +540,7 @@ App.WizardStep6Controller = Em.Controller.extend({
 
       var invisibleComponents = invisibleMasters.concat(invisibleSlaves).concat(alreadyInstalledClients);
 
-      var invisibleBlueprint = blueprintUtils.filterByComponents(App.router.get('installerController.recommendations'), invisibleComponents);
+      var invisibleBlueprint = blueprintUtils.filterByComponents(this.get('content.recommendations'), invisibleComponents);
       masterBlueprint = blueprintUtils.mergeBlueprints(masterBlueprint, invisibleBlueprint);
     } else if (this.get('isAddHostWizard')) {
       masterBlueprint = self.getMasterSlaveBlueprintForAddHostWizard();
@@ -548,6 +548,9 @@ App.WizardStep6Controller = Em.Controller.extend({
       slaveBlueprint = blueprintUtils.addComponentsToBlueprint(slaveBlueprint, invisibleSlaves);
     }
 
+    var bluePrintsForValidation = blueprintUtils.mergeBlueprints(masterBlueprint, slaveBlueprint);
+    this.set('content.recommendationsHostGroups', bluePrintsForValidation);
+
     App.ajax.send({
       name: 'config.validations',
       sender: self,
@@ -556,7 +559,7 @@ App.WizardStep6Controller = Em.Controller.extend({
         hosts: hostNames,
         services: services,
         validate: 'host_groups',
-        recommendations: blueprintUtils.mergeBlueprints(masterBlueprint, slaveBlueprint)
+        recommendations: bluePrintsForValidation
       },
       success: 'updateValidationsSuccessCallback'
     }).

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/controllers/wizard/step7_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js
index bb222b5..6bf145c 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -29,7 +29,7 @@ var stringUtils = require('utils/string_utils');
  *
  */
 
-App.WizardStep7Controller = Em.Controller.extend({
+App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
 
   name: 'wizardStep7Controller',
 
@@ -113,8 +113,6 @@ App.WizardStep7Controller = Em.Controller.extend({
 
   serviceConfigsData: require('data/service_configs'),
 
-  recommendedConfigs: null,
-
   /**
    * Are advanced configs loaded
    * @type {bool}
@@ -600,7 +598,7 @@ App.WizardStep7Controller = Em.Controller.extend({
     var s = App.StackService.find(component.get('serviceName')),
       defaultGroupSelected = component.get('selectedConfigGroup.isDefault');
 
-    if(!App.supports.serverRecommendValidate) {
+    if(!App.get('supports.serverRecommendValidate')) {
       if (s && s.get('configsValidator')) {
         var recommendedDefaults = this._getRecommendedDefaultsForComponent(component.get('serviceName'));
         s.get('configsValidator').set('recommendedDefaults', recommendedDefaults);
@@ -723,8 +721,8 @@ App.WizardStep7Controller = Em.Controller.extend({
     }
     //STEP 6: Distribute configs by service and wrap each one in App.ServiceConfigProperty (configs -> serviceConfigs)
     var self = this;
-    if (App.supports.serverRecommendValidate) {
-      this.loadDefaultConfigs(function() {
+    if (App.get('supports.serverRecommendValidate')) {
+      this.loadServerSideConfigsRecommendations().complete(function() {
         self.setStepConfigs(configs, storedConfigs);
         self.checkHostOverrideInstaller();
         self.activateSpecialConfigs();
@@ -771,7 +769,7 @@ App.WizardStep7Controller = Em.Controller.extend({
       masterComponentHosts: this.get('wizardController.content.masterComponentHosts'),
       slaveComponentHosts: this.get('wizardController.content.slaveComponentHosts')
     };
-    var serviceConfigs = App.config.renderConfigs(configs, storedConfigs, this.get('allSelectedServiceNames'), this.get('installedServiceNames'), localDB, this.get('recommendedConfigs'));
+    var serviceConfigs = App.config.renderConfigs(configs, storedConfigs, this.get('allSelectedServiceNames'), this.get('installedServiceNames'), localDB, this.get('recommendationsConfigs'));
     if (this.get('wizardController.name') === 'addServiceController') {
       serviceConfigs.setEach('showConfig', true);
       serviceConfigs.setEach('selected', false);
@@ -930,41 +928,6 @@ App.WizardStep7Controller = Em.Controller.extend({
     }
   },
 
-  loadDefaultConfigs: function(callback) {
-    var selectedServices = App.StackService.find().filterProperty('isSelected').mapProperty('serviceName');
-    var installedServices = App.StackService.find().filterProperty('isInstalled').mapProperty('serviceName');
-    var services = installedServices.concat(selectedServices).uniq();
-    this.set('isDefaultsLoaded', false);
-    var hostNames = Object.keys(this.get('content.hosts'));
-    App.ajax.send({
-      'name': 'wizard.step7.loadrecommendations.configs',
-      'sender': this,
-      'data': {
-        stackVersionUrl: App.get('stackVersionURL'),
-        hosts: hostNames,
-        services: services,
-        recommendations: App.router.get('installerController.recommendations')
-      },
-      'success': 'loadDefaultConfigsSuccess'
-    })
-    .retry({
-      times: App.maxRetries,
-      timeout: App.timeout
-    })
-    .then(function () {
-      callback();
-    }, function () {
-        App.showReloadPopup();
-        console.log('Load recommendations failed');
-       });
-  },
-
-  loadDefaultConfigsSuccess: function(data) {
-    if (!data) {
-      console.warn('error while loading default config values');
-    }
-    this.set("recommendedConfigs", Em.get(data.resources[0] , "recommendations.blueprint.configurations"));
-  },
   /**
    * Check if Oozie or Hive use existing database then need
    * to restore missed properties
@@ -1360,12 +1323,15 @@ App.WizardStep7Controller = Em.Controller.extend({
    * @method submit
    */
   submit: function () {
+    if (this.get('isSubmitDisabled')) {
+      return;
+    }
     var _this = this;
-    if (!this.get('isSubmitDisabled')) {
-      this.checkDatabaseConnectionTest().done(function () {
+    this.serverSideValidation().done(function () {
+      _this.checkDatabaseConnectionTest().done(function () {
         _this.resolveHiveMysqlDatabase();
       });
-    }
+    });
   }
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index cd25878..989a657 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -648,6 +648,10 @@ Em.I18n.translations = {
   'installer.step7.popup.mySQLWarning.confirmation.body': 'You will be brought back to the \"Assign Masters\" step and will lose all your current customizations. Are you sure?',
   'installer.step7.popup.database.connection.header': 'Database Connectivity Warning',
   'installer.step7.popup.database.connection.body': 'You have not run or passed the database connection test for: {0}. It is highly recommended that you pass the connection test before proceeding to prevent failures during deployment.',
+  'installer.step7.popup.validation.failed.header': 'Validation failed.',
+  'installer.step7.popup.validation.failed.body': 'Some services are not properly configured. You have to change the highlighted configs according to the recommended values.',
+  'installer.step7.popup.validation.request.failed.body': 'Config validaition failed.',
+  'installer.step7.popup.validation.warning.body': 'Some services are not properly configured. Recommended to change the highlighted configs. Are you sure you want to proceed witout changing configs?',
 
   'installer.step7.oozie.database.new': 'New Derby Database',
   'installer.step7.hive.database.new': 'New MySQL Database',

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/mixins.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins.js b/ambari-web/app/mixins.js
index 3384391..f087569 100644
--- a/ambari-web/app/mixins.js
+++ b/ambari-web/app/mixins.js
@@ -21,6 +21,7 @@
 
 require('mixins/common/localStorage');
 require('mixins/common/userPref');
+require('mixins/common/serverValidator');
 require('mixins/models/service_mixin');
 require('mixins/common/tableServerProvider');
 require('mixins/common/table_server_mixin');

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/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
new file mode 100644
index 0000000..a4be042
--- /dev/null
+++ b/ambari-web/app/mixins/common/serverValidator.js
@@ -0,0 +1,239 @@
+/**
+ * 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');
+var blueprintUtils = require('utils/blueprint');
+
+App.ServerValidatorMixin = Em.Mixin.create({
+
+  /**
+   * @type {bool} set true if at leasst one config has error
+   */
+  configValidationError: false,
+
+  /**
+   * @type {bool} set true if at leasst one config has warning
+   */
+  configValidationWarning: false,
+
+  /**
+   * @type {bool} set true if at leasst one config has warning
+   */
+  configValidationFailed: false,
+
+  /**
+   * recommendation configs loaded from server
+   * (used only during install)
+   * @type {Object}
+   */
+  recommendationsConfigs: null,
+
+  /**
+   * by default loads data from model otherwise must be overridden as computed property
+   * refer to \assets\data\stacks\HDP-2.1\recommendations_configs.json to learn structure
+   * (shouldn't contain configurations filed)
+   * @type {Object}
+   */
+  hostNames: function() {
+    return this.get('content.hosts')
+        ? Object.keys(this.get('content.hosts'))
+        : App.HostComponent.find().mapProperty('hostName').uniq();
+  }.property('content.hosts'),
+
+
+  /**
+   * by default loads data from model otherwise must be overridden as computed property
+   * @type {Array} - of strings (serviceNames)
+   */
+  serviceNames: function() {
+    return this.get('content.serviceName')
+        ? [this.get('content.serviceName')]
+        : App.StackService.find().filter(function(s){return s.get('isSelected') || s.get('isInstalled')}).mapProperty('serviceName');
+  }.property('content.serviceName'),
+
+  /**
+   * by default loads data from model otherwise must be overridden as computed property
+   * filter services that support server validation and concat with misc configs if Installer or current service
+   * @type {Array} - of objects (services)
+   */
+  services: function() {
+    return this.get('content.serviceName')
+        ? [App.StackService.find(this.get('content.serviceName'))]
+        : App.StackService.find().filter(function(s){
+          return s.get('allowServerValidator') && (s.get('isSelected') || s.get('isInsalled'))
+        }).concat(require("data/service_configs"));
+  }.property('content.serviceName'),
+
+  /**
+   * by default loads data from model otherwise must be overridden as computed property
+   * can be used for service|host configs pages
+   * @type {Array} of strings (hostNames)
+   */
+  hostGroups: function() {
+    return this.get('content.recommendationsHostGroups') || blueprintUtils.generateHostGroups(this.get('hostNames'), App.HostComponent.find());
+  }.property('content.recommendationsHostGroups', 'hostNames'),
+
+  /**
+   * controller that is child of this mixis has to contain stepConfigs
+   * @type {Array}
+   */
+  stepConfigs: null,
+
+  /**
+   * @method loadServerSideConfigsRecommendations
+   * laod recommendations from server
+   * (used only during install)
+   * @returns {*}
+   */
+  loadServerSideConfigsRecommendations: function() {
+    if (this.get('recommendationsConfigs') || !App.get('supports.serverRecommendValidate')) {
+      return $.Deferred().resolve();
+    }
+    return App.ajax.send({
+      'name': 'wizard.step7.loadrecommendations.configs',
+      'sender': this,
+      'data': {
+        stackVersionUrl: App.get('stackVersionURL'),
+        hosts: this.get('hostNames'),
+        services: this.get('serviceNames'),
+        recommendations: this.get('hostGroups')
+      },
+      'success': 'loadRecommendationsSuccess',
+      'error': 'loadRecommendationsError'
+    });
+  },
+
+  /**
+   * @method loadRecommendationsSuccess
+   * success callback after loading recommendations
+   * (used only during install)
+   * @param data
+   */
+  loadRecommendationsSuccess: function(data) {
+    if (!data) {
+      console.warn('error while loading default config values');
+    }
+    this.set("recommendationsConfigs", Em.get(data.resources[0] , "recommendations.blueprint.configurations"));
+  },
+
+  loadRecommendationsError: function() {
+    console.error('Load recommendations failed');
+  },
+
+  /**
+   * @method serverSideValidation
+   * send request to validate configs
+   * @returns {*}
+   */
+  serverSideValidation: function() {
+    var self = this;
+    var deferred = $.Deferred();
+    if (!App.get('supports.serverRecommendValidate')) {
+      deferred.resolve();
+      return deferred;
+    }
+    var recommendations = this.get('hostGroups');
+    recommendations.blueprint.configurations = blueprintUtils.buildConfisJSON(this.get('services'), this.get('stepConfigs'));
+    App.ajax.send({
+      name: 'config.validations',
+      sender: this,
+      data: {
+        stackVersionUrl: App.get('stackVersionURL'),
+        hosts: this.get('hostNames'),
+        services: this.get('serviceNames'),
+        validate: 'configurations',
+        recommendations: recommendations
+      },
+      success: 'validationSuccess',
+      error: 'validationError'
+    }).complete(function() {
+      self.warnUser(deferred);
+    });
+    return deferred;
+  },
+
+
+  /**
+   * @method validationSuccess
+   * success callback after getting responce from server
+   * go through the step configs and set warn and error messages
+   * @param data
+   */
+  validationSuccess: function(data) {
+    var self = this;
+    self.set('configValidationError', false);
+    self.set('configValidationWarning', false);
+    self.set('configValidationFailed', false);
+    data.resources.forEach(function(r) {
+      r.items.forEach(function(item){
+        if (item.type == "configuration") {
+          self.get('stepConfigs').forEach(function(service) {
+            service.get('configs').forEach(function(property) {
+              if ((property.get('filename') == item['config-type'] + '.xml') && (property.get('name') == item['config-name'])) {
+                if (item.level == "ERROR") {
+                  self.set('configValidationError', true);
+                  property.set('errorMessage', item.message);
+                  property.set('error', true);
+                } else if (item.level == "ERROR") {
+                  self.set('configValidationWarning', true);
+                  property.set('warnMessage', item.message);
+                  property.set('warn', true);
+                }
+              }
+            });
+          })
+        }
+      });
+    });
+  },
+
+  validationError: function() {
+    this.set('configValidationFailed', true);
+    console.error('config validation failed');
+  },
+
+
+  /**
+   * warn user if some errors or warning were
+   * in seting up configs otherwise go to the nex operation
+   * @param deferred
+   * @returns {*}
+   */
+  warnUser: function(deferred) {
+    var self = this;
+    if (this.get('configValidationFailed')) {
+      this.set('isSubmitDisabled', false);
+      this.set("isApplyingChanges", false);
+      return App.showAlertPopup(Em.I18n.t('installer.step7.popup.validation.failed.header'), Em.I18n.t('installer.step7.popup.validation.request.failed.body'));
+    } else if (this.get('configValidationError')) {
+      this.set("isApplyingChanges", false);
+      this.set('isSubmitDisabled', true);
+      return App.showAlertPopup(Em.I18n.t('installer.step7.popup.validation.failed.header'), Em.I18n.t('installer.step7.popup.validation.failed.body'));
+    } else if (this.get('configValidationWarning')) {
+      this.set('isSubmitDisabled', true);
+      this.set("isApplyingChanges", false);
+      return App.showConfirmationPopup(function () {
+        self.set('isSubmitDisabled', false);
+        self.set("isApplyingChanges", true);
+        deferred.resolve();
+      }, Em.I18n.t('installer.step7.popup.validation.warning.body'));
+    } else {
+      deferred.resolve();
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/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 7ef67f3..2df2ad7 100644
--- a/ambari-web/app/models/service_config.js
+++ b/ambari-web/app/models/service_config.js
@@ -844,13 +844,14 @@ App.ServiceConfigProperty = Ember.Object.extend({
         }
       }
     }
-    
-    var serviceValidator = this.get('serviceValidator');
-    if (serviceValidator!=null) {
-      var validationIssue = serviceValidator.validateConfig(this);
-      if (validationIssue) {
-    	this.set('warnMessage', validationIssue);
-    	isWarn = true;
+    if (!App.get('supports.serverRecommendValidate')) {
+      var serviceValidator = this.get('serviceValidator');
+      if (serviceValidator!=null) {
+        var validationIssue = serviceValidator.validateConfig(this);
+        if (validationIssue) {
+          this.set('warnMessage', validationIssue);
+          isWarn = true;
+        }
       }
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/models/stack_service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/stack_service.js b/ambari-web/app/models/stack_service.js
index c599c33..0b489f8 100644
--- a/ambari-web/app/models/stack_service.js
+++ b/ambari-web/app/models/stack_service.js
@@ -171,6 +171,9 @@ App.StackService = DS.Model.extend(App.ServiceModelMixin, {
     return defaultConfigsHandler && defaultConfigsHandler.configsValidator;
   }.property('serviceName'),
 
+  allowServerValidator: function() {
+    return ["YARN", "STORM", "MAPREDUCE2", "HIVE", "TEZ"].contains(this.get('serviceName'));
+  }.property('serviceName'),
   /**
    * configCategories are fetched from  App.StackService.configCategories.
    * Also configCategories that does not match any serviceComponent of a service and not included in the permissible default pattern are omitted

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/routes/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js
index 7abfe96..0a5e46e 100644
--- a/ambari-web/app/routes/installer.js
+++ b/ambari-web/app/routes/installer.js
@@ -250,6 +250,7 @@ module.exports = Em.Route.extend({
       controller.saveClients(wizardStep4Controller);
 
       controller.clearRecommendations(); // Force reload recommendation between steps 4 and 5
+      controller.setDBProperty('recommendations', undefined);
       controller.setDBProperty('masterComponentHosts', undefined);
       router.transitionTo('step5');
     }
@@ -273,6 +274,7 @@ module.exports = Em.Route.extend({
       var wizardStep5Controller = router.get('wizardStep5Controller');
       controller.saveMasterComponentHosts(wizardStep5Controller);
       controller.setDBProperty('slaveComponentHosts', undefined);
+      controller.setDBProperty('recommendations', wizardStep5Controller.get('content.recommendations'));
       router.transitionTo('step6');
     }
   }),
@@ -302,6 +304,8 @@ module.exports = Em.Route.extend({
         controller.setDBProperty('serviceConfigProperties', null);
         controller.setDBProperty('advancedServiceConfig', null);
         controller.setDBProperty('serviceConfigGroups', null);
+        controller.setDBProperty('recommendationsHostGroups', wizardStep6Controller.get('content.recommendationsHostGroups'));
+        controller.setDBProperty('recommendationsConfigs', null);
         controller.loadAdvancedConfigs(wizardStep7Controller);
         router.transitionTo('step7');
       }
@@ -326,12 +330,13 @@ module.exports = Em.Route.extend({
     },
     back: Em.Router.transitionTo('step6'),
     next: function (router) {
-      var installerController = router.get('installerController');
+      var controller = router.get('installerController');
       var wizardStep7Controller = router.get('wizardStep7Controller');
-      installerController.saveServiceConfigProperties(wizardStep7Controller);
+      controller.saveServiceConfigProperties(wizardStep7Controller);
       if (App.supports.hostOverridesInstaller) {
-        installerController.saveServiceConfigGroups(wizardStep7Controller);
+        controller.saveServiceConfigGroups(wizardStep7Controller);
       }
+      controller.setDBProperty('recommendationsConfigs', wizardStep7Controller.get('recommendationsConfigs'));
       router.transitionTo('step8');
     }
   }),

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/utils/blueprint.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/blueprint.js b/ambari-web/app/utils/blueprint.js
index b56ceca..f8ec9c2 100644
--- a/ambari-web/app/utils/blueprint.js
+++ b/ambari-web/app/utils/blueprint.js
@@ -185,5 +185,86 @@ module.exports = {
     });
 
     return res;
+  },
+
+  /**
+   * @method buildConfisJSON - generet JSON according to blueprint format
+   * @param {Em.Array} stepConfigs - array of Ember Objects
+   * @param {Array} services
+   * @returns {Object}
+   * Example:
+   * {
+   *   "yarn-env": {
+   *     "properties": {
+   *       "content": "some value",
+   *       "yarn_heapsize": "1024",
+   *       "resourcemanager_heapsize": "1024",
+   *     }
+   *   },
+   *   "yarn-log4j": {
+   *     "properties": {
+   *       "content": "some other value"
+   *     }
+   *   }
+   * }
+   */
+  buildConfisJSON: function(services, stepConfigs) {
+    var configurations = {};
+    services.forEach(function(service) {
+      var config = stepConfigs.findProperty('serviceName', service.get('serviceName'));
+      if (config && service.get('configTypes')) {
+        Object.keys(service.get('configTypes')).forEach(function(type) {
+          configurations[type] = {
+            properties: {}
+          }
+        });
+        config.get('configs').forEach(function(property){
+          if (configurations[property.get('filename').replace('.xml','')]){
+            configurations[property.get('filename').replace('.xml','')]['properties'][property.get('name')] = property.get('value');
+          } else {
+            console.warn(property.get('name') + " from " + property.get('filename') + " can't be validate");
+          }
+        });
+      }
+    });
+    return configurations;
+  },
+
+  /**
+   * @method generateHostGroups
+   * @param {Array} hostNames - list of all hostNames
+   * @param {Array} hostComponents - list of all hostComponents
+   * @returns {{blueprint: {host_groups: Array}, blueprint_cluster_binding: {host_groups: Array}}}
+   */
+  generateHostGroups: function(hostNames, hostComponents) {
+    var recommendations = {
+      blueprint: {
+        host_groups: []
+      },
+      blueprint_cluster_binding: {
+        host_groups: []
+      }
+    };
+
+    for (var i = 1; i <= hostNames.length; i++) {
+      var host_group = {
+        name: "host-group-" + i,
+        components: []
+      };
+      var hcFiltered = hostComponents.filterProperty('hostName', hostNames[i-1]).mapProperty('componentName');
+      for (var j = 0; j < hcFiltered.length; j++) {
+        host_group.components.push({
+          name: hcFiltered[j]
+        });
+      }
+      recommendations.blueprint.host_groups.push(host_group);
+      recommendations.blueprint_cluster_binding.host_groups.push({
+        name: "host-group-" + i,
+        hosts: [{
+          fqdn: hostNames[i-1]
+        }]
+      });
+    }
+    return recommendations;
   }
 };

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/app/utils/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js
index f576f38..62db148 100644
--- a/ambari-web/app/utils/config.js
+++ b/ambari-web/app/utils/config.js
@@ -690,8 +690,8 @@ App.config = Em.Object.create({
 
       // Use calculated default values for some configs
       var recommendedDefaults = {};
-      if (App.supports.serverRecommendValidate) {
-        if (!storedConfigs && service.get('configTypes')) {
+      if (App.get('supports.serverRecommendValidate')) {
+        if (!storedConfigs && service.get('configTypes') && service.get('allowServerValidator')) {
           Object.keys(service.get('configTypes')).forEach(function (type) {
             if (!recommended || !recommended[type]) {
               return;
@@ -727,14 +727,14 @@ App.config = Em.Object.create({
             }
           });
         }
-      }
-      if (service.get('configsValidator')) {
-        service.get('configsValidator').set('recommendedDefaults', recommendedDefaults);
-        var validators = service.get('configsValidator').get('configValidators');
-        for (var validatorName in validators) {
-          var c = configsByService.findProperty('name', validatorName);
-          if (c) {
-            c.set('serviceValidator', service.get('configsValidator'));
+        if (service.get('configsValidator')) {
+          service.get('configsValidator').set('recommendedDefaults', recommendedDefaults);
+          var validators = service.get('configsValidator').get('configValidators');
+          for (var validatorName in validators) {
+            var c = configsByService.findProperty('name', validatorName);
+            if (c) {
+              c.set('serviceValidator', service.get('configsValidator'));
+            }
           }
         }
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/818dc161/ambari-web/test/utils/blueprint_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/utils/blueprint_test.js b/ambari-web/test/utils/blueprint_test.js
index e527a09..1f8d803 100644
--- a/ambari-web/test/utils/blueprint_test.js
+++ b/ambari-web/test/utils/blueprint_test.js
@@ -276,4 +276,141 @@ describe('utils/blueprint', function() {
       );
     });
   });
+
+  describe('#buildConfisJSON', function () {
+    var tests = [
+      {
+        "services": [
+          Em.Object.create({
+            serviceName: "YARN",
+            configTypes: {
+              "yarn-site": {},
+              "yarn-env": {}
+            },
+            allowServerValidator: true,
+            isInstalled: true
+          })
+        ],
+        "stepConfigs": [
+          Em.Object.create({
+            serviceName: "YARN",
+            configs: [
+              Em.Object.create({
+                name: "p1",
+                value: "v1",
+                filename: "yarn-site.xml"
+              }),
+              Em.Object.create({
+                name: "p2",
+                value: "v2",
+                filename: "yarn-site.xml"
+              }),
+              Em.Object.create({
+                name: "p3",
+                value: "v3",
+                filename: "yarn-env.xml"
+              })
+            ]
+          })
+        ],
+        "configurations": {
+          "yarn-site": {
+            "properties": {
+              "p1": "v1",
+              "p2": "v2"
+            }
+          },
+          "yarn-env": {
+            "properties": {
+              "p3": "v3"
+            }
+          }
+        }
+      }
+    ];
+    tests.forEach(function (test) {
+      it("generate configs for request (use in validation)", function () {
+        expect(blueprintUtils.buildConfisJSON(test.services, test.stepConfigs)).to.eql(test.configurations);
+      });
+    });
+  });
+
+  describe('#generateHostGroups', function () {
+    var tests = [
+      {
+        "hostNames": ["host1", "host2"],
+        "hostComponents": [
+          Em.Object.create({
+            componentName: "C1",
+            hostName: "host1"
+          }),
+          Em.Object.create({
+            componentName: "C2",
+            hostName: "host1"
+          }),
+          Em.Object.create({
+            componentName: "C1",
+            hostName: "host2"
+          }),
+          Em.Object.create({
+            componentName: "C3",
+            hostName: "host2"
+          })
+        ],
+        result: {
+          blueprint: {
+            host_groups: [
+              {
+                name: "host-group-1",
+                "components": [
+                  {
+                    "name": "C1"
+                  },
+                  {
+                    "name": "C2"
+                  }
+                ]
+              },
+              {
+                name: "host-group-2",
+                "components": [
+                  {
+                    "name": "C1"
+                  },
+                  {
+                    "name": "C3"
+                  }
+                ]
+              }
+            ]
+          },
+          blueprint_cluster_binding: {
+            host_groups: [
+              {
+                "name": "host-group-1",
+                "hosts": [
+                  {
+                    "fqdn": "host1"
+                  }
+                ]
+              },
+              {
+                "name": "host-group-2",
+                "hosts": [
+                  {
+                    "fqdn": "host2"
+                  }
+                ]
+              },
+            ]
+          }
+        }
+      }
+    ];
+    tests.forEach(function (test) {
+      it("generate host groups", function () {
+        expect(blueprintUtils.generateHostGroups(test.hostNames, test.hostComponents)).to.eql(test.result);
+      });
+    });
+  });
 });
\ No newline at end of file