You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jg...@apache.org on 2018/06/07 17:36:25 UTC

[ambari] branch branch-feature-AMBARI-14714-mpack-advisor updated: [AMBARI-23643] Mpack Advisor integration in Install Wizard (#1487)

This is an automated email from the ASF dual-hosted git repository.

jgolieb pushed a commit to branch branch-feature-AMBARI-14714-mpack-advisor
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-feature-AMBARI-14714-mpack-advisor by this push:
     new 85ecd56  [AMBARI-23643] Mpack Advisor integration in Install Wizard (#1487)
85ecd56 is described below

commit 85ecd5675e290af840370e377423a19ff80c23ea
Author: Jason Golieb <j...@golieb.net>
AuthorDate: Thu Jun 7 13:36:12 2018 -0400

    [AMBARI-23643] Mpack Advisor integration in Install Wizard (#1487)
    
    * Remove references to deleted unit test files and fix broken tests.
    
    * [AMBARI-23969] UI should load stack services from multiple mpacks. (#1403)
    
    * [AMBARI-23969] UI should load stack services from multiple mpacks.
    
    * Removed stack version number
    
    * AMBARI-23972. Update code for : (1). 'mpack-recommendations' directory creation and (2). putting 'mpack_advisor_wrapper.py' in '/var/lib/ambari-server/resources/scripts/' folder.
    
    * AMBARI-14714. Fix some unit tests
    
    * AMBARI-14714. Fix ServiceResourceProviderTest
    
    * AMBARI-23746. Cannot create same named service in different service groups in same request
    
    * AMBARI-23746. Cannot create same named component in different service groups in same request
    
    * AMBARI-22875. Service group name mismatch
    
    * AMBARI-23746. Cannot query services with same name
    
    * AMBARI-23746. Use List for componentID duplicate check
    
    * AMBARI-23987. Set 'skip.service.checks' = true during deploy.
    
    * Clear clusterData when user is not logged in.
    
    * [AMBARI-23993] Mpack Instance Manager should create pid dir and log dir for each instance (#1424)
    
    * AMBARI-23993: Mpack Instance Manager should create pid dir and log dir for each instance
    
    * AMBARI-23993: Mpack Instance Manager should create pid dir and log dir for each instance
    
    * AMBARI-23993-2: Mpack Instance Manager should create pid dir and log dir for each instance: add get_log_dir and get_run_dir to import
    
    * AMBARI-23999 Remove hardcoded logic in UI to create HDPCORE service group and create multiple service groups
    
    * AMBARI-24011: Add workaround to hide client modules in the dashboard (jluniya) (#1443)
    
    Change-Id: Ie69b4e0ca654d59952807694cf2f3f24d6b74c0d
    
    * BUG-104321: Python Mpack Advisor should return MpackInstance block during Host Component Layout Recommendation
    
    * AMBARI-24025 Display Mpack Info on Service Summary Page
    
    * AMBARI-24025 Display Mpack Info on Service Summary Page
    
    * AMBARI-23986. Host components API call doesn't return all host components (#1450)
    
    * AMBARI-24030. Fixes the following : 1. Reading the Node Manager Host (nmHost) correctly. 2. Comments the incorrectly implemented fn : isServiceDeployed().
    
    * AMBARI-24025 Display Mpack Info on Service Summary Page
    
    * [AMBARI-24039] Quicklinks for HBASE are not displayed.
    
    * [AMBARI-24033] Use servicegroup api instead of service to show installed services in the UI.
    
    * AMBARI-14714. Fix some unit tests (#1464)
    
    * AMBARI-24046. Fix the incorrect string from config_name to config-name in mpack_advisor.py (#1480)
    
    * [AMBARI-23643] Mpack Advisor integration in Install wizard.
---
 ambari-web/app/controllers/installer.js            |  1 +
 ambari-web/app/controllers/wizard.js               | 83 ++++++++++++++++++-
 .../app/controllers/wizard/step7_controller.js     |  2 -
 .../app/controllers/wizard/step8_controller.js     | 92 ++++++++++++----------
 ambari-web/app/mixins/common/blueprint.js          |  6 +-
 .../app/mixins/common/configs/enhanced_configs.js  | 52 ++++++------
 .../hosts/host_component_recommendation_mixin.js   | 46 ++++++-----
 .../hosts/host_component_validation_mixin.js       | 46 +++++++----
 ambari-web/app/mixins/common/serverValidator.js    | 87 ++++++++------------
 .../app/mixins/wizard/assign_master_components.js  | 43 ++++------
 ambari-web/app/utils/ajax/ajax.js                  | 36 +++++----
 11 files changed, 289 insertions(+), 205 deletions(-)

diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js
index 06c41f0..c2ca8cf 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -387,6 +387,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
   },
 
   loadCurrentHostGroups: function () {
+    this.set("content.recommendations", this.getDBProperty('recommendations'));
     this.set("content.recommendationsHostGroups", this.getDBProperty('recommendationsHostGroups'));
   },
 
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index feccd7d..cb3fcf5 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -18,7 +18,7 @@
 
 
 var App = require('app');
-
+var blueprintUtils = require('utils/blueprint');
 require('models/host');
 
 App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingMixin, {
@@ -1705,5 +1705,86 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
         });
       }
     });
+  },
+
+  /**
+   * Return mpack_instances data needed for mpack advisor recommendations requests.
+   * This is basically service groups formatted as required by the mpack advisor API.
+   */
+  getMpackInstances: function (configs) {
+    const mpackInstances = {};
+    const selectedServices = this.get('content.selectedServices');
+    
+    selectedServices.forEach(service => {
+      //these will be defined by the user in the future
+      const serviceGroupName = service.mpackName;
+      const serviceInstanceName = service.name;
+
+      if (!mpackInstances[serviceGroupName]) {
+        mpackInstances[serviceGroupName] = {
+          name: serviceGroupName,
+          type: service.mpackName,
+          version: service.mpackVersion,
+          service_instances: []
+        };
+      }
+
+      const serviceInstance = {
+        name: serviceInstanceName,
+        type: service.name
+      };
+
+      //configs will be passed if we are building a request for configs recommendations/validations
+      //it will not be passed if we are building a request for host recommendations/validations
+      if (configs) {
+        const configurations = this.getConfigsForServiceInstance(service.name, service.mpackName, service.mpackVersion, null, configs);
+        if (configurations) {
+          serviceInstance.configurations = configurations;
+        }
+      }  
+
+      mpackInstances[serviceGroupName].service_instances.push(serviceInstance);
+    });
+
+    const mpack_instances = [];
+    for (let prop in mpackInstances) {
+      mpack_instances.push(mpackInstances[prop]);
+    }
+
+    return mpack_instances;
+  },
+
+  /**
+   * Returns configs specific to the given service, stack, and stack version formatted as a blueprint fragment.
+   * This is used to build out the Mpack Advisor config recommendation/validation request.
+   * This is also used to get the "MISC" configs (configs not specific to a service).
+   * If we want to filter these down to just a subset, such as the "cluster-setttings.xml" configs, pass the corresponding "fileName" value.
+   * 
+   * @param {string} serviceName The name/type of the service to get configs for.
+   * @param {string} stackName The name of the stack/mpack to get configs for.
+   * @param {string} stackVersion The version of the stack/mpack to get configs for.
+   * @param {string} filename The file where the configs originate, such as "cluster-settings.xml". Ignored if set to null.
+   * @param {array} configs List of configs to be filtered. This is typically all configs loaded from the stack information.
+   */
+  getConfigsForServiceInstance: function (serviceName, stackName, stackVersion, fileName, configs) {
+    const serviceConfigs = configs.findProperty('serviceName', serviceName);
+    
+    if (serviceConfigs) {
+      let serviceInstanceConfigs;
+      if (stackName && stackVersion) {
+        serviceInstanceConfigs = serviceConfigs.configs.filter(config => (config.stackName === stackName && config.stackVersion === stackVersion) || config.stackName === undefined);
+      } else {
+        serviceInstanceConfigs = serviceConfigs.configs;
+      }  
+
+      if (fileName) {
+        serviceInstanceConfigs = serviceInstanceConfigs.filterProperty('fileName', fileName);
+      }  
+
+      serviceConfigs.set('configs', serviceInstanceConfigs);
+      return blueprintUtils.buildConfigsJSON([serviceConfigs]); //buildConfigsJSON() expects an array
+    }
+
+    return null;
   }
 });
diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js
index e0cd249..c1f376a 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -617,13 +617,11 @@ App.WizardStep7Controller = App.WizardStepController.extend(App.ServerValidatorM
           self.loadConfigRecommendations(null, self.completeConfigLoading.bind(self));
         });
       }
-
     } else {
       console.timeEnd('applyServicesConfigs execution time: ');
       console.time('loadConfigRecommendations execution time: ');
       self.loadConfigRecommendations(null, self.completeConfigLoading.bind(self));
     }
-
   },
 
   /**
diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js
index 90c3a3b..8dd78b6 100644
--- a/ambari-web/app/controllers/wizard/step8_controller.js
+++ b/ambari-web/app/controllers/wizard/step8_controller.js
@@ -971,7 +971,7 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
       const serviceGroups = mpacks.map(mpack => ({
           "ServiceGroupInfo": {
             "service_group_name": mpack.name,
-            "version": mpack.id
+            "version": `${mpack.name}-${mpack.version}`
           }
         })
       );
@@ -1645,53 +1645,59 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
    * @method createNotification
    */
   createNotification: function () {
-    if (!this.get('isInstaller')) return;
-    var miscConfigs = this.get('configs').filterProperty('serviceName', 'MISC'),
-      createNotification = miscConfigs.findProperty('name', 'create_notification').value;
-    if (createNotification !== 'yes') return;
-      var predefinedNotificationConfigNames = require('data/configs/alert_notification').mapProperty('name'),
-      configsForNotification = this.get('configs').filterProperty('filename', 'alert_notification');
-    var properties = {},
-      names = [
-        'ambari.dispatch.recipients',
-        'mail.smtp.host',
-        'mail.smtp.port',
-        'mail.smtp.from',
-        'mail.smtp.starttls.enable',
-        'mail.smtp.startssl.enable'
-      ];
-    if (miscConfigs.findProperty('name', 'smtp_use_auth').value == 'true') { // yes, it's not converted to boolean
-      names.pushObjects(['ambari.dispatch.credential.username', 'ambari.dispatch.credential.password']);
-    }
+    if (this.get('isInstaller')) {
+      const miscConfigs = this.get('configs').filterProperty('serviceName', 'MISC');
+      if (miscConfigs) {
+        const createNotification = miscConfigs.findProperty('name', 'create_notification');
+        if (createNotification && createNotification.value === 'yes') {
+          const predefinedNotificationConfigNames = require('data/configs/alert_notification').mapProperty('name');
+          const configsForNotification = this.get('configs').filterProperty('filename', 'alert_notification');
+          const properties = {};
+          const names = [
+            'ambari.dispatch.recipients',
+            'mail.smtp.host',
+            'mail.smtp.port',
+            'mail.smtp.from',
+            'mail.smtp.starttls.enable',
+            'mail.smtp.startssl.enable'
+          ];
+
+          if (miscConfigs.findProperty('name', 'smtp_use_auth').value == 'true') { // yes, it's not converted to boolean
+            names.pushObjects(['ambari.dispatch.credential.username', 'ambari.dispatch.credential.password']);
+          }
 
-    names.forEach(function (name) {
-      properties[name] = miscConfigs.findProperty('name', name).value;
-    });
+          names.forEach(function (name) {
+            properties[name] = miscConfigs.findProperty('name', name).value;
+          });
 
-    properties['ambari.dispatch.recipients'] = properties['ambari.dispatch.recipients'].replace(/\s/g, '').split(',');
+          properties['ambari.dispatch.recipients'] = properties['ambari.dispatch.recipients'].replace(/\s/g, '').split(',');
 
-    configsForNotification.forEach(function (config) {
-      if (predefinedNotificationConfigNames.contains(config.name)) return;
-      properties[config.name] = config.value;
-    });
+          configsForNotification.forEach(function (config) {
+            if (predefinedNotificationConfigNames.contains(config.name)) return;
+            properties[config.name] = config.value;
+          });
 
-    var apiObject = {
-      AlertTarget: {
-        name: 'Initial Notification',
-        description: 'Notification created during cluster installing',
-        global: true,
-        notification_type: 'EMAIL',
-        alert_states: ['OK', 'WARNING', 'CRITICAL', 'UNKNOWN'],
-        properties: properties
-      }
-    };
-    this.addRequestToAjaxQueue({
-      name: 'alerts.create_alert_notification',
-      data: {
-        urlParams: 'overwrite_existing=true',
-        data: apiObject
+          var apiObject = {
+            AlertTarget: {
+              name: 'Initial Notification',
+              description: 'Notification created during cluster installing',
+              global: true,
+              notification_type: 'EMAIL',
+              alert_states: ['OK', 'WARNING', 'CRITICAL', 'UNKNOWN'],
+              properties: properties
+            }
+          };
+        
+          this.addRequestToAjaxQueue({
+            name: 'alerts.create_alert_notification',
+            data: {
+              urlParams: 'overwrite_existing=true',
+              data: apiObject
+            }
+          });
+        }
       }
-    });
+    }
   },
 
   /**
diff --git a/ambari-web/app/mixins/common/blueprint.js b/ambari-web/app/mixins/common/blueprint.js
index 3df3b1a..cbc633d 100644
--- a/ambari-web/app/mixins/common/blueprint.js
+++ b/ambari-web/app/mixins/common/blueprint.js
@@ -44,7 +44,11 @@ App.BlueprintMixin = Em.Mixin.create({
       res.blueprint.host_groups.push({
         name: group_name,
         components: mappedComponents[host] ? mappedComponents[host].map(function (c) {
-          return { name: Em.get(c, 'componentName') };
+          return {
+            mpack_instance: Em.get(c, 'mpackInstance'),
+            service_instance: Em.get(c, 'serviceInstance'),
+            name: Em.get(c, 'componentName')
+          };
         }) : []
       });
 
diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js b/ambari-web/app/mixins/common/configs/enhanced_configs.js
index 92d1012..7d2f7a2 100644
--- a/ambari-web/app/mixins/common/configs/enhanced_configs.js
+++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js
@@ -219,7 +219,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
       if (requiredTags.length) {
         this.loadAdditionalSites(requiredTags, stepConfigs, recommendations, dataToSend, onComplete);
       } else {
-        this.addRecommendationRequestParams(recommendations, dataToSend, stepConfigs);
+        dataToSend.recommendations = this.addRequestedConfigs(recommendations, stepConfigs);
         return this.getRecommendationsRequest(dataToSend, onComplete);
       }
     } else {
@@ -231,14 +231,22 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
   },
 
   /**
-   *
+   * Adds the configs to include in the request to the recommendations.blueprint object in the format required.
+   * 
    * @param recommendations
-   * @param dataToSend
-   * @param stepConfigs
+   * @param configs
    */
-  addRecommendationRequestParams: function(recommendations, dataToSend, stepConfigs) {
-    recommendations.blueprint.configurations = blueprintUtils.buildConfigsJSON(stepConfigs);
-    dataToSend.recommendations = recommendations;
+  addRequestedConfigs: function (recommendations, configs) {
+    const wizardController = this.get('wizardController');
+
+    if (wizardController) {
+      recommendations.blueprint.configurations = wizardController.getConfigsForServiceInstance('MISC', null, null, 'cluster-settings.xml', configs);
+      recommendations.blueprint.mpack_instances = wizardController.getMpackInstances(configs);
+    } else {
+      App.get('mpackInstances'); //TODO: implement this property on the App object along with a routine to populate it at startup, similar to App.allHostNames
+    }
+
+    return recommendations;
   },
 
   /**
@@ -253,7 +261,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
     App.config.getConfigsByTypes(sites).done(function (configs) {
       stepConfigs = stepConfigs.concat(configs);
 
-      self.addRecommendationRequestParams(recommendations, dataToSend, stepConfigs);
+      dataToSend.recommendations = self.addRequestedConfigs(recommendations, stepConfigs);
       self.getRecommendationsRequest(dataToSend, onComplete);
     });
   },
@@ -282,37 +290,24 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
     const params = {
       recommend: updateDependencies ? 'configuration-dependencies' : 'configurations',
       hosts: this.get('hostNames'),
-      services: this.get('serviceNames'),
       changed_configurations: updateDependencies ? changedConfigs : undefined
     };
 
-    //TODO - mpacks: Hard coded to use first mpack. Change when we are installing multiple mpacks.
-    const selectedMpacks = this.get('content.selectedMpacks');
-
-    if (selectedMpacks) {
-      params.stackName = selectedMpacks[0].name;
-      params.stackVersion = selectedMpacks[0].version;
-    }
-
     return params;
   },
 
   getRecommendationsRequest: function (dataToSend, callback) {
     const self = this;
     this.set('recommendationsInProgress', true);
-    const stackVersionUrl = App.getStackVersionUrl(dataToSend.stackName, dataToSend.stackVersion) || App.get('stackVersionURL');
     
     return App.ajax.send({
-      name: 'config.recommendations',
+      name: 'mpack.advisor.recommendations',
       sender: self,
       data: {
-        stackVersionUrl: stackVersionUrl,
-        dataToSend: {
+        data: {
           recommend: dataToSend.recommend,
           hosts: dataToSend.hosts,
-          services: dataToSend.services,
           changed_configurations: dataToSend.changed_configurations,
-          user_context: dataToSend.user_context,
           recommendations: dataToSend.recommendations
         }
       },
@@ -402,7 +397,7 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
    * @method dependenciesSuccess
    */
   loadRecommendationsSuccess: function (data, opt, params) {
-    this._saveRecommendedValues(data, params.dataToSend.changed_configurations);
+    this._saveRecommendedValues(data, params.data.changed_configurations);
     if (this.isConfigHasInitialState()) {
       /** clearing all recommendations info **/
       this.undoRedoRecommended(this.get('recommendations'), false);
@@ -445,12 +440,11 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
    * @private
    */
   _saveRecommendedValues: function(data, changedConfigs) {
-    Em.assert('invalid data - `data.resources[0].recommendations.blueprint.configurations` not defined ', data && data.resources[0] && Em.get(data.resources[0], 'recommendations.blueprint.configurations'));
     var recommendations = data.resources[0].recommendations;
     if (recommendations['config-groups'] && this.get('selectedConfigGroup') && !this.get('selectedConfigGroup.isDefault')) {
       this.saveConfigGroupsRecommendations(recommendations, changedConfigs);
     } else {
-      var configObject = recommendations.blueprint.configurations;
+      var configObject = this.getAllRecommendedConfigs(recommendations);
       this.get('stepConfigs').forEach(function(stepConfig) {
         this.updateConfigsByRecommendations(configObject, stepConfig.get('configs'), changedConfigs);
       }, this);
@@ -459,6 +453,12 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP
     this.cleanUpRecommendations();
   },
 
+  getAllRecommendedConfigs: function (recommendations) {
+    const serviceInstances = recommendations.blueprint.mpack_instances.reduce((result, value) => result.concat(value.service_instances), []);
+    const configs = serviceInstances.reduce((result, value) => Object.assign(result, value.configurations), {});
+    return configs;
+  },
+
   /**
    *
    * @param {object} recommendations
diff --git a/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js b/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js
index e0cbb3f..1d151a4 100644
--- a/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js
+++ b/ambari-web/app/mixins/common/hosts/host_component_recommendation_mixin.js
@@ -21,26 +21,29 @@ require('mixins/common/blueprint');
 var App = require('app');
 
 /**
- * @typedef {object} RecommendComponentObject
- * @property {string} componentName name of the component
- * @property {number} [size=0] desired components size
- * @property {string[]} [hosts=[]] hosts assigned to component
+ * @typedef {object} ServiceInstanceObject
+ * @property {string} name name of the service instance
+ * @property {string} type of the service instance (usually the name of the service, i.e. ZOOKEEPER)
+ */
+
+/**
+ * @typedef {object} MpackInstanceObject
+ * @property {string} name name of the mpack instances (usually the name of a service group)
+ * @property {string} type of the mpack instance (usually the name of the mpack, i.e. HDPCORE)
+ * @property {string} version of the mpack
+ * @property {ServiceInstanceObject[]} service_instances list of service instances
  */
 
 /**
  * @typedef {object} HostComponentRecommendationOptions
  * @property {string[]} hosts list of host names, in most cases all available host names
- * @property {RecommendComponentObject[]} components list of components
- * @property {string[]} services list of service names
- * @property {object} [blueprint=null] when null blueprint will be created by <code>HostComponentRecommendationOptions.components</code> attribute
+ * @property {MpackInstanceObject[]} mpack_instances list of mpack instances
  */
 
 /**
  * @typedef {object} HostRecommendationRequestData
- * @property {string} stackVersionUrl stack version url
- * @property {string[]} hosts host names
- * @property {string[]} services service names
  * @property {string} recommend recommendation type e.g. 'host_groups'
+ * @property {string[]} hosts list of host names
  * @property {object} recommendations blueprint object
  */
 
@@ -58,7 +61,6 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, {
    */
   getRecommendedHosts: function(options) {
     var opts = $.extend({
-      services: [],
       hosts: [],
       components: [],
       blueprint: null
@@ -77,11 +79,12 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, {
     var res = [];
     if (!components) return [];
     components.forEach(function(component) {
-      var componentName = Em.get(component, 'componentName');
       if (Em.get(component, 'hosts.length')) {
         Em.get(component, 'hosts').forEach(function(hostName) {
           res.push(Em.Object.create({
-            componentName: componentName,
+            componentName: Em.get(component, 'componentName'),
+            mpackInstance: Em.get(component, 'mpackInstance'),
+            serviceInstance: Em.get(component, 'serviceInstance'),
             hostName: hostName
           }));
         });
@@ -97,17 +100,17 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, {
    * @method getRecommendationRequestData
    */
   getRecommendationRequestData: function(options) {
-    const stackVersionUrl = App.getStackVersionUrl(options.stackName, options.stackVersion) || App.get('stackVersionURL');
-
-    return {
-      stackVersionUrl: stackVersionUrl,
-      dataToSend: {
+    const requestData = {
+      data: {
         recommend: 'host_groups',
         hosts: options.hosts,
-        services: options.services,
         recommendations: options.blueprint || this.getComponentsBlueprint(options.components)
       }
-    };
+    }
+
+    requestData.data.recommendations.blueprint.mpack_instances = options.mpack_instances;
+
+    return requestData;
   },
 
   /**
@@ -118,7 +121,7 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, {
    */
   loadComponentsRecommendationsFromServer: function(recommendationData) {
     return App.ajax.send({
-      name: 'config.recommendations',
+      name: 'mpack.advisor.recommendations',
       sender: this,
       data: recommendationData,
       success: 'loadRecommendationsSuccessCallback',
@@ -126,6 +129,7 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, {
     });
   },
 
+  //these can be overridden in the derived object
   loadRecommendationsSuccessCallback: function() {},
   loadRecommendationsErrorCallback: function() {}
 });
diff --git a/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js b/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js
index a7e752c..ee7fb84 100644
--- a/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js
+++ b/ambari-web/app/mixins/common/hosts/host_component_validation_mixin.js
@@ -21,12 +21,23 @@ require('mixins/common/blueprint');
 var App = require('app');
 
 /**
+ * @typedef {object} ServiceInstanceObject
+ * @property {string} name name of the service instance
+ * @property {string} type of the service instance (usually the name of the service, i.e. ZOOKEEPER)
+ */
+
+/**
+ * @typedef {object} MpackInstanceObject
+ * @property {string} name name of the mpack instances (usually the name of a service group)
+ * @property {string} type of the mpack instance (usually the name of the mpack, i.e. HDPCORE)
+ * @property {string} version of the mpack
+ * @property {ServiceInstanceObject[]} service_instances list of service instances
+ */
+
+/**
  * @typedef {object} HostValidationRequestData
- * @property {string} stackVersionUrl stack version url
  * @property {string[]} hosts host names
- * @property {string[]} services service names
- * @property {string} validate validation type e.g. 'host_groups'
- * @property {object} recommendations blueprint object
+ * @property {MpackInstanceObject[]} mpack_instances list of mpack instances
  */
 
 App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, {
@@ -38,7 +49,6 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, {
    */
   validateSelectedHostComponents: function(options) {
     var opts = $.extend({
-      services: [],
       blueprint: null,
       hosts: [],
       components: []
@@ -57,11 +67,12 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, {
     var res = [];
     if (!components) return [];
     components.forEach(function(component) {
-      var componentName = Em.get(component, 'componentName');
       if (Em.get(component, 'hosts.length')) {
         Em.get(component, 'hosts').forEach(function(hostName) {
           res.push(Em.Object.create({
-            componentName: componentName,
+            componentName: Em.get(component, 'componentName'),
+            mpackInstance: Em.get(component, 'mpackInstance'),
+            serviceInstance: Em.get(component, 'serviceInstance'),
             hostName: hostName
           }));
         });
@@ -76,15 +87,17 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, {
    * @return {HostValidationRequestData}
    */
   getHostComponentValidationParams: function(options) {
-    const stackVersionUrl = App.getStackVersionUrl(options.stackName, options.stackVersion) || App.get('stackVersionURL');
-
-    return {
-      stackVersionUrl: stackVersionUrl,
-      hosts: options.hosts,
-      services: options.services,
-      validate: 'host_groups',
-      recommendations: options.blueprint || this.getComponentsBlueprint(options.components)
+    const requestData = {
+      data: {
+        validate: 'host_groups',
+        hosts: options.hosts,
+        recommendations: options.blueprint || this.getComponentsBlueprint(options.components)
+      }
     };
+
+    requestData.data.recommendations.blueprint.mpack_instances = options.mpack_instances;
+
+    return requestData;
   },
 
   /**
@@ -95,7 +108,7 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, {
    */
   getHostComponentValidationRequest: function(validationData) {
     return App.ajax.send({
-      name: 'config.validations',
+      name: 'mpack.advisor.validations',
       sender: this,
       data: validationData,
       success: 'updateValidationsSuccessCallback',
@@ -103,6 +116,7 @@ App.HostComponentValidationMixin = Em.Mixin.create(App.BlueprintMixin, {
     });
   },
 
+  //these can be overridden in the derived object
   updateValidationsSuccessCallback: function() {},
   updateValidationsErrorCallback: function() {}
 });
diff --git a/ambari-web/app/mixins/common/serverValidator.js b/ambari-web/app/mixins/common/serverValidator.js
index 4b40bb1..b8e069a 100644
--- a/ambari-web/app/mixins/common/serverValidator.js
+++ b/ambari-web/app/mixins/common/serverValidator.js
@@ -114,8 +114,21 @@ App.ServerValidatorMixin = Em.Mixin.create({
    * @type {Array} of strings (hostNames)
    */
   hostGroups: function() {
-    return this.get('content.recommendationsHostGroups') || blueprintUtils.generateHostGroups(App.get('allHostNames'));
-  }.property('content.recommendationsHostGroups', 'App.allHostNames', 'App.componentToBeAdded', 'App.componentToBeDeleted'),
+    const hostGroups = this.get('content.recommendationsHostGroups')
+      || this.get('content.recommendations')
+      || blueprintUtils.generateHostGroups(App.get('allHostNames'));
+    
+    return hostGroups;
+  }.property('content.recommendationsHostGroups', 'content.recommendations', 'App.allHostNames', 'App.componentToBeAdded', 'App.componentToBeDeleted'),
+
+  getMpackInstances(configs) {
+    if (this.get('isWizard')) {
+      const wizardController = this.get('wizardController');
+      return wizardController.getMpackInstances(configs);
+    } else {
+      App.get('mpackInstances'); //TODO: implement this property on the App object along with a routine to populate it at startup, similar to App.allHostNames
+    }
+  },
 
   /**
    * controller that is child of this mixin has to contain stepConfigs
@@ -133,8 +146,7 @@ App.ServerValidatorMixin = Em.Mixin.create({
       criticalIssues: []
     }));
 
-    const mpacks = this.get('content.selectedMpacks'); //TODO - mpacks
-    this.runServerSideValidation(mpacks[0].name, mpacks[0].version).done(function() {
+    this.runServerSideValidation().done(function() {
       if (self.get('configErrorList.issues.length') || self.get('configErrorList.criticalIssues.length')) {
         App.showConfigValidationPopup(self.get('configErrorList'), primary, secondary, self);
       } else {
@@ -152,25 +164,17 @@ App.ServerValidatorMixin = Em.Mixin.create({
    * send request to validate configs
    * @returns {*}
    */
-  runServerSideValidation: function (stackName, stackVersion) {
-    var self = this;
-    var recommendations = this.get('hostGroups');
-    var stepConfigs = this.get('stepConfigs');
-    var dfd = $.Deferred();
-
-    this.getBlueprintConfigurations(this.get('stepConfigs'))
-      .done(function(blueprintConfigurations) {
-        recommendations.blueprint.configurations = blueprintConfigurations;
-        var request = self.validateSelectedConfigs({
-          hosts: self.get('hostNames'),
-          services: self.get('serviceNames'),
-          blueprint: recommendations,
-          stackName: stackName,
-          stackVersion: stackVersion
-        });
-        self.set('validationRequest', request);
-        request.done(dfd.resolve).fail(dfd.reject);
-      });
+  runServerSideValidation: function () {
+    const dfd = $.Deferred();
+
+    const request = this.validateSelectedConfigs({
+      hosts: this.get('hostNames'),
+      blueprint: this.get('hostGroups')
+    });
+
+    this.set('validationRequest', request);
+    request.done(dfd.resolve).fail(dfd.reject);
+
     return dfd.promise();
   },
 
@@ -182,7 +186,6 @@ App.ServerValidatorMixin = Em.Mixin.create({
    */
   validateSelectedConfigs: function(options) {
     var opts = $.extend({
-      services: [],
       hosts: [],
       blueprint: null
     }, options || {});
@@ -197,7 +200,7 @@ App.ServerValidatorMixin = Em.Mixin.create({
    */
   getServiceConfigsValidationRequest: function(validationData) {
     return App.ajax.send({
-      name: 'config.validations',
+      name: 'mpack.advisor.validations',
       sender: this,
       data: validationData,
       success: 'validationSuccess',
@@ -211,36 +214,16 @@ App.ServerValidatorMixin = Em.Mixin.create({
    * @returns {ConfigsValidationRequestData}
    */
   getServiceConfigsValidationParams: function(options) {
-    const stackVersionUrl = App.getStackVersionUrl(options.stackName, options.stackVersion) || App.get('stackVersionURL');
-
     return {
-      stackVersionUrl: stackVersionUrl,
-      hosts: options.hosts,
-      services: options.services,
-      validate: 'configurations',
-      recommendations: options.blueprint
+      data: {
+        validate: 'configurations',
+        hosts: options.hosts,
+        recommendations: options.blueprint
+      }
     };
   },
 
   /**
-   * Return JSON for blueprint configurations
-   * @param {App.ServiceConfigs[]} serviceConfigs
-   * @returns {*}
-   */
-  getBlueprintConfigurations: function (serviceConfigs) {
-    var dfd = $.Deferred();
-    // check if we have configs from 'cluster-env', if not, then load them, as they are mandatory for validation request
-    if (!serviceConfigs.findProperty('serviceName', 'MISC')) {
-      App.config.getConfigsByTypes([{site: 'cluster-env', serviceName: 'MISC'}]).done(function(configs) {
-        dfd.resolve(blueprintUtils.buildConfigsJSON(serviceConfigs.concat(configs)));
-      });
-    } else {
-      dfd.resolve(blueprintUtils.buildConfigsJSON(serviceConfigs));
-    }
-    return dfd.promise();
-  },
-
-  /**
    * Creates config validation error object
    *
    * @param type - error type, see <code>errorTypes<code>
@@ -384,14 +367,12 @@ App.ServerValidatorMixin = Em.Mixin.create({
   valueObserver: function () {
     var self = this;
     if (this.get('isInstallWizard') && this.get('currentTabName') === 'all-configurations') {
-      const mpacks = this.get('content.selectedMpacks'); //TODO - mpacks
-
       if (this.get('requestTimer')) clearTimeout(this.get('requestTimer'));
       self.set('requestTimer', setTimeout(function () {
         if (self.get('validationRequest')) {
           self.get('validationRequest').abort();
         }
-        self.runServerSideValidation(mpacks[0].name, mpacks[0].version).done(function () {
+        self.runServerSideValidation().done(function () {
           self.set('validationRequest', null);
           self.set('requestTimer', 0);
         });
diff --git a/ambari-web/app/mixins/wizard/assign_master_components.js b/ambari-web/app/mixins/wizard/assign_master_components.js
index d0b4bd9..ff878b2 100644
--- a/ambari-web/app/mixins/wizard/assign_master_components.js
+++ b/ambari-web/app/mixins/wizard/assign_master_components.js
@@ -465,23 +465,13 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
    */
   getRecommendationRequestData: function(options) {
     var res = this._super(options);
-    res.dataToSend.services = this.getCurrentServiceNames();
     if (!this.get('isInstallerWizard')) {
-      res.dataToSend.recommendations = this.getCurrentMasterSlaveBlueprint();
+      res.data.recommendations = this.getCurrentMasterSlaveBlueprint();
     }
     return res;
   },
 
   /**
-   * @override App.HostComponentRecommendationMixin
-   */
-  getHostComponentValidationParams: function(options) {
-    var res = this._super(options);
-    res.services = this.getCurrentServiceNames();
-    return res;
-  },
-
-  /**
    * Returns selected and installed service names
    * @return {string[]}
    */
@@ -530,12 +520,9 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
         self.set('backFromNextStep', true);
       }
       
-      //TODO - mpacks: Hard-coding to only ask for recommendations for first mpack. Need to change this when we are installing multiple mpacks.
-      const selectedMpacks = self.get('content.selectedMpacks');
       self.getRecommendedHosts({
-        stackName: selectedMpacks[0].name,
-        stackVersion: selectedMpacks[0].version,
-        hosts: self.getHosts()
+        hosts: self.getHosts(),
+        mpack_instances: self.get('wizardController').getMpackInstances()
       }).then(function () {
         self.loadStepCallback(self.createComponentInstallationObjects(), self);
       });
@@ -711,7 +698,11 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
         host_group.components.forEach(function(component) {
           var willBeDisplayed = true;
           var stackMasterComponent = stackMasterComponentsMap[component.name];
+          
           if (stackMasterComponent) {
+            stackMasterComponent.mpackInstance = component.mpack_instance;
+            stackMasterComponent.serviceInstance = component.service_instance;
+            
             var isMasterCreateOnConfig = this.get('mastersToCreate').contains(component.name);
             // If service is already installed and not being added as a new service then render on UI only those master components
             // that have already installed hostComponents.
@@ -757,6 +748,8 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
     componentObj.component_name = componentName;
     componentObj.display_name = App.format.role(fullComponent.get('componentName'), false);
     componentObj.serviceId = fullComponent.get('serviceName');
+    componentObj.serviceInstance = fullComponent.serviceInstance;
+    componentObj.mpackInstance = fullComponent.mpackInstance;
     componentObj.isServiceCoHost = App.StackServiceComponent.find().findProperty('componentName', componentName).get('isCoHostedComponent') && !this.get('mastersToMove').contains(componentName);
     componentObj.selectedHost = savedComponent ? savedComponent.hostName : hostName;
     componentObj.isInstalled = savedComponent ? savedComponent.isInstalled || (this.get('markSavedComponentsAsInstalled') && !this.get('mastersToCreate').contains(fullComponent.get('componentName'))) : false;
@@ -935,6 +928,7 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
       return aValue - bValue;
     });
   },
+
   /**
    * Update dependent co-hosted components according to the change in the component host
    * @method updateCoHosts
@@ -953,7 +947,6 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
     }, this);
   }.observes('selectedServicesMasters.@each.selectedHost'),
 
-
   /**
    * On change callback for inputs
    * @param {string} componentName
@@ -1165,8 +1158,9 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
   },
 
   recommendAndValidate: function(callback) {
-    var self = this,
-      hostNames = this.getHosts();
+    const self = this;
+    const hostNames = this.getHosts();
+    const mpackInstances = this.get('wizardController').getMpackInstances();
 
     if (this.get('validationInProgress')) {
       this.set('runQueuedValidation', true);
@@ -1175,19 +1169,14 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
 
     this.set('validationInProgress', true);
     
-    //TODO - mpacks: Hard coded to request first mpack only. Must be changed when we are installing multiple mpacks.
-    const selectedMpacks = this.get('content.selectedMpacks');
-    // load recommendations with partial request
     this.getRecommendedHosts({
       hosts: hostNames,
-      stackName: selectedMpacks[0].name,
-      stackVersion: selectedMpacks[0].version,
+      mpack_instances: mpackInstances,
       components: this.getCurrentComponentHostMap()
     }).then(function() {
       self.validateSelectedHostComponents({
         hosts: hostNames,
-        stackName: selectedMpacks[0].name,
-        stackVersion: selectedMpacks[0].version,  
+        mpack_instances: mpackInstances,
         blueprint: self.get('recommendations')
       }).then(function() {
         if (callback) {
@@ -1214,6 +1203,8 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
           Em.set(component, 'size', Em.getWithDefault(component, 'hosts.length', 0));
         } else {
           acc.push({
+            mpackInstance: Em.get(i, 'mpackInstance'),
+            serviceInstance: Em.get(i, 'serviceInstance'),
             componentName: componentName,
             hosts: [hostName],
             size: 1
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index b225aee..0bcf2af 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -2145,22 +2145,6 @@ var urls = {
     }
   },
 
-  'config.validations': {
-    'real': '{stackVersionUrl}/validations',
-    'mock': '/data/stacks/HDP-2.1/validations.json',
-    'type': 'POST',
-    'format': function (data) {
-      return {
-        data: JSON.stringify({
-          hosts: data.hosts,
-          services: data.services,
-          validate: data.validate,
-          recommendations: data.recommendations
-        })
-      }
-    }
-  },
-
   'preinstalled.checks': {
     'real': '/requests',
     'mock': '',
@@ -3136,6 +3120,26 @@ var urls = {
     real: '/mpacks?fields=*'
   },
 
+  'mpack.advisor.recommendations': {
+    real: '/mpacks/recommendations',
+    format: function (data) {
+      return {
+        type: 'POST',
+        data: JSON.stringify(data.data)
+      }
+    }
+  },
+
+  'mpack.advisor.validations': {
+    real: '/mpacks/validations',
+    format: function (data) {
+      return {
+        type: 'POST',
+        data: JSON.stringify(data.data)
+      }
+    }
+  },
+
   'registry.all': {
     real: '/registries?fields=mpacks/*,mpacks/versions/RegistryMpackVersionInfo/*,scenarios/*'
   },

-- 
To stop receiving notification emails like this one, please contact
jgolieb@apache.org.