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/15 18:52:11 UTC

[ambari] branch branch-feature-AMBARI-14714-mpack-advisor updated: [AMBARI-24083] Enable same service to be installed from multiple mpacks (#1538)

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 6ad876f  [AMBARI-24083] Enable same service to be installed from multiple mpacks (#1538)
6ad876f is described below

commit 6ad876fa91e5088b56e3ecf8e2b68de1ab9f8b51
Author: Jason Golieb <j...@golieb.net>
AuthorDate: Fri Jun 15 14:52:08 2018 -0400

    [AMBARI-24083] Enable same service to be installed from multiple mpacks (#1538)
    
    * [AMBARI-24083] Allow clients from multiple mpacks to be installed
    
    * Unit test fixes.
    
    * Unit test added
---
 ambari-web/app/controllers/installer.js            |   5 +
 ambari-web/app/controllers/wizard.js               |  19 ++--
 .../app/controllers/wizard/step6_controller.js     |   4 +-
 .../app/controllers/wizard/step8_controller.js     |  99 +++++++++---------
 .../configs/stack_config_properties_mapper.js      |   4 +-
 ambari-web/app/mappers/mpack_service_mapper.js     |   6 +-
 ambari-web/app/mappers/stack_service_mapper.js     |   5 +-
 .../app/mixins/wizard/assign_master_components.js  |   2 -
 ambari-web/test/controllers/installer_test.js      |   9 +-
 ambari-web/test/controllers/wizard/step5_test.js   |  24 +++--
 .../test/mappers/stack_service_mapper_test.js      |   2 +-
 .../mixins/common/configs/enhanced_configs_test.js | 115 +++++++++++++--------
 .../host_component_recommendation_mixin_test.js    |  52 ++++++----
 .../hosts/host_component_validation_mixin_test.js  |  73 +++++++------
 14 files changed, 244 insertions(+), 175 deletions(-)

diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js
index c2ca8cf..15c1c43 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -346,6 +346,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         display_name: _component.get('display_name'),
         component: _component.get('component_name'),
         serviceId: _component.get('serviceId'),
+        serviceGroupName: _component.get('mpackInstance'),
         isInstalled: false,
         host_id: hosts[_component.get('selectedHost')].id
       });
@@ -429,6 +430,8 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
       client.forEach(function (clientComponent) {
         clients.pushObject({
           component_name: clientComponent.get('componentName'),
+          service_name: _service.get('serviceName'),
+          serviceGroupName: _service.get('stackName'),
           display_name: clientComponent.get('displayName'),
           isInstalled: false
         });
@@ -1029,6 +1032,8 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         client.forEach(clientComponent => {
           clients.pushObject({
             component_name: clientComponent.get('componentName'),
+            service_name: service.get('serviceName'),
+            serviceGroupName: service.get('stackName'),
             display_name: clientComponent.get('displayName'),
             isInstalled: false
           });
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index d423533..01d150b 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -940,20 +940,21 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
    * @param stepController
    */
   saveSlaveComponentHosts: function (stepController) {
-    var hosts = stepController.get('hosts'),
-      dbHosts = this.getDBProperty('hosts'),
-      headers = stepController.get('headers');
+    const hosts = stepController.get('hosts');
+    const dbHosts = this.getDBProperty('hosts');
+    const headers = stepController.get('headers');
 
-    var formattedHosts = Ember.Object.create();
+    const formattedHosts = Ember.Object.create();
     headers.forEach(function (header) {
       formattedHosts.set(header.get('name'), []);
     });
 
     hosts.forEach(function (host) {
+      const checkboxes = host.checkboxes;
 
-      var checkboxes = host.checkboxes;
       headers.forEach(function (header) {
-        var cb = checkboxes.findProperty('title', header.get('label'));
+        const cb = checkboxes.findProperty('title', header.get('label'));
+
         if (cb.checked) {
           formattedHosts.get(header.get('name')).push({
             group: 'Default',
@@ -964,13 +965,15 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
       });
     });
 
-    var slaveComponentHosts = [];
+    const slaveComponentHosts = [];
 
     headers.forEach(function (header) {
       slaveComponentHosts.push({
         componentName: header.get('name'),
         displayName: header.get('label').replace(/\s/g, ''),
-        hosts: formattedHosts.get(header.get('name'))
+        hosts: formattedHosts.get(header.get('name')),
+        serviceName: header.get('serviceName'),
+        serviceGroupName: header.get('serviceGroupName')
       });
     });
 
diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js
index 3f0b2d7..73e6544 100644
--- a/ambari-web/app/controllers/wizard/step6_controller.js
+++ b/ambari-web/app/controllers/wizard/step6_controller.js
@@ -322,6 +322,8 @@ App.WizardStep6Controller = App.WizardStepController.extend(App.HostComponentVal
         if (serviceComponent.get('isShownOnInstallerSlaveClientPage')) {
           headers.pushObject(Em.Object.create({
             name: serviceComponent.get('componentName'),
+            serviceName: stackService.get('serviceName'),
+            serviceGroupName: stackService.get('stackName'),
             label: App.format.role(serviceComponent.get('componentName'), false),
             allChecked: false,
             isRequired: serviceComponent.get('isRequired'),
@@ -335,7 +337,7 @@ App.WizardStep6Controller = App.WizardStepController.extend(App.HostComponentVal
     }, this);
     if (this.get('content.clients') && !!this.get('content.clients').length) {
       headers.pushObject(Em.Object.create({
-        name: 'CLIENT',
+        name: 'CLIENT',        
         label: App.format.role('CLIENT', false),
         allChecked: false,
         noChecked: true,
diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js
index 8dd78b6..4f73fe0 100644
--- a/ambari-web/app/controllers/wizard/step8_controller.js
+++ b/ambari-web/app/controllers/wizard/step8_controller.js
@@ -1118,37 +1118,52 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
    * @method createMasterHostComponents
    */
   createMasterHostComponents: function () {
-    var masterOnAllHosts = [];
+    const masterOnAllHosts = [];
 
     this.get('content.services').filterProperty('isSelected').forEach(function (service) {
-      service.get('serviceComponents').filterProperty('isRequiredOnAllHosts').forEach(function (component) {
+      service.get('serviceComponents').filterProperty('isRequiredOnAllHosts').forEach(component => {
         if (component.get('isMaster')) {
           masterOnAllHosts.push(component.get('componentName'));
         }
       }, this);
     }, this);
 
-    // create master components for only selected services.
-    var selectedMasterComponents = this.get('content.masterComponentHosts').filter(function (_component) {
-      return this.get('selectedServices').mapProperty('serviceName').contains(_component.serviceId)
-    }, this);
-    selectedMasterComponents.mapProperty('component').uniq().forEach(function (component) {
-      var hostNames = [];
+    //get unique occurrences per service group, since components in the same SG going onto multiple hosts must only be processed once
+    const uniqueMasterComponents = {};
+    this.get('content.masterComponentHosts').forEach(component => {
+      uniqueMasterComponents[`${component.component}-${component.serviceId}-${component.serviceGroupName}`] = component;
+    });
+    
+    //convert to arry for further filtering
+    const masterComponentsArray = [];
+    for (let prop in uniqueMasterComponents) {
+      masterComponentsArray.push(uniqueMasterComponents[prop]);
+    }
+    
+    //get only selected services.
+    const selectedMasterComponents = masterComponentsArray.filter(component => this.get('selectedServices').mapProperty('serviceName').contains(component.serviceId), this);
+    selectedMasterComponents.forEach(component => {
+      const componentName = component.component;
+      const serviceName = component.serviceId;
+      const serviceGroupName = component.serviceGroupName;
+
       if (masterOnAllHosts.length > 0) {
-        var compOnAllHosts = false;
-        for (var i=0; i < masterOnAllHosts.length; i++) {
-          if (component === masterOnAllHosts[i]) {
+        let compOnAllHosts = false;
+        
+        for (let i = 0; i < masterOnAllHosts.length; i++) {
+          if (componentName === masterOnAllHosts[i]) {
             compOnAllHosts = true;
             break;
           }
         }
+        
         if (!compOnAllHosts) {
-          hostNames = selectedMasterComponents.filterProperty('component', component).filterProperty('isInstalled', false).mapProperty('hostName');
-          this.registerHostsToComponent(hostNames, component);
+          const hostNames = selectedMasterComponents.filterProperty('component', componentName).filterProperty('isInstalled', false).mapProperty('hostName');
+          this.registerHostsToComponent(hostNames, componentName, serviceName, serviceGroupName);
         }
       } else {
-        hostNames = selectedMasterComponents.filterProperty('component', component).filterProperty('isInstalled', false).mapProperty('hostName');
-        this.registerHostsToComponent(hostNames, component);
+        const hostNames = selectedMasterComponents.filterProperty('component', componentName).filterProperty('isInstalled', false).mapProperty('hostName');
+        this.registerHostsToComponent(hostNames, componentName, serviceName, serviceGroupName);
       }
     }, this);
   },
@@ -1222,14 +1237,13 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
           }
           if (!compOnAllHosts) {
             hostNames = _slave.hosts.filterProperty('isInstalled', false).mapProperty('hostName');
-            this.registerHostsToComponent(hostNames, _slave.componentName);
+            this.registerHostsToComponent(hostNames, _slave.componentName, _slave.serviceName, _slave.serviceGroupName);
           }
         } else {
           hostNames = _slave.hosts.filterProperty('isInstalled', false).mapProperty('hostName');
-          this.registerHostsToComponent(hostNames, _slave.componentName);
+          this.registerHostsToComponent(hostNames, _slave.componentName, _slave.serviceName, _slave.serviceGroupName);
         }
-      }
-      else {
+      } else {
         clients.forEach(function (_client) {
           hostNames = _slave.hosts.mapProperty('hostName');
           // The below logic to install clients to existing/New master hosts should not be applied to Add Host wizard.
@@ -1262,11 +1276,11 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
             }
             if (!compOnAllHosts) {
               hostNames = hostNames.uniq();
-              this.registerHostsToComponent(hostNames, _client.component_name);
+              this.registerHostsToComponent(hostNames, _client.component_name, _client.service_name, _client.serviceGroupName);
             }
           } else {
             hostNames = hostNames.uniq();
-            this.registerHostsToComponent(hostNames, _client.component_name);
+            this.registerHostsToComponent(hostNames, _client.component_name, _client.service_name, _client.serviceGroupName);
           }
         }, this);
       }
@@ -1286,18 +1300,17 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
     var clients = this.get('content.clients').filterProperty('isInstalled', false);
     var clientsToMasterMap = this.getClientsMap('isMaster');
     var clientsToClientMap = this.getClientsMap('isClient');
-    var installedClients = [];
+    let installedClients = [];
 
     // Get all the installed Client components
     this.get('content.services').filterProperty('isInstalled').forEach(function (_service) {
-      var serviceClients = App.StackServiceComponent.find().filterProperty('serviceName', _service.get('serviceName')).filterProperty('isClient');
-      serviceClients.forEach(function (client) {
-        installedClients.push(client.get('componentName'));
-      }, this);
+      installedClients = App.StackServiceComponent.find().filterProperty('serviceName', _service.get('serviceName')).filterProperty('isClient');
     }, this);
 
     // Check if there is a dependency for being co-hosted between existing client and selected new master
-    installedClients.forEach(function (_clientName) {
+    installedClients.forEach(function (client) {
+      const _clientName = client.get('componentName');
+
       if (clientsToMasterMap[_clientName] || clientsToClientMap[_clientName]) {
         var hostNames = [];
         if (clientsToMasterMap[_clientName]) {
@@ -1323,8 +1336,8 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
           // If a dependency for being co-hosted is derived between existing client and selected new master but that
           // dependency is already satisfied in the cluster then disregard the derived dependency
           this.removeClientsFromList(_clientName, hostNames);
-          this.registerHostsToComponent(hostNames, _clientName);
-          if(hostNames.length > 0) {
+          this.registerHostsToComponent(hostNames, _clientName, client.service_name, client.serviceGroupName);
+          if (hostNames.length > 0) {
             this.get('content.additionalClients').pushObject({hostNames: hostNames, componentName: _clientName});
           }
         }
@@ -1372,9 +1385,9 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
     this.get('content.services').filterProperty('isSelected').forEach(function (service) {
       service.get('serviceComponents').filterProperty('isRequiredOnAllHosts').forEach(function (component) {
         if (service.get('isInstalled') && notInstalledHosts.length) {
-          this.registerHostsToComponent(notInstalledHosts.mapProperty('hostName'), component.get('componentName'));
+          this.registerHostsToComponent(notInstalledHosts.mapProperty('hostName'), component.componentName, component.serviceId, component.serviceGroupName);
         } else if (!service.get('isInstalled') && registeredHosts.length) {
-          this.registerHostsToComponent(registeredHosts.mapProperty('hostName'), component.get('componentName'));
+          this.registerHostsToComponent(registeredHosts.mapProperty('hostName'), component.componentName, component.serviceId, component.serviceGroupName);
         }
       }, this);
     }, this);
@@ -1383,10 +1396,11 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
     var hiveService = this.get('content.services').filterProperty('isSelected', true).filterProperty('isInstalled', false).findProperty('serviceName', 'HIVE');
     if (hiveService) {
       var hiveDb = this.get('content.serviceConfigProperties').findProperty('name', 'hive_database');
+      const hiveService = masterHosts.filterProperty('component', 'HIVE_SERVER');
       if (hiveDb.value === "New MySQL Database") {
-        this.registerHostsToComponent(masterHosts.filterProperty('component', 'HIVE_SERVER').mapProperty('hostName'), 'MYSQL_SERVER');
+        this.registerHostsToComponent(masterHosts.filterProperty('component', 'HIVE_SERVER').mapProperty('hostName'), 'MYSQL_SERVER', 'HIVE', hiveService.serviceGroupName);
       } else if (hiveDb.value === "New PostgreSQL Database") {
-        this.registerHostsToComponent(masterHosts.filterProperty('component', 'HIVE_SERVER').mapProperty('hostName'), 'POSTGRESQL_SERVER');
+        this.registerHostsToComponent(masterHosts.filterProperty('component', 'HIVE_SERVER').mapProperty('hostName'), 'POSTGRESQL_SERVER', 'HIVE', hiveService.serviceGroupName);
       }
     }
   },
@@ -1398,28 +1412,11 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
    * @param {String} componentName
    * @method registerHostsToComponent
    */
-  registerHostsToComponent: function (hostNames, componentName) {
+  registerHostsToComponent: function (hostNames, componentName, serviceName, serviceGroupName) {
     if (!hostNames.length) return;
 
-    let serviceName;
-    let serviceGroupName;
-
     const queryStr = `Hosts/host_name.in(${hostNames.join(',')})`;
 
-    let services;
-    if (this.get('isAddHost')) {
-      services = this.get('installedServices');
-    } else {
-      services = this.get('selectedServices');
-    }
-
-    services.forEach( function (service) {
-      if (service.get('serviceComponents').findProperty('componentName', componentName)) {
-        serviceName = service.get('serviceName');
-        serviceGroupName = service.get('stackName');
-      }
-    });
-
     var data = {
       "RequestInfo": {
         "query": queryStr
diff --git a/ambari-web/app/mappers/configs/stack_config_properties_mapper.js b/ambari-web/app/mappers/configs/stack_config_properties_mapper.js
index ceba40d..36a93f3 100644
--- a/ambari-web/app/mappers/configs/stack_config_properties_mapper.js
+++ b/ambari-web/app/mappers/configs/stack_config_properties_mapper.js
@@ -127,7 +127,7 @@ App.stackConfigPropertiesMapper = App.QuickDataMapper.create({
                 type : dep.StackConfigurationDependency.dependency_type,
                 name : dep.StackConfigurationDependency.dependency_name
               });
-              var service = App.StackService.find(config.StackConfigurations.service_name);
+              var service = App.StackService.find().findProperty('serviceName', config.StackConfigurations.service_name);
               var dependentService = App.config.get('serviceByConfigTypeMap')[dep.StackConfigurationDependency.dependency_type];
               if (dependentService && service && dependentService.get('serviceName') != service.get('serviceName') && !service.get('dependentServiceNames').contains(dependentService.get('serviceName'))) {
                 service.set('dependentServiceNames', service.get('dependentServiceNames').concat(dependentService.get('serviceName')));
@@ -136,7 +136,7 @@ App.stackConfigPropertiesMapper = App.QuickDataMapper.create({
           }
           if (Em.get(config, 'StackConfigurations.property_depends_on.length') > 0) {
             config.StackConfigurations.property_depends_on.forEach(function(dep) {
-              var service = App.StackService.find(config.StackConfigurations.service_name);
+              var service = App.StackService.find().findProperty('serviceName', config.StackConfigurations.service_name);
               var dependentService = App.config.get('serviceByConfigTypeMap')[dep.type];
               if (dependentService && service && dependentService.get('serviceName') != service.get('serviceName') && !service.get('dependentServiceNames').contains(dependentService.get('serviceName'))) {
                 service.set('dependentServiceNames', service.get('dependentServiceNames').concat(dependentService.get('serviceName')));
diff --git a/ambari-web/app/mappers/mpack_service_mapper.js b/ambari-web/app/mappers/mpack_service_mapper.js
index 72cffa3..3c02e9b 100644
--- a/ambari-web/app/mappers/mpack_service_mapper.js
+++ b/ambari-web/app/mappers/mpack_service_mapper.js
@@ -22,7 +22,7 @@ App.MpackServiceMapper = App.QuickDataMapper.create({
   component_model: App.StackServiceComponent,
 
   config: {
-    id: 'service_name',
+    id: 'id',
     stack_id: 'stack_id',
     service_name: 'service_name',
     service_type: 'service_type',
@@ -79,7 +79,6 @@ App.MpackServiceMapper = App.QuickDataMapper.create({
     var result = [];
     var stackServiceComponents = [];
     var nonInstallableServices = ['KERBEROS'];
-    var displayOrderLength = App.StackService.displayOrder.length;
     var stackService = service.StackServices;
     var serviceComponents = [];
     service.components.forEach(function (serviceComponent) {
@@ -95,7 +94,8 @@ App.MpackServiceMapper = App.QuickDataMapper.create({
       }
       stackServiceComponents.push(parsedResult);
     }, this);
-    stackService.stack_id = stackService.stack_name + '-' + stackService.stack_version;
+    stackService.stack_id = `${stackService.stack_name}-${stackService.stack_version}`;
+    stackService.id = `${stackService.service_name}-${stackService.stack_id}`;
     stackService.service_components = serviceComponents;
     stackService.is_service_with_widgets = service.artifacts.someProperty('Artifacts.artifact_name', 'widgets_descriptor');
     // @todo: replace with server response value after API implementation
diff --git a/ambari-web/app/mappers/stack_service_mapper.js b/ambari-web/app/mappers/stack_service_mapper.js
index a33bf65..7d4c9f7 100644
--- a/ambari-web/app/mappers/stack_service_mapper.js
+++ b/ambari-web/app/mappers/stack_service_mapper.js
@@ -22,7 +22,7 @@ App.stackServiceMapper = App.QuickDataMapper.create({
   component_model: App.StackServiceComponent,
 
   config: {
-    id: 'service_name',
+    id: 'id',
     stack_id: 'stack_id',
     service_name: 'service_name',
     service_type: 'service_type',
@@ -119,7 +119,8 @@ App.stackServiceMapper = App.QuickDataMapper.create({
         }
         stackServiceComponents.push(parsedResult);
       }, this);
-      stackService.stack_id = stackService.stack_name + '-' + stackService.stack_version;
+      stackService.stack_id = `${stackService.stack_name}-${stackService.stack_version}`;
+      stackService.id = `${stackService.service_name}-${stackService.stack_id}`;
       stackService.service_components = serviceComponents;
       stackService.is_service_with_widgets = item.artifacts.someProperty('Artifacts.artifact_name', 'widgets_descriptor');
       // @todo: replace with server response value after API implementation
diff --git a/ambari-web/app/mixins/wizard/assign_master_components.js b/ambari-web/app/mixins/wizard/assign_master_components.js
index ff878b2..69b7e2c 100644
--- a/ambari-web/app/mixins/wizard/assign_master_components.js
+++ b/ambari-web/app/mixins/wizard/assign_master_components.js
@@ -811,7 +811,6 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
   renderComponents: function (masterComponents) {
     var installedServices = App.StackService.find().filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'); //list of shown services
     var result = [];
-    var serviceComponentId, previousComponentName;
 
     this.addNewMasters(masterComponents);
 
@@ -822,7 +821,6 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
       if (masterComponent.get('isMasterWithMultipleInstances')) {
         showRemoveControl = installedServices.contains(masterComponent.get('stackService.serviceName')) &&
             (masterComponents.filterProperty('component_name', item.component_name).length > 1);
-        previousComponentName = item.component_name;
         componentObj.set('serviceComponentId', result.filterProperty('component_name', item.component_name).length + 1);
         componentObj.set("showRemoveControl", showRemoveControl);
       }
diff --git a/ambari-web/test/controllers/installer_test.js b/ambari-web/test/controllers/installer_test.js
index 42fc5d2..5aa19c3 100644
--- a/ambari-web/test/controllers/installer_test.js
+++ b/ambari-web/test/controllers/installer_test.js
@@ -824,7 +824,8 @@ describe('App.InstallerController', function () {
         {
           "component_name": "name",
           "display_name": "dname",
-          "isInstalled": false
+          "isInstalled": false,
+          "service_name": "i1"
         }
       ]);
     });
@@ -854,7 +855,8 @@ describe('App.InstallerController', function () {
             display_name: 'n1',
             component_name: 'c1',
             serviceId: 1,
-            selectedHost: 'h1'
+            selectedHost: 'h1',
+            mpackInstance: 'm1'
           })
         ])
       });
@@ -865,7 +867,8 @@ describe('App.InstallerController', function () {
           "component": "c1",
           "serviceId": 1,
           "isInstalled": false,
-          "host_id": 11
+          "host_id": 11,
+          "serviceGroupName": "m1"
         }
       ]);
     });
diff --git a/ambari-web/test/controllers/wizard/step5_test.js b/ambari-web/test/controllers/wizard/step5_test.js
index 1cd9902..3d74999 100644
--- a/ambari-web/test/controllers/wizard/step5_test.js
+++ b/ambari-web/test/controllers/wizard/step5_test.js
@@ -1014,7 +1014,9 @@ describe('App.WizardStep5Controller', function () {
         {
           fullComponent: Em.Object.create({
             componentName: 'c1',
-            serviceName: 's1'
+            serviceName: 's1',
+            serviceInstance: 'si1',
+            mpackInstance: 'm1'
           }),
           hostName: 'h1',
           mastersToMove: ['c1'],
@@ -1029,13 +1031,17 @@ describe('App.WizardStep5Controller', function () {
             serviceId: 's1',
             selectedHost: 'h2',
             isInstalled: true,
-            isServiceCoHost: false
+            isServiceCoHost: false,
+            serviceInstance: 'si1',
+            mpackInstance: 'm1'
           }
         },
         {
           fullComponent: Em.Object.create({
             componentName: 'c1',
-            serviceName: 's1'
+            serviceName: 's1',
+            serviceInstance: 'si1',
+            mpackInstance: 'm1'
           }),
           hostName: 'h1',
           mastersToMove: [],
@@ -1046,13 +1052,17 @@ describe('App.WizardStep5Controller', function () {
             serviceId: 's1',
             selectedHost: 'h1',
             isInstalled: false,
-            isServiceCoHost: false
+            isServiceCoHost: false,
+            serviceInstance: 'si1',
+            mpackInstance: 'm1'
           }
         },
         {
           fullComponent: Em.Object.create({
             componentName: 'c1',
-            serviceName: 's1'
+            serviceName: 's1',
+            serviceInstance: 'si1',
+            mpackInstance: 'm1'
           }),
           hostName: 'h1',
           mastersToMove: [],
@@ -1063,7 +1073,9 @@ describe('App.WizardStep5Controller', function () {
             serviceId: 's1',
             selectedHost: 'h1',
             isInstalled: false,
-            isServiceCoHost: true
+            isServiceCoHost: true,
+            serviceInstance: 'si1',
+            mpackInstance: 'm1'
           }
         }
       ]).forEach(function (test, i) {
diff --git a/ambari-web/test/mappers/stack_service_mapper_test.js b/ambari-web/test/mappers/stack_service_mapper_test.js
index 88b4b43..a381e9e 100644
--- a/ambari-web/test/mappers/stack_service_mapper_test.js
+++ b/ambari-web/test/mappers/stack_service_mapper_test.js
@@ -171,7 +171,7 @@ describe('App.stackServiceMapper', function () {
       },
       sortedServiceNames = ["HDFS", "HIVE", "ZOOKEEPER", "ACCUMULO", "KAFKA", "KERBEROS"],
       serviceResult = {
-        id: "KAFKA",
+        id: "KAFKA-HDP-2.2",
         serviceName: "KAFKA",
         displayName: "Kafka",
         configTypes: {
diff --git a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
index 98f3182..5c58037 100644
--- a/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
+++ b/ambari-web/test/mixins/common/configs/enhanced_configs_test.js
@@ -525,7 +525,6 @@ describe('App.EnhancedConfigsMixin', function () {
       sinon.stub(mock, 'onComplete');
       sinon.stub(mixin, 'getConfigRecommendationsParams').returns({});
       sinon.stub(mixin, 'modifyRecommendationConfigGroups');
-      sinon.stub(mixin, 'addRecommendationRequestParams');
       sinon.stub(mixin, 'getRecommendationsRequest');
       sinon.stub(mixin, 'loadAdditionalSites');
     });
@@ -534,7 +533,6 @@ describe('App.EnhancedConfigsMixin', function () {
       mock.onComplete.restore();
       mixin.getConfigRecommendationsParams.restore();
       mixin.modifyRecommendationConfigGroups.restore();
-      mixin.addRecommendationRequestParams.restore();
       mixin.getRecommendationsRequest.restore();
       mixin.loadAdditionalSites.restore();
     });
@@ -566,63 +564,33 @@ describe('App.EnhancedConfigsMixin', function () {
       mixin.loadConfigRecommendations([{}], mock.onComplete);
       expect(mixin.getConfigRecommendationsParams.calledWith(true)).to.be.true;
       expect(mixin.modifyRecommendationConfigGroups.calledOnce).to.be.true;
-      expect(mixin.addRecommendationRequestParams.calledOnce).to.be.true;
       expect(mixin.getRecommendationsRequest.calledOnce).to.be.true;
     });
   });
 
-  describe("#addRecommendationRequestParams()", function () {
-
-    beforeEach(function() {
-      sinon.stub(blueprintUtils, 'buildConfigsJSON').returns([{}]);
-    });
-
-    afterEach(function() {
-      blueprintUtils.buildConfigsJSON.restore();
-    });
-
-    it("recommendations should be set", function () {
-      var dataToSend = {};
-      mixin.addRecommendationRequestParams({blueprint: {}}, dataToSend, []);
-      expect(dataToSend).to.be.eql({
-        "recommendations": {
-          "blueprint": {
-            "configurations": [
-              {}
-            ]
-          }
-        }
-      });
-    });
-  });
-
   describe("#loadAdditionalSites()", function () {
 
     beforeEach(function() {
       sinon.stub(App.config, 'getConfigsByTypes').returns({
         done: function(callback) {callback([]);}
       });
-      sinon.stub(mixin, 'addRecommendationRequestParams');
       sinon.stub(mixin, 'getRecommendationsRequest');
+      sinon.stub(mixin, 'addRequestedConfigs');
       mixin.loadAdditionalSites([], [], {}, {}, Em.K);
     });
 
     afterEach(function() {
       App.config.getConfigsByTypes.restore();
-      mixin.addRecommendationRequestParams.restore();
       mixin.getRecommendationsRequest.restore();
+      mixin.addRequestedConfigs.restore();
     });
 
     it("App.config.getConfigsByTypes should be called", function() {
       expect(App.config.getConfigsByTypes.calledOnce).to.be.true;
     });
 
-    it("addRecommendationRequestParams should be called", function() {
-      expect(mixin.addRecommendationRequestParams.calledWith({}, {}, [])).to.be.true;
-    });
-
     it("getRecommendationsRequest should be called", function() {
-      expect(mixin.getRecommendationsRequest.calledWith({}, Em.K)).to.be.true;
+      expect(mixin.getRecommendationsRequest.calledOnce).to.be.true;
     });
   });
 
@@ -673,7 +641,6 @@ describe('App.EnhancedConfigsMixin', function () {
       expect(mixin.getConfigRecommendationsParams(true, [{}])).to.be.eql({
         recommend: 'configuration-dependencies',
         hosts: ['host1'],
-        services: ['S1'],
         changed_configurations: [{}]
       });
     });
@@ -684,7 +651,6 @@ describe('App.EnhancedConfigsMixin', function () {
       expect(mixin.getConfigRecommendationsParams(false, [{}])).to.be.eql({
         recommend: 'configurations',
         hosts: ['host1'],
-        services: ['S1'],
         changed_configurations: undefined
       });
     });
@@ -706,7 +672,7 @@ describe('App.EnhancedConfigsMixin', function () {
     it("App.ajax.send should be called", function() {
       mixin.getRecommendationsRequest({}, mock.callback);
       expect(mixin.get('recommendationsInProgress')).to.be.true;
-      var args = testHelpers.findAjaxRequest('name', 'config.recommendations');
+      var args = testHelpers.findAjaxRequest('name', 'mpack.advisor.recommendations');
       expect(args[0]).exists;
       args[0].callback();
       expect(mixin.get('recommendationsInProgress')).to.be.false;
@@ -847,7 +813,7 @@ describe('App.EnhancedConfigsMixin', function () {
   });
 
   describe("#loadRecommendationsSuccess()", function () {
-    var params = {dataToSend: {changed_configurations: []}};
+    var params = {data: {changed_configurations: []}};
 
     beforeEach(function() {
       sinon.stub(mixin, '_saveRecommendedValues');
@@ -962,7 +928,8 @@ describe('App.EnhancedConfigsMixin', function () {
           {
             recommendations: {
               blueprint: {
-                configurations: {}
+                configurations: {},
+                mpack_instances: []
               }
             }
           }
@@ -979,7 +946,8 @@ describe('App.EnhancedConfigsMixin', function () {
             recommendations: {
               'config-groups': [],
               blueprint: {
-                configurations: {}
+                configurations: {},
+                mpack_instances: []
               }
             }
           }
@@ -990,7 +958,8 @@ describe('App.EnhancedConfigsMixin', function () {
       expect(mixin.saveConfigGroupsRecommendations.calledWith({
         'config-groups': [],
         blueprint: {
-          configurations: {}
+          configurations: {},
+          mpack_instances: []
         }
       }, [])).to.be.true;
     });
@@ -1002,7 +971,8 @@ describe('App.EnhancedConfigsMixin', function () {
             recommendations: {
               'config-groups': [],
               blueprint: {
-                configurations: {}
+                configurations: {},
+                mpack_instances: []
               }
             }
           }
@@ -1022,7 +992,8 @@ describe('App.EnhancedConfigsMixin', function () {
             recommendations: {
               'config-groups': [],
               blueprint: {
-                configurations: {}
+                configurations: {},
+                mpack_instances: []
               }
             }
           }
@@ -1041,7 +1012,8 @@ describe('App.EnhancedConfigsMixin', function () {
           {
             recommendations: {
               blueprint: {
-                configurations: {}
+                configurations: {},
+                mpack_instances: []
               }
             }
           }
@@ -1467,5 +1439,58 @@ describe('App.EnhancedConfigsMixin', function () {
       expect(mixin.isConfigGroupAffected(['host1'], ['host2', 'host1'])).to.be.true;
     });
   });
+
+  describe('#addRequestedConfigs', function () {
+    it('adds the configs to include in the request to the recommendations.blueprint object in the format required', function () {
+      sinon.stub(blueprintUtils, 'buildConfigsJSON', function (configs) { return configs[0]; });
+      mixin.set('wizardController', App.InstallerController.create({
+        content: {
+          selectedServices: [
+            {
+              name: 'OTHER',
+              mpackName: 'MPACK',
+              mpackVersion: '1.0',
+            }
+          ]
+        }
+      }));
+
+      const actual = { blueprint: {} };
+      const configs = [
+        Em.Object.create({
+          serviceName: 'MISC',
+          configs: ['misc1', 'misc2']
+        }),
+        Em.Object.create({
+          serviceName: 'OTHER',
+          configs: ['other1', 'other2']
+        })
+      ];
+      const expected = {
+        blueprint: {
+          configurations: configs[0],
+          mpack_instances: [
+            {
+              name: 'MPACK',
+              type: 'MPACK',
+              version: '1.0',
+              service_instances: [
+                {
+                  name: 'OTHER',
+                  type: 'OTHER',
+                  configurations: configs[1]
+                }
+              ]
+            }
+          ]
+        }
+      };
+
+      mixin.addRequestedConfigs(actual, configs);
+      expect(actual).to.deep.equal(expected);
+
+      blueprintUtils.buildConfigsJSON.restore();
+    });
+  });
 });
 
diff --git a/ambari-web/test/mixins/common/hosts/host_component_recommendation_mixin_test.js b/ambari-web/test/mixins/common/hosts/host_component_recommendation_mixin_test.js
index 13db571..451cccf 100644
--- a/ambari-web/test/mixins/common/hosts/host_component_recommendation_mixin_test.js
+++ b/ambari-web/test/mixins/common/hosts/host_component_recommendation_mixin_test.js
@@ -76,7 +76,6 @@ describe('App.HostComponentRecommendationMixin', function() {
       {
         opts: null,
         e: {
-          services: [],
           hosts: [],
           components: [],
           blueprint: null
@@ -85,13 +84,11 @@ describe('App.HostComponentRecommendationMixin', function() {
       },
       {
         opts: {
-          services: ['s1'],
           hosts: ['h1'],
           components: [{componentName: 'c1'}],
           blueprint: {recommend:{}}
         },
         e: {
-          services: ['s1'],
           hosts: ['h1'],
           components: [{componentName: 'c1'}],
           blueprint: {recommend:{}}
@@ -109,9 +106,9 @@ describe('App.HostComponentRecommendationMixin', function() {
   describe('#loadComponentsRecommedationsFromServer', function() {
     it('default request options checking', function() {
       mixedObject.loadComponentsRecommendationsFromServer('someData');
-      var args = helpers.findAjaxRequest('name', 'config.recommendations');
+      var args = helpers.findAjaxRequest('name', 'mpack.advisor.recommendations');
       expect(args[0]).to.be.eql({
-        name: 'config.recommendations',
+        name: 'mpack.advisor.recommendations',
         sender: mixedObject,
         data: 'someData',
         success: 'loadRecommendationsSuccessCallback',
@@ -131,20 +128,34 @@ describe('App.HostComponentRecommendationMixin', function() {
       {
         options: {
           hosts: ['h1'],
-          services: ['s1'],
-          components: [Em.Object.create({componentName: 'c1', hostName: 'h1'})],
-          blueprint: null
+          components: [Em.Object.create({
+            componentName: 'c1',
+            hostName: 'h1',
+            mpackInstance: 'm1',
+            serviceInstance: 'si1'
+          })],
+          blueprint: null,
+          mpack_instances: []
         },
         e: {
-          dataToSend: {
+          data: {
             recommend: 'host_groups',
             hosts: ['h1'],
-            services: ['s1'],
             recommendations: {
               blueprint: {
                 host_groups: [
-                  { name: 'host-group-1', components: [{ name: 'c1' }] }
-                ]
+                  {
+                    name: 'host-group-1',
+                    components: [
+                      {
+                        name: 'c1',
+                        mpack_instance: 'm1',
+                        service_instance: 'si1'
+                      }
+                    ]
+                  }
+                ],
+                mpack_instances: []
               },
               blueprint_cluster_binding: {
                 host_groups: [
@@ -152,28 +163,27 @@ describe('App.HostComponentRecommendationMixin', function() {
                 ]
               }
             }
-          },
-          stackVersionUrl: '/stack/url'
+          }
         },
         m: 'when blueprint not passed it should be generated from components list'
       },
       {
         options: {
           hosts: ['h1'],
-          services: ['s1'],
           components: [Em.Object.create({componentName: 'c1', hostName: 'h1'})],
-          blueprint: { blueprint: {}}
+          blueprint: { blueprint: {} },
+          mpack_instances: []
         },
         e: {
-          dataToSend: {
+          data: {
             recommend: 'host_groups',
             hosts: ['h1'],
-            services: ['s1'],
             recommendations: {
-              blueprint: {}
+              blueprint: {
+                mpack_instances: []
+              }
             }
-          },
-          stackVersionUrl: '/stack/url'
+          }
         },
         m: 'when blueprint passed it should be used instead of generated blueprint'
       }
diff --git a/ambari-web/test/mixins/common/hosts/host_component_validation_mixin_test.js b/ambari-web/test/mixins/common/hosts/host_component_validation_mixin_test.js
index 9589f90..236210c 100644
--- a/ambari-web/test/mixins/common/hosts/host_component_validation_mixin_test.js
+++ b/ambari-web/test/mixins/common/hosts/host_component_validation_mixin_test.js
@@ -76,7 +76,6 @@ describe('App.HostComponentValidationMixin', function() {
       {
         opts: null,
         e: {
-          services: [],
           hosts: [],
           components: [],
           blueprint: null
@@ -85,13 +84,11 @@ describe('App.HostComponentValidationMixin', function() {
       },
       {
         opts: {
-          services: ['s1'],
           hosts: ['h1'],
           components: [{componentName: 'c1'}],
           blueprint: {recommend:{}}
         },
         e: {
-          services: ['s1'],
           hosts: ['h1'],
           components: [{componentName: 'c1'}],
           blueprint: {recommend:{}}
@@ -109,9 +106,9 @@ describe('App.HostComponentValidationMixin', function() {
   describe('#getHostComponentValidationRequest', function() {
     it('default request options checking', function() {
       mixedObject.getHostComponentValidationRequest('someData');
-      var args = helpers.findAjaxRequest('name', 'config.validations');
+      var args = helpers.findAjaxRequest('name', 'mpack.advisor.validations');
       expect(args[0]).to.be.eql({
-        name: 'config.validations',
+        name: 'mpack.advisor.validations',
         sender: mixedObject,
         data: 'someData',
         success: 'updateValidationsSuccessCallback',
@@ -131,25 +128,39 @@ describe('App.HostComponentValidationMixin', function() {
       {
         options: {
           hosts: ['h1'],
-          services: ['s1'],
-          components: [Em.Object.create({componentName: 'c1', hostName: 'h1'})],
-          blueprint: null
+          components: [
+            Em.Object.create({
+              componentName: 'c1',
+              hostName: 'h1',
+              mpackInstance: 'm1',
+              serviceInstance: 'si1'
+            })
+          ],
+          blueprint: null,
+          mpack_instances: []
         },
         e: {
-          validate: 'host_groups',
-          stackVersionUrl: '/stack/url',
-          hosts: ['h1'],
-          services: ['s1'],
-          recommendations: {
-            blueprint: {
-              host_groups: [
-                {name: 'host-group-1',components: [{name: 'c1'}]}
-              ]
-            },
-            blueprint_cluster_binding: {
-              host_groups: [
-                {name: 'host-group-1', hosts: [{fqdn: 'h1'}]}
-              ]
+          data: {
+            validate: 'host_groups',
+            hosts: ['h1'],
+            recommendations: {
+              blueprint: {
+                host_groups: [
+                  {
+                    name: 'host-group-1', components: [{
+                      name: 'c1',
+                      mpack_instance: 'm1',
+                      service_instance: 'si1' 
+                    }]
+                  }
+                ],
+                mpack_instances: []
+              },
+              blueprint_cluster_binding: {
+                host_groups: [
+                  { name: 'host-group-1', hosts: [{ fqdn: 'h1' }] }
+                ]
+              }
             }
           }
         },
@@ -158,17 +169,19 @@ describe('App.HostComponentValidationMixin', function() {
       {
         options: {
           hosts: ['h1'],
-          services: ['s1'],
           components: [Em.Object.create({componentName: 'c1', hostName: 'h1'})],
-          blueprint: { blueprint: {}}
+          blueprint: { blueprint: {} },
+          mpack_instances: []
         },
         e: {
-          validate: 'host_groups',
-          stackVersionUrl: '/stack/url',
-          hosts: ['h1'],
-          services: ['s1'],
-          recommendations: {
-            blueprint: {}
+          data: {
+            validate: 'host_groups',
+            hosts: ['h1'],
+            recommendations: {
+              blueprint: {
+                mpack_instances: []
+              }
+            }
           }
         },
         m: 'when blueprint passed it should be used instead of generated blueprint'

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