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/10/01 18:44:30 UTC

[ambari] branch branch-feature-AMBARI-14714 updated: [AMBARI-24630] Redo Add Service wizard (#2310)

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

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


The following commit(s) were added to refs/heads/branch-feature-AMBARI-14714 by this push:
     new 58b315d  [AMBARI-24630] Redo Add Service wizard (#2310)
58b315d is described below

commit 58b315da7b41a8cedb7d18ed80bbc18600a0afde
Author: Jason Golieb <j...@golieb.net>
AuthorDate: Mon Oct 1 14:44:26 2018 -0400

    [AMBARI-24630] Redo Add Service wizard (#2310)
    
    * [AMBARI-24630] Revamped Add Service wizard
    
    * Fixes for install wizard
---
 ambari-web/app/controllers/installer.js            |  89 +-
 .../app/controllers/main/host/add_controller.js    |   8 +-
 .../app/controllers/main/service/add_controller.js | 901 +++++++++++----------
 ambari-web/app/controllers/wizard.js               | 191 +++--
 .../wizard/customMpackRepos_controller.js          |   2 +-
 .../wizard/downloadMpacks_controller.js            |  16 +-
 .../controllers/wizard/selectMpacks_controller.js  |  99 ++-
 .../app/controllers/wizard/step7_controller.js     |  19 +-
 .../app/controllers/wizard/step8_controller.js     | 125 ++-
 ambari-web/app/mappers/service_mapper.js           |  45 +-
 ambari-web/app/mappers/service_metrics_mapper.js   |   2 +
 ambari-web/app/mixins/common/blueprint.js          |   5 +-
 .../common/configs/config_recommendations.js       |  17 +-
 .../hosts/host_component_recommendation_mixin.js   |   2 -
 .../app/mixins/wizard/assign_master_components.js  |   8 +-
 ambari-web/app/router.js                           |   6 +-
 ambari-web/app/routes/add_service_routes.js        | 790 +++++++++++++-----
 ambari-web/app/routes/installer.js                 |   3 +-
 ambari-web/app/templates/main/service/add.hbs      |  23 +-
 .../app/templates/wizard/configureDownload.hbs     |  14 +-
 ambari-web/app/templates/wizard/selectMpacks.hbs   |  16 +-
 ambari-web/app/utils/ajax/ajax.js                  |  24 +-
 ambari-web/app/utils/config.js                     |   4 +-
 .../main/service/add_controller_test.js            | 861 ++++++++++----------
 .../test/controllers/wizard/selectMpacks_test.js   | 109 ++-
 ambari-web/test/controllers/wizard_test.js         | 102 ++-
 .../configs/config_recommendation_parser_test.js   |   6 +-
 .../host_component_recommendation_mixin_test.js    |  21 +-
 .../hosts/host_component_validation_mixin_test.js  |   8 +-
 29 files changed, 2083 insertions(+), 1433 deletions(-)

diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js
index 8a26167..f9e5e05 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -18,8 +18,6 @@
 
 
 var App = require('app');
-var stringUtils = require('utils/string_utils');
-var validator = require('utils/validator');
 
 App.InstallerController = App.WizardController.extend(App.Persist, {
 
@@ -70,17 +68,6 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     this.set('errors', []);
   },
 
-  getStepController: function (stepName) {
-    if (typeof (stepName) === "number") {
-      stepName = this.get('steps')[stepName];
-    }
-
-    stepName = stepName.charAt(0).toUpperCase() + stepName.slice(1);
-    const stepController = App.router.get('wizard' + stepName + 'Controller');
-
-    return stepController;
-  },
-
   isInstallerWizard: true,
 
   content: Em.Object.create({
@@ -153,6 +140,8 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     'operatingSystems',
     'repositories',
     'selectedMpacks',
+    'mpacksToRegister',
+    'allMpacks',
     'selectedServices',
     'selectedStack',
     'downloadConfig',
@@ -201,7 +190,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     this.setDBProperty('hosts', dbHosts);
   },
 
-  cancelInstall: function() {
+  cancelInstall: function () {
     return App.showConfirmationPopup(() => {
       App.router.get('applicationController').goToAdminView();
     });
@@ -402,7 +391,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         this.getSupportedOSList(versionDefinition, stackInfo, params.dfd);
       }, this);
     } else {
-      App.showAlertPopup(Em.I18n.t('common.error'), Em.I18n.t('installer.step1.noVersionDefinitions'), function() {
+      App.showAlertPopup(Em.I18n.t('common.error'), Em.I18n.t('installer.step1.noVersionDefinitions'), function () {
         App.router.send('back');
       });
     }
@@ -760,8 +749,8 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
       this.showStackErrorAndSkipStepIfNeeded(response);
       return;
     }
-    response.operating_systems.forEach(function(supportedOS) {
-      if(!existedMap[supportedOS.OperatingSystems.os_type]) {
+    response.operating_systems.forEach(function (supportedOS) {
+      if (!existedMap[supportedOS.OperatingSystems.os_type]) {
         supportedOS.isSelected = false;
         existedOS.push(supportedOS);
       } else {
@@ -778,7 +767,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
             });
           });
         }
-        else{
+        else {
           existedMap[supportedOS.OperatingSystems.os_type].repositories.forEach(function (repo) {
             supportedOS.repositories.forEach(function (supportedRepo) {
               if (supportedRepo.Repositories.repo_id == repo.Repositories.repo_id) {
@@ -821,11 +810,11 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
   getSupportedOSListErrorCallback: function (request, ajaxOptions, error, data, params) {
     var header = Em.I18n.t('installer.step1.useLocalRepo.getSurpottedOs.error.title');
     var body = "";
-    if(request && request.responseText){
+    if (request && request.responseText) {
       try {
         var json = $.parseJSON(request.responseText);
         body = json.message;
-      } catch (err) {}
+      } catch (err) { }
     }
     App.showAlertPopup(header, body);
   },
@@ -842,9 +831,9 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         repoVersionId: repoToUpdate.id,
         repoVersion: repoVersion
       }
-    }).success(function() {
+    }).success(function () {
       deferred.resolve([]);
-    }).error(function() {
+    }).error(function () {
       deferred.resolve([]);
     });
     return deferred.promise();
@@ -856,7 +845,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
    * @param {Em.Object} repo
    * @returns {{operating_systems: Array}}
    */
-  prepareRepoForSaving: function(repo) {
+  prepareRepoForSaving: function (repo) {
     var repoVersion = { "operating_systems": [] };
     var ambariManagedRepositories = !repo.get('useRedhatSatellite');
     var k = 0;
@@ -879,7 +868,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
                 "components": repository.get('components'),
                 "tags": repository.get('tags'),
                 "distribution": repository.get('distribution'),
-                "applicable_services" : repository.get('applicable_services')
+                "applicable_services": repository.get('applicable_services')
               }
             });
           }
@@ -987,6 +976,18 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     params.dfd.reject();
   },
 
+  /**
+   * In addition to the default behavior, we want to always treat mpacks
+   * as being added in the current wizard when are in the Install wizard.
+   */
+  loadRegisteredMpacksCallback: function (response) {
+    this._super(response);
+
+    const registeredMpacks = this.get('content.registeredMpacks');
+    registeredMpacks.forEach(mpack => mpack.currentWizard = true);
+    this.setDBProperty('registeredMpacks', registeredMpacks);
+  },
+
   loadMap: {
     'step0': [
       {
@@ -1299,22 +1300,6 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     });
   },
 
-  setStepsEnable: function () {
-    const steps = this.get('steps');
-    for (let i = 0, length = steps.length; i < length; i++) {
-      let stepDisabled = true;
-      
-      const stepController = this.getStepController(steps[i]);
-      if (stepController) {
-        stepController.set('wizardController', this);
-        stepDisabled = stepController.isStepDisabled();
-      }
-
-      const stepIndex = this.getStepIndex(steps[i]);
-      this.get('isStepDisabled').findProperty('step', stepIndex).set('value', stepDisabled);
-    }
-  },
-
   clearStackServices: function (deleteAll) {
     var dfd = $.Deferred();
 
@@ -1412,6 +1397,28 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
       dfd.resolve();
     });
     
-    return dfd;
+    return dfd.promise();
+  },
+  
+  /**
+   * Load config themes for enhanced config layout.
+   *
+   * @method loadConfigThemes
+   * @return {$.Deferred}
+   */
+  loadConfigThemes: function () {
+    const self = this;
+    const dfd = $.Deferred();
+    
+    if (!this.get('stackConfigsLoaded')) {
+      // Load stack configs before loading themes
+      App.config.loadClusterConfigsFromStack().always(self.loadServiceConfigs.bind(self)).always(() => {
+        dfd.resolve();
+      });
+    } else {
+      dfd.resolve();
+    }
+
+    return dfd.promise();
   }
 });
diff --git a/ambari-web/app/controllers/main/host/add_controller.js b/ambari-web/app/controllers/main/host/add_controller.js
index 2d8ade3..aba6d2d 100644
--- a/ambari-web/app/controllers/main/host/add_controller.js
+++ b/ambari-web/app/controllers/main/host/add_controller.js
@@ -59,7 +59,8 @@ App.AddHostController = App.WizardController.extend({
     advancedServiceConfig: null,
     controllerName: 'addHostController',
     serviceConfigGroups: null,
-    configGroups: null
+    configGroups: null,
+    registeredMpacks: []
   }),
 
   /**
@@ -399,11 +400,6 @@ App.AddHostController = App.WizardController.extend({
     this.set('content.cluster', this.getCluster());
   },
 
-  clearStorageData: function () {
-    this._super();
-    this.resetDbNamespace();
-  },
-
   /**
    * Clear all temporary data
    */
diff --git a/ambari-web/app/controllers/main/service/add_controller.js b/ambari-web/app/controllers/main/service/add_controller.js
index ee84342..afe0207 100644
--- a/ambari-web/app/controllers/main/service/add_controller.js
+++ b/ambari-web/app/controllers/main/service/add_controller.js
@@ -18,13 +18,39 @@
 
 
 var App = require('app');
-App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, {
+
+App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, App.Persist, {
 
   name: 'addServiceController',
 
-  totalSteps: 7,
+  isCheckInProgress: false,
 
-  /**
+  totalSteps: function () {
+    const steps = this.get("steps");
+
+    if (steps) {
+      return steps.length;
+    }
+
+    return 0;
+  }.property('steps.[]'),
+
+  steps: [
+    "configureDownload",
+    "selectMpacks",
+    "customMpackRepos",
+    "downloadMpacks",
+    "customProductRepos",
+    "verifyProducts",
+    "step5",
+    "step6",
+    "step7",
+    "step8",
+    "step9",
+    "step10"
+  ],
+
+    /**
    * @type {string}
    */
   displayName: Em.I18n.t('services.add.header'),
@@ -34,307 +60,212 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, {
    */
   hideBackButton: true,
 
-  /**
-   * @type {string}
-   * @default null
-   */
-  serviceToInstall: null,
+  errors: [],
 
-  /**
-   *
-   */
-  installClientQueueLength: 0,
+  hasErrors: function () {
+    return this.get('errors').length > 0;
+  }.property('errors'),
 
-  areInstalledConfigGroupsLoaded: false,
+  addError: function (newError) {
+    const errors = this.get('errors');
+    this.set('errors', errors.concat(newError));
+  },
+
+  clearErrors: function () {
+    this.set('errors', []);
+  },
 
-  /**
-   * All wizards data will be stored in this variable
-   *
-   * cluster - cluster name
-   * installOptions - ssh key, repo info, etc.
-   * services - services list
-   * hosts - list of selected hosts
-   * slaveComponentHosts, - info about slave hosts
-   * masterComponentHosts - info about master hosts
-   * config??? - to be described later
-   */
   content: Em.Object.create({
     cluster: null,
-    hosts: null,
     installOptions: null,
+    hosts: null,
     services: null,
     slaveComponentHosts: null,
     masterComponentHosts: null,
     serviceConfigProperties: null,
     advancedServiceConfig: null,
-    controllerName: 'addServiceController',
     configGroups: [],
+    slaveGroupProperties: null,
+    stacks: null,
     clients: [],
-    additionalClients: [],
-    installedHosts: {}
+    // list of components, that was added from configs page via AssignMasterOnStep7Controller
+    componentsFromConfigs: [],
+    /**
+     * recommendations for host groups loaded from server
+     */
+    recommendations: null,
+    /**
+     * recommendationsHostGroups - current component assignment after 5 and 6 steps,
+     * or adding hiveserver2 interactive on "configure services" page
+     * (uses for host groups validation and to load recommended configs)
+     */
+    recommendationsHostGroups: null,
+    controllerName: 'addServiceController',
+    mpacks: [],
+    registeredMpacks: [],
+    mpackVersions: [],
+    mpackServiceVersions: [],
+    mpackServices: [],
+    serviceGroups: [],
+    serviceInstances: [],
+    // Tracks which steps have been saved before.
+    // If you revisit a step, we will know if the step has been saved previously and we can warn about making changes.
+    // If a previously saved step is changed, setStepSaved() will "unsave" all subsequent steps so we don't warn on every screen.
+    // Furthermore, we only need to track this state for steps that have an affect on subsequent steps.
+    stepsSavedState: null
   }),
 
-  loadMap: {
-    '1': [
-      {
-        type: 'sync',
-        callback: function () {
-          this.loadServices();
-        }
-      }
-    ],
-    '2': [
-      {
-        type: 'async',
-        callback: function () {
-          var dfd = $.Deferred();
-          var self = this;
-          this.loadHosts().done(function () {
-            self.loadMasterComponentHosts().done(function () {
-              self.load('hosts');
-              self.loadRecommendations();
-              dfd.resolve();
-            });
-          });
-          return dfd.promise();
-        }
-      }
-    ],
-    '3': [
-      {
-        type: 'async',
-        callback: function () {
-          var dfd = $.Deferred();
-          var self = this;
-          this.loadHosts().done(function () {
-            self.loadServices();
-            self.loadClients();
-            self.loadSlaveComponentHosts();//depends on loadServices
-            dfd.resolve();
-          });
-          return dfd.promise();
-        }
-      }
-    ],
-    '4': [
-      {
-        type: 'async',
-        callback: function () {
-          var self = this;
-          var dfd = $.Deferred();
-          this.load('cluster');
-          this.set('content.additionalClients', []);
-          this.set('installClientQueueLength', 0);
-          this.set('installClietsQueue', App.ajaxQueue.create({abortOnError: false}));
-          this.loadKerberosDescriptorConfigs().done(function() {
-            self.loadServiceConfigGroups();
-            self.loadConfigThemes().then(function() {
-              self.loadServiceConfigProperties();
-              self.loadCurrentHostGroups();
-              dfd.resolve();
-            });
-          });
-          return dfd.promise();
-        }
-      }
-    ]
-  },
-
-  setCurrentStep: function (currentStep, completed) {
-    this._super(currentStep, completed);
-    App.clusterStatus.setClusterStatus({
-      wizardControllerName: this.get('name'),
-      localdb: App.db.data
-    });
-  },
+  /**
+   * Wizard properties in local storage, which should be cleaned right after wizard has been finished
+   */
+  dbPropertiesToClean: [
+    'service',
+    'hosts',
+    'masterComponentHosts',
+    'slaveComponentHosts',
+    'cluster',
+    'allHostNames',
+    'installOptions',
+    'allHostNamesPattern',
+    'serviceComponents',
+    'clients',
+    'selectedServiceNames',
+    'serviceConfigGroups',
+    'serviceConfigProperties',
+    'fileNamesToUpdate',
+    'bootStatus',
+    'stacksVersions',
+    'currentStep',
+    'serviceInfo',
+    'hostInfo',
+    'recommendations',
+    'recommendationsHostGroups',
+    'recommendationsConfigs',
+    'componentsFromConfigs',
+    'operatingSystems',
+    'repositories',
+    'selectedMpacks',
+    'mpacksToRegister',
+    'selectedServices',
+    'selectedStack',
+    'downloadConfig',
+    'stepsSavedState',
+    'serviceGroups',
+    'addedServiceGroups',
+    'serviceInstances',
+    'addedServiceInstances',
+    'registeredMpacks'
+  ],
+
+  init: function () {
+    this._super();
 
-  loadCurrentHostGroups: function () {
-    this.set("content.recommendationsHostGroups", this.getDBProperty('recommendationsHostGroups'));
+    //enable first step, which is at index 0 in this wizard
+    const stepAtIndex0 = this.get('isStepDisabled').findProperty('step', 0)
+    if (stepAtIndex0) {
+      stepAtIndex0.set('value', false);
+    }
   },
 
   /**
-   * Load services data. Will be used at <code>Select services(step4)</code> step
+   * Load data for services selected from mpacks. Will be used at <code>Download Mpacks</code> step submit action.
+   *
+   * @param  {string} stackName
+   * @param  {string} stackVersion
+   * @param  {string} serviceName
    */
-  loadServices: function () {
-    var services = this.getDBProperty('services');
-    var stackServices = App.StackService.find();
-    if (services) {
-      stackServices.forEach(function (item) {
-        var isSelected = services.selectedServices.contains(item.get('serviceName')) || item.get('serviceName') == this.get('serviceToInstall');
-        var isInstalled = services.installedServices.contains(item.get('serviceName'));
-        item.set('isSelected', isSelected || (this.get("currentStep") == "1" ? isInstalled : isSelected));
-        item.set('isInstalled', isInstalled);
-      }, this);
-      this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected').filterProperty('isInstalled', false), 3);
-    } else {
-      services = {
-        selectedServices: [],
-        installedServices: []
-      };
-      stackServices.forEach(function (item) {
-        var isInstalled = App.Service.find().someProperty('id', item.get('serviceName'));
-        var isSelected = item.get('serviceName') === this.get('serviceToInstall') || item.get('coSelectedServices').contains(this.get('serviceToInstall'));
-        item.set('isSelected', isInstalled || isSelected);
-        item.set('isInstalled', isInstalled);
-        var serviceName = item.get('serviceName');
-        if (isInstalled) {
-          services.selectedServices.push(serviceName);
-          services.installedServices.push(serviceName);
-        }
-        else {
-          if (isSelected) {
-            services.selectedServices.push(serviceName);
-          }
-        }
-      }, this);
-      this.setDBProperty('services', services);
-    }
-    App.store.fastCommit();
-    this.set('serviceToInstall', null);
-    this.set('content.services', stackServices);
-    var self = this;
-    this.loadServiceVersionFromVersionDefinitions().complete(function () {
-      var serviceVersionsMap = self.get('serviceVersionsMap');
-      stackServices.forEach(function (stackService) {
-        var serviceName = stackService.get('serviceName');
-        Em.set(stackService, 'serviceVersionDisplay', serviceVersionsMap[serviceName]);
-      });
-      self.set('content.services', stackServices);
+  loadMpackServiceInfo: function (stackName, stackVersion, serviceName) {
+    return App.ajax.send({
+      name: 'wizard.mpack_service_components',
+      sender: this,
+      data: {
+        stackName: stackName,
+        stackVersion: stackVersion,
+        serviceName: serviceName
+      }
     });
   },
 
-  /**
-   * Save data to model
-   * @param stepController App.WizardStep4Controller
-   */
-  saveServices: function (stepController) {
-    var services = {
-      selectedServices: [],
-      installedServices: []
-    };
-    var selectedServices = stepController.get('content').filterProperty('isSelected', true).filterProperty('isInstalled', false);
-    var selectedServiceNames = selectedServices.mapProperty('serviceName');
-    services.selectedServices.pushObjects(selectedServiceNames);
-    services.installedServices.pushObjects(stepController.get('content').filterProperty('isInstalled', true).mapProperty('serviceName'));
-    // save services that already installed but ignored on choose services page
-    // these services marked by `isInstallable` flag with value `false`, for example `Kerberos` service
-    services.installedServices.pushObjects(App.Service.find().mapProperty('serviceName').filter(function(serviceName) {
-      return !services.installedServices.contains(serviceName);
-    }));
-    this.setDBProperty('services', services);
-
-    this.set('content.selectedServiceNames', selectedServiceNames);
-    this.setDBProperty('selectedServiceNames', selectedServiceNames);
-    this.setSkipSlavesStep(selectedServices, 3);
+  loadMpackServiceInfoSuccess: function (serviceInfo) {
+    serviceInfo.StackServices.is_selected = true;
+    App.MpackServiceMapper.map(serviceInfo);
+  },
+
+  loadMpackServiceInfoError: function (request, status, error) {
+    console.log(`${message} ${status} - ${error}`);
+    
+    const message = Em.I18n.t('installer.error.mpackServiceInfo');
+    this.addError(message);
+    return message;   
   },
 
+  allServiceGroups: function () {
+    return [].concat(this.get('content.serviceGroups')).concat(this.get('content.addedServiceGroups'));
+  }.property('content.serviceGroups', 'content.addedServiceGroups'),
+
   /**
    * Save Master Component Hosts data to Main Controller
    * @param stepController App.WizardStep5Controller
+   * @param  skip  {Boolean}
    */
-  saveMasterComponentHosts: function (stepController) {
-    var obj = stepController.get('selectedServicesMasters');
-    var masterComponentHosts = [];
-    var installedComponents = App.HostComponent.find();
+  saveMasterComponentHosts: function (stepController, skip) {
+    var obj = stepController.get('selectedServicesMasters'),
+      hosts = this.getDBProperty('hosts');
 
+    var masterComponentHosts = [];
     obj.forEach(function (_component) {
-      var installedComponent = installedComponents.findProperty('componentName', _component.component_name);
       masterComponentHosts.push({
-        display_name: _component.display_name,
-        component: _component.component_name,
-        hostName: _component.selectedHost,
-        serviceId: _component.serviceId,
-        isInstalled: !!installedComponent,
-        workStatus: installedComponent && installedComponent.get('workStatus')
+        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
       });
     });
 
-    this.setDBProperty('masterComponentHosts', masterComponentHosts);
     this.set('content.masterComponentHosts', masterComponentHosts);
-
-    this.set('content.skipMasterStep', this.get('content.masterComponentHosts').everyProperty('isInstalled', true));
-    this.get('isStepDisabled').findProperty('step', 2).set('value', this.get('content.skipMasterStep'));
+    if (!skip) {
+      this.setDBProperty('masterComponentHosts', masterComponentHosts);
+    }
   },
 
   /**
    * Load master component hosts data for using in required step controllers
+   * @param inMemory {Boolean}: Load master component hosts from memory
    */
-  loadMasterComponentHosts: function () {
-    var self = this,
-        dfd = $.Deferred(),
-        ASSIGN_MASTER_STEP = 2,
-        DEPLOYMENT_STEPS = ['6', '7', '8'];
-    this._super().done(function () {
-      self.set('content.skipMasterStep', App.StackService.find().filterProperty('isSelected').filterProperty('hasMaster').everyProperty('isInstalled', true));
-      self.get('isStepDisabled').findProperty('step', ASSIGN_MASTER_STEP).set('value', self.get('content.skipMasterStep') || DEPLOYMENT_STEPS.contains(self.get('currentStep')));
-      dfd.resolve();
-    });
-    return dfd.promise();
-  },
-
-  /**
-   * Does service have any configs
-   * @param {string} serviceName
-   * @returns {boolean}
-   */
-  isServiceNotConfigurable: function (serviceName) {
-    return App.get('services.noConfigTypes').contains(serviceName);
-  },
-
-  /**
-   * Should Config Step be skipped (based on selected services list)
-   * @returns {boolean}
-   */
-  skipConfigStep: function () {
-    var skipConfigStep = true;
-    var selectedServices = this.get('content.services').filterProperty('isSelected', true).filterProperty('isInstalled', false).mapProperty('serviceName');
-    selectedServices.map(function (serviceName) {
-      skipConfigStep = skipConfigStep && this.isServiceNotConfigurable(serviceName);
-    }, this);
-    return skipConfigStep;
-  },
+  loadMasterComponentHosts: function (lookInMemoryOnly) {
+    var props = this.getDBProperties(['masterComponentHosts', 'hosts']),
+      masterComponentHosts = this.get("content.masterComponentHosts"),
+      hosts = props.hosts || {},
+      hostNames = Em.keys(hosts);
 
-  loadServiceConfigProperties: function () {
-    this._super();
-    if (!this.get('content.services')) {
-      this.loadServices();
-    }
-    if (this.get('currentStep') > 1 && this.get('currentStep') < 6) {
-      this.set('content.skipConfigStep', this.skipConfigStep());
-      this.get('isStepDisabled').findProperty('step', 4).set('value', this.get('content.skipConfigStep'));
+    if (!lookInMemoryOnly && !masterComponentHosts) {
+      masterComponentHosts = props.masterComponentHosts;
     }
-  },
 
-  /**
-   * Load kerberos descriptor configuration
-   * @returns {$.Deferred}
-   */
-  loadKerberosDescriptorConfigs: function() {
-    var self = this,
-        dfd = $.Deferred(),
-        mergedDescriptorConfigs;
-    if (App.get('isKerberosEnabled')) {
-      this.loadClusterDescriptorStackConfigs().then(function (stackProperties) {
-        self.loadClusterDescriptorConfigs().then(function(properties) {
-          self.set('kerberosDescriptorData', properties);
-          mergedDescriptorConfigs = self.mergeDescriptorStackWithConfigs(stackProperties, properties);
-          self.set('kerberosDescriptorConfigs', mergedDescriptorConfigs);
-        }).always(function(){
-          dfd.resolve();
-        });
-      });
+    if (Em.isNone(masterComponentHosts)) {
+      masterComponentHosts = [];
     } else {
-      dfd.resolve();
+      masterComponentHosts.forEach(function (component) {
+        for (var i = 0; i < hostNames.length; i++) {
+          if (hosts[hostNames[i]].id === component.host_id) {
+            component.hostName = hostNames[i];
+            break;
+          }
+        }
+      });
     }
-    return dfd.promise();
+    this.set("content.masterComponentHosts", masterComponentHosts);
   },
 
-  saveServiceConfigProperties: function (stepController) {
-    this._super(stepController);
-    if (this.get('currentStep') > 1 && this.get('currentStep') < 6) {
-      this.set('content.skipConfigStep', this.skipConfigStep());
-      this.get('isStepDisabled').findProperty('step', 4).set('value', this.get('content.skipConfigStep'));
-    }
+  loadCurrentHostGroups: function () {
+    this.set("content.recommendations", this.getDBProperty('recommendations'));
+    this.set("content.recommendationsHostGroups", this.getDBProperty('recommendationsHostGroups'));
+  },
+
+  loadRecommendationsConfigs: function () {
+    App.router.set("wizardStep7Controller.recommendationsConfigs", this.getDBProperty('recommendationsConfigs'));
   },
 
   /**
@@ -348,7 +279,6 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, {
     if (!Em.isNone(slaveComponentHosts)) {
       slaveComponentHosts.forEach(function (component) {
         component.hosts.forEach(function (host) {
-          //Em.set(host, 'hostName', hosts[host.host_id].name);
           for (var i = 0; i < hostNames.length; i++) {
             if (hosts[hostNames[i]].id === host.host_id) {
               host.hostName = hostNames[i];
@@ -358,212 +288,337 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, {
         });
       });
     }
-
-    this.set('content.installedHosts', this.getDBProperty('hosts') || this.get('content.hosts'));
     this.set("content.slaveComponentHosts", slaveComponentHosts);
   },
 
-  /**
-   * Generate clients list for selected services and save it to model
-   */
-  saveClients: function () {
-    var clients = [];
-    var serviceComponents = App.StackServiceComponent.find();
-    this.get('content.services').filterProperty('isSelected').filterProperty('isInstalled',false).forEach(function (_service) {
-      var serviceClients = serviceComponents.filterProperty('serviceName', _service.get('serviceName')).filterProperty('isClient');
-      serviceClients.forEach(function (client) {
-        clients.push({
-          component_name: client.get('componentName'),
-          display_name: client.get('displayName'),
-          isInstalled: false
-        });
-      }, this);
-    }, this);
+  loadMap: {
+    'selectMpacks': [
+      {
+        type: 'async',
+        callback: function () {
+          const self = this;
+          return this.loadRegisteredMpacks()
+          .done(this.loadServiceGroups.bind(this))
+          .done(() => {
+            self.load('stepsSavedState');
+            self.load('cluster');
+            self.load('selectedServices');
+            self.load('selectedMpacks');
+            self.load('addedServiceGroups');
+            self.load('addedServiceInstances');
+            self.load('advancedMode');
+            
+            const dfd = $.Deferred();
+            dfd.resolve();
+            return dfd.promise();
+          });
+        }
+      }
+    ],
+    'customProductRepos': [
+      {
+        type: 'async',
+        callback: function () {
+          return this.loadSelectedServiceInfo(this.getStepSavedState('customProductRepos'));
+        }
+      },
+    ],
+    'step3': [
+      {
+        type: 'async',
+        callback: function () {
+          return this.loadHosts();
+        }
+      }
+    ],
+    'step5': [
+      {
+        type: 'sync',
+        callback: function () {
+          this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), this.getStepIndex('step7'));
+          this.loadMasterComponentHosts();
+          this.loadConfirmedHosts();
+          this.loadComponentsFromConfigs();
+          this.loadRecommendations();
+        }
+      }
+    ],
+    'step6': [
+      {
+        type: 'sync',
+        callback: function () {
+          this.loadSlaveComponentHosts();
+          this.loadClients();
+          this.loadComponentsFromConfigs();
+          this.loadRecommendations();
+        }
+      }
+    ],
+    'step7': [
+      {
+        type: 'async',
+        callback: function () {
+          var dfd = $.Deferred();
+          var self = this;
+          this.loadServiceConfigGroups();
+          this.loadCurrentHostGroups();
+          this.loadRecommendationsConfigs();
+          this.loadComponentsFromConfigs();
+          this.loadConfigThemes().then(function() {
+            self.loadServiceConfigProperties();
+            dfd.resolve();
+          });
+          return dfd.promise();
+        }
+      }
+    ],
+    'step8': [
+      {
+        type: 'sync',
+        callback: function () {
+          this.load('selectedStack');
+        }
+      }
+    ]
+  },
 
-    this.setDBProperty('clients', clients);
-    this.set('content.clients', clients);
+  gotoStep5: function () {
+    this.gotoStep('step5');
   },
 
-  /**
-   * Load information about hosts with clients components
-   */
-  loadClients: function () {
-    var clients = this.getDBProperty('clients');
-    if (clients) {
-      this.set('content.clients', clients);
-    } else {
-      this.saveClients();
-    }
+  gotoStep6: function () {
+    this.gotoStep('step6');
   },
 
-  /**
-   * Remove all loaded data.
-   * Created as copy for App.router.clearAllSteps
-   */
-  clearAllSteps: function () {
-    this.clearInstallOptions();
-    // clear temporary information stored during the install
-    this.set('content.cluster', this.getCluster());
+  gotoStep7: function () {
+    this.gotoStep('step7');
   },
 
-  /**
-   * Clear all temporary data
-   */
-  finish: function () {
-    this.clearAllSteps();
-    this.clearStorageData();
-    this.clearServiceConfigProperties();
-    this.resetDbNamespace();
-    App.router.get('updateController').updateAll();
+  gotoStep8: function () {
+    this.gotoStep('step8');
   },
 
-  /**
-   * genarates data for ajax request to launch install services
-   * @method generateDataForInstallServices
-   * @param {Array} selectedServices
-   * @returns {{context: *, ServiceInfo: {state: string}, urlParams: string}}
-   */
-  generateDataForInstallServices: function(selectedServices) {
-    if (selectedServices.contains('OOZIE')) {
-      selectedServices = selectedServices.concat(['HDFS', 'YARN', 'MAPREDUCE2']);
-    }
-    return {
-      "context": Em.I18n.t('requestInfo.installServices'),
-      "ServiceInfo": {"state": "INSTALLED"},
-      "urlParams": "ServiceInfo/service_name.in(" + selectedServices.join(',') + ")"
-    };
+  gotoStep9: function () {
+    this.gotoStep('step9');
   },
 
-  /**
-   * main method for installing additional clients and services
-   * @param {function} callback
-   * @method installServices
-   */
-  installServices: function (callback) {
-    var self = this;
-    this.set('content.cluster.oldRequestsId', []);
-    this.installAdditionalClients().done(function () {
-      self.installSelectedServices(callback);
-    });
+  gotoStep10: function () {
+    this.gotoStep('step10');
   },
 
-  /**
-   * method to install added services
-   * @param {function} callback
-   * @method installSelectedServices
-   */
-  installSelectedServices: function (callback) {
-    var name = 'common.services.update';
-    var selectedServices = this.get('content.services').filterProperty('isInstalled', false).filterProperty('isSelected', true).mapProperty('serviceName');
-    var dependentServices = this.getServicesBySelectedSlaves();
-    var data = this.generateDataForInstallServices(selectedServices.concat(dependentServices));
-    this.installServicesRequest(name, data, callback.bind(this));
+  gotoConfigureDownload: function () {
+    this.gotoStep('configureDownload');
+  },
+  
+  gotoSelectMpacks: function () {
+    this.gotoStep('selectMpacks');
   },
 
-  installServicesRequest: function (name, data, callback) {
-    callback = callback || Em.K;
-    App.ajax.send({
-      name: name,
-      sender: this,
-      data: data,
-      success: 'installServicesSuccessCallback',
-      error: 'installServicesErrorCallback'
-    }).then(callback, callback);
+  gotoCustomMpackRepos: function () {
+    this.gotoStep('customMpackRepos');
   },
 
-  /**
-   * return list of services by selected and not installed slave components
-   * @returns {Array}
-   */
-  getServicesBySelectedSlaves: function () {
-    var result = [];
-    this.get('content.slaveComponentHosts').forEach(function (slaveComponent) {
-      if (slaveComponent.hosts.someProperty('isInstalled', false)) {
-        var stackComponent = App.StackServiceComponent.find().findProperty('componentName', slaveComponent.componentName);
-        if (stackComponent) {
-          result.push(stackComponent.get('serviceName'));
-        }
-      }
-    });
-    return result.uniq();
+  gotoDownloadMpacks: function () {
+    this.gotoStep('downloadMpacks');
+  },
+
+  gotoCustomProductRepos: function () {
+    this.gotoStep('customProductRepos');
+  },
+
+  gotoVerifyProducts: function () {
+    this.gotoStep('verifyProducts');
+  },
+
+  isStep5: function () {
+    return this.get('currentStep') == this.getStepIndex('step5');
+  }.property('currentStep'),
+
+  isStep6: function () {
+    return this.get('currentStep') == this.getStepIndex('step6');
+  }.property('currentStep'),
+
+  isStep7: function () {
+    return this.get('currentStep') == this.getStepIndex('step7');
+  }.property('currentStep'),
+
+  isStep8: function () {
+    return this.get('currentStep') == this.getStepIndex('step8');
+  }.property('currentStep'),
+
+  isStep9: function () {
+    return this.get('currentStep') == this.getStepIndex('step9');
+  }.property('currentStep'),
+
+  isStep10: function () {
+    return this.get('currentStep') == this.getStepIndex('step10');
+  }.property('currentStep'),
+
+  isConfigureDownload: function () {
+    return this.get('currentStep') == this.getStepIndex('configureDownload');
+  }.property('currentStep'),
+
+  isSelectMpacks: function () {
+    return this.get('currentStep') == this.getStepIndex('selectMpacks');
+  }.property('currentStep'),
+
+  isCustomMpackRepos: function () {
+    return this.get('currentStep') == this.getStepIndex('customMpackRepos');
+  }.property('currentStep'),
+
+  isDownloadMpacks: function () {
+    return this.get('currentStep') == this.getStepIndex('downloadMpacks');
+  }.property('currentStep'),
+
+  isCustomProductRepos: function () {
+    return this.get('currentStep') == this.getStepIndex('customProductRepos');
+  }.property('currentStep'),
+
+  isVerifyProducts: function () {
+    return this.get('currentStep') == this.getStepIndex('verifyProducts');
+  }.property('currentStep'),
+
+  clearConfigActionComponents: function() {
+    var masterComponentHosts = this.get('content.masterComponentHosts');
+    var componentsAddedFromConfigAction = this.get('content.componentsFromConfigs');
+
+    if (componentsAddedFromConfigAction && componentsAddedFromConfigAction.length) {
+      componentsAddedFromConfigAction.forEach(function(_masterComponent){
+        masterComponentHosts = masterComponentHosts.rejectProperty('component', _masterComponent);
+      });
+    }
+    this.set('content.masterComponentHosts', masterComponentHosts);
+    this.setDBProperty('masterComponentHosts', masterComponentHosts);
   },
 
   /**
-   * installs clients before install new services
-   * on host where some components require this
-   * @method installAdditionalClients
+   * Clear all temporary data
    */
-  installAdditionalClients: function () {
+  finish: function () {
+    this.setCurrentStep('0');
+    this.clearStorageData();
+    this.clearServiceConfigProperties();
+    App.router.get('userSettingsController').postUserPref('show_bg', true);
+    App.themesMapper.resetModels();
+  },
+
+  clearStackServices: function (deleteAll) {
     var dfd = $.Deferred();
-    var count = 0;
-    if (this.get('content.additionalClients.length') > 0) {
-      this.get('content.additionalClients').forEach(function (c) {
-        if (c.hostNames.length > 0) {
-          var queryStr = 'HostRoles/component_name='+ c.componentName + '&HostRoles/host_name.in(' + c.hostNames.join() + ')';
-          this.get('installClietsQueue').addRequest({
-            name: 'common.host_component.update',
-            sender: this,
-            data: {
-              query: queryStr,
-              context: 'Install ' + App.format.role(c.componentName, false),
-              HostRoles: {
-                state: 'INSTALLED'
-              },
-              counter: count++,
-              deferred: dfd
-            },
-            success: 'installClientSuccess',
-            error: 'installClientError'
+
+    if (deleteAll) {
+      const stackServices = App.StackService.find();
+      let stackServicesCount = stackServices.content.length;
+
+      if (stackServicesCount > 0) {
+        stackServices.forEach(service => {
+          Em.run.once(this, () => {
+            App.MpackServiceMapper.deleteRecord(service);
+            stackServicesCount--;
+
+            if (stackServicesCount === 0) {
+              dfd.resolve();
+            }
           });
-        }
-      }, this);
-      if (!this.get('installClietsQueue.queue.length')) {
-        return dfd.resolve();
-      }
-      this.set('installClientQueueLength', this.get('installClietsQueue.queue.length'));
-      App.get('router.wizardStep8Controller').set('servicesInstalled', true);
-      this.get('installClietsQueue').start();
+        });
+      } else {
+        dfd.resolve();
+      }  
     } else {
       dfd.resolve();
-    }
+    }  
+
     return dfd.promise();
   },
 
   /**
-   * callback for when install clients success
-   * @param data
-   * @param opt
-   * @param params
-   * @method installClientComplete
+   * Updates the stepsSaved array based on the stepName provided.
+   * If the passed step is already saved, then nothing is changed.
+   * Otherwise, the passed step is set to saved and all subsequent steps are set to unsaved.
+   *
+   * @param  {type} stepName Name of the step being saved.
    */
-  installClientSuccess: function(data, opt, params) {
-    if (this.get('installClientQueueLength') - 1 === params.counter) {
-      params.deferred.resolve();
+  setStepSaved: function (stepName) {
+    const stepIndex = this.getStepIndex(stepName);
+    const oldState = this.get('content.stepsSavedState') || {};
+    const newState = Em.Object.create(oldState);
+
+    if (!newState[stepIndex]) {
+      for (let i = stepIndex + 1, length = this.get('steps').length; i < length; i++) {
+        newState[i] = false;
+      };
+
+      newState[stepIndex] = true;
+
+      this.set('content.stepsSavedState', newState);
+      this.save('stepsSavedState');
     }
   },
 
   /**
-   * callback for when install clients fail
-   * @param request
-   * @param ajaxOptions
-   * @param error
-   * @param opt
-   * @param params
+   * Populates the StackService model from the "stack" info that was created when mpacks were registered in the Download Mpack step.
+   * Then, it locally persists info about the selected services.
+   *
+   * @param {Boolean} keepStackServices If true, previously loaded stack services are retained.
+   *                                    This is to support back/forward navigation in the wizard
+   *                                    and should correspond to the saved state of the step after Download Mpacks.
+   * @return {object} a promise
    */
-  installClientError: function(request, ajaxOptions, error, opt, params) {
-    if (this.get('installClientQueueLength') - 1 === params.counter) {
-      params.deferred.resolve();
-    }
-  },
+  loadSelectedServiceInfo: function (keepStackServices) {
+    var dfd = $.Deferred();
 
-  loadServiceConfigGroups: function () {
-    this._super();
-    this.set('areInstalledConfigGroupsLoaded', !Em.isNone(this.getDBProperty('serviceConfigGroups')));
+    this.clearStackServices(!keepStackServices).then(() => {
+      //get info about services from specific stack versions and save to StackService model
+      this.set('content.selectedServiceNames', this.getDBProperty('selectedServiceNames'));
+      const selectedServices = this.get('content.selectedServices');
+      const servicePromises = selectedServices.map(service =>
+        this.loadMpackServiceInfo(service.mpackName, service.mpackVersion, service.name)
+          .then(this.loadMpackServiceInfoSuccess.bind(this), this.loadMpackServiceInfoError.bind(this))
+      );
+
+      return $.when(...servicePromises);
+    }).then(() => {
+      const services = App.StackService.find();
+      this.set('content.services', services);
+
+      const clients = [];
+      services.forEach(service => {
+        const client = service.get('serviceComponents').filterProperty('isClient', true);
+        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
+          });
+        });
+      });
+      this.set('content.clients', clients);
+      this.save('clients');
+
+      dfd.resolve();
+    });
+    
+    return dfd.promise();
   },
 
-  clearStorageData: function () {
-    this._super();
-    this.set('areInstalledConfigGroupsLoaded', false);
-  }
+  /**
+   * Load config themes for enhanced config layout.
+   *
+   * @method loadConfigThemes
+   * @return {$.Deferred}
+   */
+  loadConfigThemes: function () {
+    const dfd = $.Deferred();
+    
+    if (!this.get('stackConfigsLoaded')) {
+      this.loadServiceConfigs().always(() => {
+        dfd.resolve();
+      });
+    } else {
+      dfd.resolve();
+    }
 
+    return dfd.promise();
+  }
 });
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index ca7a71e..49a4a07 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -49,9 +49,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     'componentsFromConfigs',
     'stepsSavedState',
     'operatingSystems',
-    'repositories',
-    'selectedMpacks',
-    'selectedServices'
+    'repositories'
   ],
 
   sensibleConfigs: [
@@ -149,16 +147,32 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     return hosts;
   }.property('content.hosts'),
 
+  getStepController: function (stepName) {
+    if (typeof (stepName) === "number") {
+      stepName = this.get('steps')[stepName];
+    }
+
+    stepName = stepName.charAt(0).toUpperCase() + stepName.slice(1);
+    const stepController = App.router.get('wizard' + stepName + 'Controller');
+
+    return stepController;
+  },
+
   setStepsEnable: function () {
-    for (var i = 1; i <= this.get('totalSteps'); i++) {
-      var step = this.get('isStepDisabled').findProperty('step', i);
-      if (i <= this.get('currentStep')) {
-        step.set('value', false);
-      } else {
-        step.set('value', true);
+    const steps = this.get('steps');
+    for (let i = 0, length = steps.length; i < length; i++) {
+      let stepDisabled = true;
+      
+      const stepController = this.getStepController(steps[i]);
+      if (stepController) {
+        stepController.set('wizardController', this);
+        stepDisabled = stepController.isStepDisabled();
       }
+
+      const stepIndex = this.getStepIndex(steps[i]);
+      this.get('isStepDisabled').findProperty('step', stepIndex).set('value', stepDisabled);
     }
-  }.observes('currentStep', 'totalSteps'),
+  },
 
   /**
    * Enable step link in left nav menu
@@ -177,16 +191,9 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     }
   },
 
-  /**
-   * Set current step to new value.
-   * Method moved from App.router.setInstallerCurrentStep
-   * @param currentStep
-   * @param completed
-   */
   currentStep: function () {
     return App.get('router').getWizardCurrentStep(this.get('name').substr(0, this.get('name').length - 10));
-  }.property(),
-
+  }.property().volatile(),
 
   /**
    * Get the wizard type based on the wizard name set in the specific controller.
@@ -199,12 +206,11 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
   }.property('name'),
 
   /**
-   * Get the name of the current step.
+   * Get step name by index.
    *
    * @return {string}
    */
-  currentStepName: function () {
-    const index = this.get('currentStep');
+  getStepName: function (index) {
     const steps = this.get('steps');
 
     if (steps) {
@@ -213,12 +219,50 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
 
     //legacy support
     return 'step' + index;
-  }.property('steps', 'currentStep'),
+  },
+
+  /**
+   * Get the name of the first step.
+   *
+   * @return {string}
+   */
+  firstStepName: function () {
+    return this.getStepName(0);
+  }.property('steps'),
+
+  /**
+   * Get the name of the last step.
+   *
+   * @return {string}
+   */
+  lastStepName: function () {
+    const steps = this.get('steps');
+    if (steps) {
+      return this.getStepName(steps.length - 1);
+    }
+
+    //legacy support
+    const totalSteps = this.get('totalSteps');
+    if (totalSteps) {
+      return this.getStepName(totalSteps);
+    }
+
+    return null;
+  }.property('steps'),
+
+  /**
+   * Get the name of the current step.
+   *
+   * @return {string}
+   */
+  currentStepName: function () {
+    const index = this.get('currentStep');
+    return this.getStepName(index);
+  }.property().volatile(),
 
   /**
    * Set current step to new value.
    * If no new value is provided, it sets current step to same value.
-   * Method moved from App.router.setInstallerCurrentStep
    * @param currentStep
    * @param completed
    */
@@ -230,7 +274,6 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
 
     this.set('previousStep', this.get('currentStep'));
     App.db.setWizardCurrentStep(this.get('wizardType'), index, completed);
-    this.set('currentStep', index);
   },
 
   getPreviousStepName: function () {
@@ -723,7 +766,6 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     this.set('content', Ember.Object.create({
       'controllerName': this.get('content.controllerName')
     }));
-    this.set('currentStep', 0);
     this.clearStorageData();
   },
 
@@ -745,6 +787,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
       hash[key] = undefined;
     }, this);
     this.setDBProperties(hash);
+    this.resetDbNamespace();
   },
 
   getInstallOptions: function() {
@@ -1356,7 +1399,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
    */
   loadClients: function () {
     var clients = this.getDBProperty('clients');
-    this.set('content.clients', clients);
+    this.set('content.clients', clients || []);
   },
 
   /**
@@ -1399,8 +1442,8 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
    * If not, it is loaded from the server and stored in both localStorage and the controller's content.
    */
   loadRegisteredMpacks: function () {
-    let dfd;
-    let registeredMpacks = this.getDBProperty('registeredMpacks');
+    const dfd = $.Deferred();
+    const registeredMpacks = this.getDBProperty('registeredMpacks');
     
     if (registeredMpacks) {
       this.set('content.registeredMpacks', registeredMpacks);
@@ -1410,15 +1453,16 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
         App.stackMapper.map(JSON.parse(JSON.stringify(rmp)));
       });
 
-      dfd = $.Deferred();
       dfd.resolve();
     } else {
-      dfd = App.ajax.send({
+      App.ajax.send({
         name: 'mpack.get_registered_mpacks',
         sender: this,
         success: 'loadRegisteredMpacksCallback',
         error: 'defaultErrorCallback'
-      });
+      })
+      .done(() => dfd.resolve())
+      .fail(() => dfd.reject());
     }
 
     return dfd.promise();
@@ -1480,21 +1524,24 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
   },
 
   loadHosts: function () {
-    var dfd;
-    var hostsInDb = this.getDBProperty('hosts');
+    const dfd = $.Deferred();
+    const hostsInDb = this.getDBProperty('hosts');
+    
     if (hostsInDb) {
       this.set('content.hosts', hostsInDb);
-      dfd = $.Deferred();
       dfd.resolve();
     } else {
-      dfd = App.ajax.send({
+      App.ajax.send({
         name: 'hosts.confirmed',
         sender: this,
         data: {},
         success: 'loadHostsSuccessCallback',
         error: 'defaultErrorCallback'
-      });
+      })
+        .done(() => dfd.resolve())
+        .fail(() => dfd.reject());
     }
+
     return dfd.promise();
   },
 
@@ -1520,22 +1567,24 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
    * If not, it is loaded from the server and stored in both localStorage and the controller's content.
    */
   loadServiceGroups: function () {
-    let dfd;
+    const dfd = $.Deferred();  
     const serviceGroups = this.getDBProperty('serviceGroups');
     const serviceInstances = this.getDBProperty('serviceInstances');
 
     if (serviceGroups && serviceInstances) {
       this.set('content.serviceGroups', serviceGroups);
       this.set('content.serviceInstances', serviceInstances);
-      dfd = $.Deferred();
+      
       dfd.resolve();
     } else {
-      dfd = App.ajax.send({
+      App.ajax.send({
         name: 'servicegroup.get_all_details',
         sender: this,
         success: 'loadServiceGroupsCallback',
         error: 'loadServiceGroupsErrorCallback'
-      });
+      })
+      .done(() => dfd.resolve())
+      .fail(() => dfd.reject());
     }
 
     return dfd.promise();
@@ -1547,8 +1596,8 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
   loadServiceGroupsCallback: function (response) {
     const serviceGroups = response.items.map(item =>
       ({
-        name: item.service_group_name,
-        mpackVersionId: item.mpack_name + item.mpack_version
+        name: item.ServiceGroupInfo.service_group_name,
+        mpackVersionId: `${item.ServiceGroupInfo.mpack_name}-${item.ServiceGroupInfo.mpack_version}`
       })
     );
     this.setDBProperty('serviceGroups', serviceGroups);
@@ -1558,7 +1607,8 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
       serviceInstances.concat(item.services.map(service => 
         ({
           name: service.ServiceInfo.service_name,
-          serviceGroupName: service.ServiceInfo.service_group_name
+          serviceGroupName: service.ServiceInfo.service_group_name,
+          serviceName: service.ServiceInfo.service_name
         })
       ))
     , []);
@@ -1644,42 +1694,33 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     return result;
   },
 
-  /**
-   * Load config themes for enhanced config layout.
-   *
-   * @method loadConfigThemes
-   * @return {$.Deferred}
-   */
-  loadConfigThemes: function () {
+  loadServiceConfigs: function () {
     const self = this;
     const dfd = $.Deferred();
-    
-    if (!this.get('stackConfigsLoaded')) {
-      // Load stack configs before loading themes
-      App.config.loadClusterConfigsFromStack().always(() => {
-        const mpacks = self.get('content.selectedMpacks');
-        const configPromises = mpacks.map(mpack => {
-          const stackName = mpack.name;
-          const stackVersion = mpack.version;
-          
-          const serviceNames = App.StackService.find().filter(service => {
-            return service.get('stackName') === stackName && service.get('stackVersion') === stackVersion
-              && (service.get('isSelected') || service.get('isInstalled'));
-          }).mapProperty('serviceName');
-
-          return App.config.loadConfigsFromStack(serviceNames, stackName, stackVersion)
-            .done(App.config.saveConfigsToModel)
-            .done(() => self.loadConfigThemeForServices(serviceNames, stackName, stackVersion));
-        });
+    const mpacks = this.get('content.selectedMpacks');
 
-        $.when(...configPromises).always(() => {
-          self.set('stackConfigsLoaded', true);
-          dfd.resolve();
-        });  
-      });
-    } else {
+    const configPromises = mpacks.map(mpack => {
+      const stackName = mpack.name;
+      const stackVersion = mpack.version;
+      
+      const serviceNames = App.StackService.find().filter(service => {
+        return service.get('stackName') === stackName && service.get('stackVersion') === stackVersion
+          && (service.get('isSelected') || service.get('isInstalled'));
+      }).mapProperty('serviceName');
+
+      if (serviceNames.length > 0) {
+        return App.config.loadConfigsFromStack(serviceNames, stackName, stackVersion)
+          .done(App.config.saveConfigsToModel)
+          .done(() => self.loadConfigThemeForServices(serviceNames, stackName, stackVersion));
+      } else {
+        return $.Deferred().resolve().promise();
+      }
+    });
+
+    $.when(...configPromises).always(() => {
+      self.set('stackConfigsLoaded', true);
       dfd.resolve();
-    }
+    });
 
     return dfd.promise();
   },
diff --git a/ambari-web/app/controllers/wizard/customMpackRepos_controller.js b/ambari-web/app/controllers/wizard/customMpackRepos_controller.js
index 276881e..7e05aa0 100644
--- a/ambari-web/app/controllers/wizard/customMpackRepos_controller.js
+++ b/ambari-web/app/controllers/wizard/customMpackRepos_controller.js
@@ -24,7 +24,7 @@ App.WizardCustomMpackReposController = App.WizardStepController.extend({
 
   stepName: 'customMpackRepos',
 
-  mpacks: Em.computed.alias('content.selectedMpacks'),
+  mpacks: Em.computed.alias('content.mpacksToRegister'),
   
   isStepDisabled: function (stepIndex, currentIndex) {
     const normallyDisabled = this._super(stepIndex, currentIndex);
diff --git a/ambari-web/app/controllers/wizard/downloadMpacks_controller.js b/ambari-web/app/controllers/wizard/downloadMpacks_controller.js
index 06764e9..aca89a4 100644
--- a/ambari-web/app/controllers/wizard/downloadMpacks_controller.js
+++ b/ambari-web/app/controllers/wizard/downloadMpacks_controller.js
@@ -26,10 +26,17 @@ App.WizardDownloadMpacksController = App.WizardStepController.extend({
 
   mpacks: [],
 
+  isStepDisabled: function (stepIndex, currentIndex) {
+    const normallyDisabled = this._super(stepIndex, currentIndex);
+    const mpacksToRegister = this.get('wizardController.content.mpacksToRegister') || [];
+
+    return normallyDisabled || !mpacksToRegister.length;
+  },
+
   addMpacks: function () {
-    const selectedMpacks = this.get('content.selectedMpacks');
+    const mpacksToRegister = this.get('content.mpacksToRegister');
 
-    selectedMpacks.forEach(mpack => {
+    mpacksToRegister.forEach(mpack => {
       this.get('mpacks').pushObject(Em.Object.create({
         name: mpack.name,
         version: mpack.version,
@@ -106,7 +113,10 @@ App.WizardDownloadMpacksController = App.WizardStepController.extend({
         data: {
           id: id
         }
-      }).then(mpackInfo => registeredMpacks.push(mpackInfo));
+      }).then(mpackInfo => {
+        mpackInfo.currentWizard = true;
+        registeredMpacks.push(mpackInfo);
+      });
     }  
   },
 
diff --git a/ambari-web/app/controllers/wizard/selectMpacks_controller.js b/ambari-web/app/controllers/wizard/selectMpacks_controller.js
index 12e3f0f..c6694ed 100644
--- a/ambari-web/app/controllers/wizard/selectMpacks_controller.js
+++ b/ambari-web/app/controllers/wizard/selectMpacks_controller.js
@@ -36,18 +36,8 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
 
   filterServicesPlaceholder: Em.I18n.t('installer.selectMpacks.filterServices'),
 
-  /** 
-   * Mpacks already registered on the server.
-   */
-  registeredMpacks: Em.computed.alias('content.registeredMpacks'),
-
-  /**
-   * Mpacks selected to be registered via the current wizard.
-   */
-  selectedMpacks: Em.computed.alias('content.selectedMpacks'),
-
   /**
-   * Service instances already existin in the cluster.
+   * Service instances already existing in the cluster.
    */
   serviceInstances: [],
 
@@ -84,21 +74,21 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     return this.get('serviceGroups').concat(this.get('addedServiceGroups'));
   }.property('serviceGroups.@each', 'addedServiceGroups.@each'),
 
-  selectedUseCases: function selectedUseCases() {
+  selectedUseCases: function () {
     return this.get('content.mpackUseCases').filterProperty('selected');
   }.property('content.mpackUseCases.@each.selected'),
 
-  selectedServices: function selectedServices() {
+  selectedServices: function () {
     const mpackServiceVersions = this.get('content.mpackServiceVersions');
     return mpackServiceVersions ? mpackServiceVersions.filter(s => s.get('selected') === true) : [];
   }.property('content.mpackServiceVersions.@each.selected'),
 
-  selectedMpackVersions: function selectedMpackVersions() {
+  selectedMpackVersions: function () {
     const versions = this.get('content.mpackVersions');
     return versions ? versions.filter(v => v.get('selected') === true) : [];
-  }.property('content.mpackVersions.@each.selected', 'selectedServices'),
+  }.property('content.mpackVersions.@each.selected'),
 
-  isSaved: function isSaved() {
+  isSaved: function () {
     const wizardController = this.get('wizardController');
     if (wizardController) {
       return wizardController.getStepSavedState('selectMpacks');
@@ -106,12 +96,11 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     return false;
   }.property('wizardController.content.stepsSavedState'),
 
-  isSubmitDisabled: function isSubmitDisabled() {
-    const mpackServiceVersions = this.get('content.mpackServiceVersions');
+  isSubmitDisabled: function () {
     return App.get('router.btnClickInProgress')
       || (this.get('wizardController.errors') && this.get('wizardController.errors').length > 0)
-      || mpackServiceVersions.filterProperty('selected', true).length === 0;
-  }.property('content.mpackServiceVersions.@each.selected', 'App.router.btnClickInProgress', 'wizardController.errors'),
+      || this.get('addedServiceInstances.length') === 0;
+  }.property('addedServiceInstances.length', 'App.router.btnClickInProgress', 'wizardController.errors'),
 
   //#endregion
 
@@ -173,7 +162,7 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
               return Em.Object.create({
                 selected: false,
                 displayed: index === 0 ? true : false, //by default, display first version
-                id: mpack.RegistryMpackInfo.mpack_name + version.RegistryMpackVersionInfo.mpack_version,
+                id: `${mpack.RegistryMpackInfo.mpack_name}-${version.RegistryMpackVersionInfo.mpack_version}`,
                 version: version.RegistryMpackVersionInfo.mpack_version,
                 mpackUrl: version.RegistryMpackVersionInfo.mpack_uri,
                 logoUrl: version.RegistryMpackVersionInfo.mpack_logo_uri,
@@ -182,7 +171,7 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
                   return Em.Object.create({
                     selected: false,
                     displayed: index === 0 ? true : false, //by default, display first version
-                    id: mpack.RegistryMpackInfo.mpack_name + version.RegistryMpackVersionInfo.mpack_version + service.name,
+                    id: `${mpack.RegistryMpackInfo.mpack_name}-${version.RegistryMpackVersionInfo.mpack_version}-${service.name}`,
                     name: service.name,
                     displayName: service.displayName,
                     version: service.version
@@ -315,6 +304,25 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     return deferred.promise();
   },
 
+  /**
+   * Updates the loaded registry state based on the service groups and instances in the cluster.
+   */
+  updateRegistry: function () {
+    const serviceGroups = this.get('serviceGroups');
+    if (serviceGroups) {
+      serviceGroups.forEach(serviceGroup => {
+        serviceGroup.set('mpackVersion.selected', true);
+      })
+    }
+
+    const serviceInstances = this.get('serviceInstances');
+    if (serviceInstances) {
+      serviceInstances.forEach(serviceInstance => {
+        serviceInstance.set('service.selected', true);
+      })
+    }
+  },
+
   //#endregion
 
   //#region Helpers
@@ -456,7 +464,8 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     
     if (recommendations && recommendations.length > 0 && recommendations[0].mpacks && recommendations[0].mpacks.length > 0) {
       recommendations[0].mpacks.forEach(mpack => {
-        const serviceGroup = this.addServiceGroup(mpack.mpack_name + mpack.mpack_version, mpack.mpack_name);
+        const serviceGroup = this.getServiceGroup(mpack.mpack_name) || this.addServiceGroup(`${mpack.mpack_name}-${mpack.mpack_version}`, mpack.mpack_name); //TODO: this is hard-coding the service group name
+        serviceGroup.mpackVersion.set('selected', true);
         serviceGroup.mpackVersion.services.forEach(service => this.addServiceInstance(service.name, service.name, serviceGroup));
       });
     } else {
@@ -568,7 +577,7 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
    */
   createServiceInstance: function (name, service, serviceGroup, canRemove) {
     return Em.Object.create({
-      id: serviceGroup.get('name') + name,
+      id: `${serviceGroup.get('name')}-${name}`,
       name: name,
       service: service,
       serviceGroup: serviceGroup,
@@ -627,7 +636,8 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     const mpackVersion = this.addMpackVersion(mpackVersionId);
     
     if (mpackVersion) {
-      const serviceGroup = this.addServiceGroup(mpackVersionId, mpackVersion.get('mpack.name')); //TODO: for now we are setting the service group name equal to the mpack name
+      const serviceGroupName = mpackVersion.get('mpack.name'); //TODO: for now we are setting the service group name equal to the mpack name
+      const serviceGroup = this.getServiceGroup(serviceGroupName) || this.addServiceGroup(mpackVersionId, serviceGroupName);
       
       if (serviceGroup) {
         mpackVersion.get('services').forEach(service => this.addServiceInstance(service.get('name'), service.get('name'), serviceGroup)); //TODO: for now we are setting the service instance name equal to the service name
@@ -786,10 +796,22 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
 
   //#region Load/Save
 
+  clearStep: function () {
+    this.set('serviceGroups', []);
+    this.set('addedServiceGroups', []);
+    this.set('serviceInstances', []);
+    this.set('addedServiceInstances', []);
+  },
+
   loadStep: function () {
+    var self = this;
+
+    this.clearStep();
+
     this.getRegistry().done(() => {
-      this.getServiceGroups();
-      this.getServiceInstances();
+      self.getServiceGroups();
+      self.getServiceInstances();
+      self.updateRegistry();
     });
   },
 
@@ -811,7 +833,7 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     const addedServiceGroups = this.get('content.addedServiceGroups');
     if (addedServiceGroups) {
       addedServiceGroups.forEach(serviceGroup => {
-        this.addServiceGroup(serviceGroup.mpackVersionId, serviceGroup.name);
+        this.addServiceGroup(`${serviceGroup.mpackName}-${serviceGroup.mpackVersion}`, serviceGroup.name);
       });
     }
   },
@@ -829,13 +851,8 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
           const service = serviceGroup.get('mpackVersion.services').findProperty('name', serviceInstance.serviceName);
         
           if (service) {
-            this.get('serviceInstances').pushObject({
-              id: serviceInstance.serviceGroupName + serviceInstance.name,
-              name: serviceInstance.name,
-              service: service,
-              serviceGroup: serviceGroup,
-              canRemove: false
-            });
+            const si = this.createServiceInstance(serviceInstance.name, service, serviceGroup, false);
+            this.get('serviceInstances').pushObject(si);
           }
         }
       });
@@ -861,7 +878,9 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
     } else { //toggling to Advanced Mode
       this.set('noRecommendationAvailable', false);
     }
-    
+
+    this.updateRegistry();
+
     this.set('content.advancedMode', !isAdvancedMode);
   },
 
@@ -959,6 +978,14 @@ App.WizardSelectMpacksController = App.WizardStepController.extend({
         return selectedMpack;
       });
       this.set('content.selectedMpacks', selectedMpacks);
+      
+      //Gather ids of mpacks that were already registered before this wizard began
+      const registeredMpackIds = this.get('content.registeredMpacks').filter(mpack => !mpack.currentWizard) //Exclude mpacks that were registered during the current wizard; user is just moving back/forward
+        .map(mpack => `${mpack.MpackInfo.mpack_name}-${mpack.MpackInfo.mpack_version}`);
+      
+      //Get mpacks that need to be registered, which are the mpacks indicated by the current selection, but not those already registered on the server before this wizard began
+      const mpacksToRegister = selectedMpacks.reject(mpack => registeredMpackIds.contains(mpack.id));
+      this.set('content.mpacksToRegister', mpacksToRegister);
 
       App.router.send('next');
     }
diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js
index a8980ca..1a0d88e 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -960,23 +960,8 @@ App.WizardStep7Controller = App.WizardStepController.extend(App.ServerValidatorM
       masterComponentHosts: this.get('wizardController.content.masterComponentHosts'),
       slaveComponentHosts: this.get('wizardController.content.slaveComponentHosts')
     };
-
-    var selectedRepoVersion,
-        repoVersion;
-
-    if (this.get('wizardController.name') === 'addServiceController') {
-      repoVersion = App.RepositoryVersion.find().filter(function(i) {
-        return i.get('stackVersionType') === App.get('currentStackName') &&
-          i.get('stackVersionNumber') === App.get('currentStackVersionNumber');
-      })[0];
-      if (repoVersion) {
-        selectedRepoVersion = Em.get(repoVersion, 'repositoryVersion').split('-')[0];
-      }
-    } else {
-      selectedRepoVersion = Em.getWithDefault(App.Stack.find().findProperty('isSelected', true) || {}, 'repositoryVersion', false);
-    }
-
-    var configsByService = {}, dependencies = this.get('configDependencies');
+    var configsByService = {};
+    var dependencies = this.get('configDependencies');
 
     configs.forEach(function (_config) {
       if (!configsByService[_config.serviceName]) {
diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js
index 127f0ce..ba8687a 100644
--- a/ambari-web/app/controllers/wizard/step8_controller.js
+++ b/ambari-web/app/controllers/wizard/step8_controller.js
@@ -153,14 +153,6 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
     return services;
   }.property(),
 
-  selectedMpacks: function() {
-    return this.get('content.selectedMpacks') || this.get('wizardController').getDBProperty('selectedMpacks');
-  }.property(),
-
-  downloadConfig: function() {
-    return this.get('content.downloadConfig') || this.get('wizardController').getDBProperty('downloadConfig');
-  }.property(),
-
   getSelectedStack: function() {
     //TODO Remove when cluster don't need stack version, until use version of any mpack
     return App.Stack.find().objectAt(0);
@@ -295,7 +287,7 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
 
     // cluster name
     var cluster = this.rawContent.findProperty('config_name', 'cluster');
-    cluster.config_value = this.get('content.cluster.name');
+    cluster.config_value = this.get('clusterName');
     this.get('clusterInfo').pushObject(Ember.Object.create(cluster));
 
     //hosts
@@ -311,56 +303,49 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
     totalHostsObj.config_value = totalHostsCount + ' (' + newHostsCount + ' new)';
     this.get('clusterInfo').pushObject(Em.Object.create(totalHostsObj));
 
-    //repo
-    if (this.get('isAddService')) {
-      // For some stacks there is no info regarding stack versions to upgrade, e.g. HDP-2.1
-      if (App.StackVersion.find().get('content.length')) {
-        this.loadRepoInfo();
-      } else {
-        this.loadDefaultRepoInfo();
-      }
-    } else {
-      if (this.get('isInstaller')) {
-        // from install wizard
-        var downloadConfig = this.get('downloadConfig');
-        var allRepos = [];
-        var selectedMpacks = this.get('content.selectedMpacks');
-        var registeredMpacks = this.get('content.registeredMpacks');
-        if (selectedMpacks && registeredMpacks) {
-          selectedMpacks.forEach(mpack => {
-            if (mpack.operatingSystems) { //repos have been customized
-              mpack.operatingSystems.forEach(os => {
-                if (os.selected) {
-                  os.repos.forEach(function (repo) {
-                    allRepos.push(Em.Object.create({
-                      base_url: repo.downloadUrl,
-                      os_type: os.type,
-                      repo_id: repo.repoId
-                    }));
-                  });
-                }
-              })
-            } else { //repos have not been customized, so use default info
-              const rmp = registeredMpacks.find(rmp => rmp.MpackInfo.mpack_name === mpack.name && rmp.MpackInfo.mpack_version === mpack.version);
-              if (rmp) {
-                rmp.operating_systems.forEach(os => {
-                  os.OperatingSystems.repositories.forEach(function (repo) {
-                    allRepos.push(Em.Object.create({
-                      base_url: repo.base_url,
-                      os_type: repo.os_type,
-                      repo_id: repo.repo_id
-                    }));
-                  })
-                })
-              }
+    var allRepos = [];
+    var selectedMpacks = this.get('content.selectedMpacks');
+    var registeredMpacks = this.get('content.registeredMpacks');
+    if (selectedMpacks && registeredMpacks) {
+      selectedMpacks.forEach(mpack => {
+        if (mpack.operatingSystems) { //repos have been customized
+          mpack.operatingSystems.forEach(os => {
+            if (os.selected) {
+              os.repos.forEach(function (repo) {
+                allRepos.push(Em.Object.create({
+                  base_url: repo.downloadUrl,
+                  os_type: os.type,
+                  repo_id: repo.repoId
+                }));
+              });
             }
-          });
+          })
+        } else { //repos have not been customized, so use default info
+          const rmp = registeredMpacks.find(rmp => rmp.MpackInfo.mpack_name === mpack.name && rmp.MpackInfo.mpack_version === mpack.version);
+          if (rmp) {
+            rmp.operating_systems.forEach(os => {
+              os.OperatingSystems.repositories.forEach(function (repo) {
+                allRepos.push(Em.Object.create({
+                  base_url: repo.base_url,
+                  os_type: repo.os_type,
+                  repo_id: repo.repo_id
+                }));
+              })
+            })
+          }
         }
-        allRepos.set('display_name', Em.I18n.t("installer.step8.repoInfo.displayName"));
-        this.get('clusterInfo').set('useRedhatSatellite', downloadConfig.useRedhatSatellite);
-        this.get('clusterInfo').set('repoInfo', allRepos);
-      }
+      });
     }
+    
+    const downloadConfig = this.get('content.downloadConfig');
+    if (downloadConfig) {
+      this.get('clusterInfo').set('useRedhatSatellite', downloadConfig.useRedhatSatellite);
+    } else {
+      this.get('clusterInfo').set('useRedhatSatellite', false);
+    }
+
+    allRepos.set('display_name', Em.I18n.t("installer.step8.repoInfo.displayName"));
+    this.get('clusterInfo').set('repoInfo', allRepos);
   },
 
   /**
@@ -995,8 +980,6 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
    * @method createServiceGroup
    */
   createServiceGroups: function () {
-    if (!this.get('isInstaller')) return;
-    
     var data = this.createServiceGroupsData();
     if (data) {
       this.addRequestToAjaxQueue({
@@ -1014,13 +997,13 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
    * @method createServiceGroupsData
    */
   createServiceGroupsData: function () {
-    const mpacks = this.get('selectedMpacks');
+    const addedServiceGroups = this.get('content.addedServiceGroups');
     
-    if (mpacks) {
-      const serviceGroups = mpacks.map(mpack => ({
+    if (addedServiceGroups) {
+      const serviceGroups = addedServiceGroups.map(serviceGroup => ({
           "ServiceGroupInfo": {
-            "service_group_name": mpack.name,
-            "version": `${mpack.name}-${mpack.version}`
+            "service_group_name": serviceGroup.name,
+            "version": `${serviceGroup.mpackName}-${serviceGroup.mpackVersion}`
           }
         })
       );
@@ -1506,13 +1489,15 @@ App.WizardStep8Controller = App.WizardStepController.extend(App.AddSecurityConfi
       })
     });
 
-    this.addRequestToAjaxQueue({
-      name: 'common.cluster.settings.create',
-      data: {
-        clusterName: this.get('clusterName'),
-        data: data
-      }
-    });
+    if (clusterSettings.length > 0) {
+      this.addRequestToAjaxQueue({
+        name: 'common.cluster.settings.create',
+        data: {
+          clusterName: this.get('clusterName'),
+          data: data
+        }
+      });
+    }
   },
 
   /**
diff --git a/ambari-web/app/mappers/service_mapper.js b/ambari-web/app/mappers/service_mapper.js
index 6176505..c606602 100644
--- a/ambari-web/app/mappers/service_mapper.js
+++ b/ambari-web/app/mappers/service_mapper.js
@@ -48,30 +48,31 @@ App.serviceMapper = App.QuickDataMapper.create({
     });
 
     services.forEach(function (service) {
-      if(!service.ServiceInfo.service_name.includes('CLIENT')) {
-        var cachedService = App.cache['services'].findProperty('ServiceInfo.service_name', service.ServiceInfo.service_name);
-        if (cachedService) {
-          // restore service workStatus
-          App.Service.find(cachedService.ServiceInfo.service_name).set('workStatus', service.ServiceInfo.state);
-          cachedService.ServiceInfo.state = service.ServiceInfo.state;
-        } else {
-          var serviceData = {
-            ServiceInfo: {
-              id: `${service.ServiceInfo.service_name}-${service.ServiceInfo.service_group_name}`,
-              service_name: service.ServiceInfo.service_name,
-              service_group_name: service.ServiceInfo.service_group_name,
-              state: service.ServiceInfo.state,
-              mpack_name: service.ServiceInfo.mpack_name,
-              mpack_version: service.ServiceInfo.mpack_version,
-              tool_tip_content: `${service.ServiceInfo.service_name} (${service.ServiceInfo.service_group_name})`
-            },
-            host_components: [],
-            components: []
-          };
-          App.cache['services'].push(serviceData);
+      var cachedService = App.cache['services'].findProperty('ServiceInfo.service_name', service.ServiceInfo.service_name);
+      if (cachedService) {
+        // restore service workStatus
+        const serviceRec = App.Service.find(cachedService.ServiceInfo.service_name);
+        if (serviceRec.get('isLoaded')) {
+          serviceRec.set('workStatus', service.ServiceInfo.state);
         }
-        passiveStateMap[service.ServiceInfo.service_name] = service.ServiceInfo.maintenance_state;
+        cachedService.ServiceInfo.state = service.ServiceInfo.state;
+      } else {
+        var serviceData = {
+          ServiceInfo: {
+            id: `${service.ServiceInfo.service_name}-${service.ServiceInfo.service_group_name}`,
+            service_name: service.ServiceInfo.service_name,
+            service_group_name: service.ServiceInfo.service_group_name,
+            state: service.ServiceInfo.state,
+            mpack_name: service.ServiceInfo.mpack_name,
+            mpack_version: service.ServiceInfo.mpack_version,
+            tool_tip_content: `${service.ServiceInfo.service_name} (${service.ServiceInfo.service_group_name})`
+          },
+          host_components: [],
+          components: []
+        };
+        App.cache['services'].push(serviceData);
       }
+      passiveStateMap[service.ServiceInfo.service_name] = service.ServiceInfo.maintenance_state;
     });
 
     if (!this.get('initialAppLoad')) {
diff --git a/ambari-web/app/mappers/service_metrics_mapper.js b/ambari-web/app/mappers/service_metrics_mapper.js
index 60f4c92..d7ee227 100644
--- a/ambari-web/app/mappers/service_metrics_mapper.js
+++ b/ambari-web/app/mappers/service_metrics_mapper.js
@@ -243,6 +243,8 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
           var comp = this.parseIt(host_component, this.config3);
           comp.id = id;
           comp.service_id = serviceName;
+          comp.service_name = serviceName;
+          comp.service_group_name = service.ServiceInfo.service_group_name;
           if (comp.component_name === 'HIVE_SERVER_INTERACTIVE') {
             hiveInteractiveServers.push(comp);
           }
diff --git a/ambari-web/app/mixins/common/blueprint.js b/ambari-web/app/mixins/common/blueprint.js
index cbc633d..2278ff4 100644
--- a/ambari-web/app/mixins/common/blueprint.js
+++ b/ambari-web/app/mixins/common/blueprint.js
@@ -45,8 +45,9 @@ App.BlueprintMixin = Em.Mixin.create({
         name: group_name,
         components: mappedComponents[host] ? mappedComponents[host].map(function (c) {
           return {
-            mpack_instance: Em.get(c, 'mpackInstance'),
-            service_instance: Em.get(c, 'serviceInstance'),
+            mpack_instance: Em.get(c, 'serviceGroupName'),
+            //service_instance: Em.get(c, 'serviceInstance'), // TODO: this should work in the future;
+            service_instance: Em.get(c, 'serviceName'),       // for now, we use the service name as the service instance name.
             name: Em.get(c, 'componentName')
           };
         }) : []
diff --git a/ambari-web/app/mixins/common/configs/config_recommendations.js b/ambari-web/app/mixins/common/configs/config_recommendations.js
index 839b7e8..2c7280d 100644
--- a/ambari-web/app/mixins/common/configs/config_recommendations.js
+++ b/ambari-web/app/mixins/common/configs/config_recommendations.js
@@ -58,16 +58,14 @@ App.ConfigRecommendations = Em.Mixin.create({
    * @returns {recommendation}
    */
   applyRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentProperties, isEditable) {
-    try {
-      var parentPropertyIds = this.formatParentProperties(parentProperties);
-      var recommendation = this.getRecommendation(name, fileName, configGroupName);
-      if (recommendation) {
-        return this.updateRecommendation(recommendation, recommendedValue, parentPropertyIds);
-      }
-      return this.addRecommendation(name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds, isEditable);
-    } catch(e) {
-      console.error(e.message);
+    var parentPropertyIds = this.formatParentProperties(parentProperties);
+    var recommendation = this.getRecommendation(name, fileName, configGroupName);
+
+    if (recommendation) {
+      return this.updateRecommendation(recommendation, recommendedValue, parentPropertyIds);
     }
+
+    return this.addRecommendation(name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds, isEditable);
   },
 
   /**
@@ -95,6 +93,7 @@ App.ConfigRecommendations = Em.Mixin.create({
    * @returns {recommendation}
    */
   addRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds, isEditable) {
+    console.log(name, fileName);
     Em.assert('name and fileName should be defined', name && fileName);
     const site = App.config.getConfigTagFromFileName(fileName);
     const service = App.config.get('serviceByConfigTypeMap')[site];
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 1d151a4..191a25c 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
@@ -108,8 +108,6 @@ App.HostComponentRecommendationMixin = Em.Mixin.create(App.BlueprintMixin, {
       }
     }
 
-    requestData.data.recommendations.blueprint.mpack_instances = options.mpack_instances;
-
     return requestData;
   },
 
diff --git a/ambari-web/app/mixins/wizard/assign_master_components.js b/ambari-web/app/mixins/wizard/assign_master_components.js
index aa78259..456c849 100644
--- a/ambari-web/app/mixins/wizard/assign_master_components.js
+++ b/ambari-web/app/mixins/wizard/assign_master_components.js
@@ -472,9 +472,13 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
    */
   getRecommendationRequestData: function(options) {
     var res = this._super(options);
+    
     if (!this.get('isInstaller')) {
       res.data.recommendations = this.getCurrentMasterSlaveBlueprint();
     }
+
+    res.data.recommendations.blueprint.mpack_instances = options.mpack_instances;
+
     return res;
   },
 
@@ -683,7 +687,7 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
   createComponentInstallationObjects: function() {
     var stackMasterComponentsMap = {},
         masterHosts = this.get('content.masterComponentHosts'),
-        servicesToAdd = (this.get('content.services')|| []).filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'),
+        servicesToAdd = this.get('content.selectedServiceNames'),
         recommendations = this.get('recommendations'),
         resultComponents = [],
         multipleComponentHasBeenAdded = {},
@@ -718,7 +722,7 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
             // 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.
             // NOTE: On upgrade there might be a prior installed service with non-installed newly introduced serviceComponent
-            if (!servicesToAdd.contains(stackMasterComponent.get('serviceName')) && !isMasterCreateOnConfig) {
+            if (servicesToAdd && !servicesToAdd.contains(stackMasterComponent.get('serviceName')) && !isMasterCreateOnConfig) {
               willBeDisplayed = masterHosts.someProperty('component', component.name);
             }
 
diff --git a/ambari-web/app/router.js b/ambari-web/app/router.js
index 33b5145..426ebf0 100644
--- a/ambari-web/app/router.js
+++ b/ambari-web/app/router.js
@@ -267,10 +267,12 @@ App.Router = Em.Router.extend({
    * @param wizardType one of <code>installer</code>, <code>addHost</code>, <code>addServices</code>
    */
   getWizardCurrentStep: function (wizardType) {
-    var currentStep = App.db.getWizardCurrentStep(wizardType);
+    const currentStep = App.db.getWizardCurrentStep(wizardType);
+    
     if (!currentStep) {
-      currentStep = wizardType === 'installer' ? '0' : '1';
+      return 0;
     }
+    
     return currentStep;
   },
 
diff --git a/ambari-web/app/routes/add_service_routes.js b/ambari-web/app/routes/add_service_routes.js
index d43cb36..4bfa299 100644
--- a/ambari-web/app/routes/add_service_routes.js
+++ b/ambari-web/app/routes/add_service_routes.js
@@ -17,11 +17,10 @@
  */
 
 var App = require('app');
-// @todo: remove App.supports.kerberosAutomated after Kerberos Automation Wizard support
-var consoleMsg = Em.I18n.t('app.logger.wizard');
 
-module.exports = App.WizardRoute.extend({
+module.exports = Em.Route.extend(App.RouterRedirections, {
   route: '/service/add',
+  App: require('app'),
 
   enter: function (router) {
     if (App.isAuthorized('SERVICE.ADD_DELETE_SERVICES') && App.supports.enableAddDeleteServices) {
@@ -60,10 +59,10 @@ module.exports = App.WizardRoute.extend({
               },
               showWarningPopup: function() {
                 var mainPopupContext = this;
-                var currentStep = addServiceController.get('currentStep');
-                const DEPLOY_STEP = '6';
-                const LAST_STEP = '7';
-                if (currentStep === LAST_STEP) {
+                var currentStep = addServiceController.get('currentStepName');
+                const lastStep = addServiceController.get('lastStepName');
+                const DEPLOY_STEP = 'step6';
+                if (currentStep === lastStep) {
                   mainPopupContext.afterWarning();
                 } else {
                   App.ModalPopup.show({
@@ -85,27 +84,8 @@ module.exports = App.WizardRoute.extend({
               }
             });
             addServiceController.set('popup', popup);
-            var currentClusterStatus = App.clusterStatus.get('value');
-            if (currentClusterStatus) {
-              switch (currentClusterStatus.clusterState) {
-                case 'ADD_SERVICES_DEPLOY_PREP_2' :
-                  addServiceController.setCurrentStep('5');
-                  break;
-                case 'ADD_SERVICES_INSTALLING_3' :
-                case 'SERVICE_STARTING_3' :
-                case 'ADD_SERVICES_INSTALLED_4' :
-                  addServiceController.setCurrentStep('7');
-                  break;
-                default:
-                  if(App.db.data.AddService.currentStep && App.db.data.AddService.currentStep !== addServiceController.get('currentStep')) {
-                    addServiceController.setCurrentStep(App.db.data.AddService.currentStep);
-                  }
-                  break;
-              }
-            }
-
             App.router.get('wizardWatcherController').setUser(addServiceController.get('name'));
-            router.transitionTo('step' + addServiceController.get('currentStep'));
+            router.transitionTo(addServiceController.get('currentStepName'));
           });
         });
       });
@@ -117,304 +97,678 @@ module.exports = App.WizardRoute.extend({
 
   },
 
-  step1: App.StepRoute.extend({
-    route: '/step1',
+  configureDownload: Em.Route.extend({
+    route: '/configureDownload',
+    breadcrumbs: { label: Em.I18n.translations['installer.configureDownload.header'] },
     connectOutlets: function (router) {
-      App.logger.setTimer(consoleMsg.format(1));
+      console.time('configureDownload connectOutlets');
+      var self = this;
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('1');
+      var configureDownloadController = router.get('wizardConfigureDownloadController');
+      configureDownloadController.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('configureDownload');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('configureDownload');
       controller.set('hideBackButton', true);
-      controller.dataLoading().done(function () {
-        controller.loadAllPriorSteps().done(function () {
-          //var wizardStep4Controller = router.get('wizardStep4Controller');
-          wizardStep4Controller.set('wizardController', controller);
-          controller.loadServiceVersionFromVersionDefinitions().complete(function () {
-            controller.set('content.services', App.StackService.find().forEach(function (item) {
-              // user the service version from VersionDefinition
-              Ember.set(item, 'serviceVersionDisplay', controller.get('serviceVersionsMap')[item.get('serviceName')]);
-              //item.set('serviceVersionDisplay', controller.get('serviceVersionsMap')[item.get('serviceName')]);
-            }));
-            App.logger.logTimerIfMoreThan(consoleMsg.format(1));
-            controller.connectOutlet('wizardStep4', controller.get('content.services').filterProperty('isInstallable', true));
-          });
-        });
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardConfigureDownload', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('configureDownload connectOutlets');
       });
     },
-    nextTransition: function (router) {
-      var addServiceController = router.get('addServiceController');
-      //var wizardStep4Controller = router.get('wizardStep4Controller');
-      addServiceController.saveServices(wizardStep4Controller);
-      addServiceController.saveClients(wizardStep4Controller);
-      addServiceController.setDBProperty('masterComponentHosts', undefined);
-
-      var wizardStep5Controller = router.get('wizardStep5Controller');
-      wizardStep5Controller.clearRecommendations(); // Force reload recommendation between steps 1 and 2
-      addServiceController.setDBProperty('recommendations', undefined);
-      addServiceController.clearEnhancedConfigs();
-      router.transitionTo('step2');
+    
+    next: function (router) {
+      console.time('configureDownload next');
+      if(router.get('btnClickInProgress')) {
+        return;
+      }
+      App.set('router.nextBtnClickInProgress', true);
+      var controller = router.get('addServiceController');
+      controller.save('downloadConfig');
+      router.transitionTo('selectMpacks');
+      console.timeEnd('configureDownload next');
     }
   }),
 
-  step2: App.StepRoute.extend({
-    route: '/step2',
+  selectMpacks: App.StepRoute.extend({
+    route: '/selectMpacks',
+    breadcrumbs: { label: Em.I18n.translations['installer.selectMpacks.header'] },
     connectOutlets: function (router) {
-      App.logger.setTimer(consoleMsg.format(2));
+      console.time('selectMpacks connectOutlets');
+      var self = this;
       var controller = router.get('addServiceController');
-      var wizardStep2Controller = router.get('wizardStep5Controller');
-      controller.setCurrentStep('2');
+      controller.setCurrentStep('selectMpacks');
       controller.set('hideBackButton', false);
-      wizardStep2Controller.set('isInitialLayout', true);
-      controller.dataLoading().done(function () {
-        controller.loadAllPriorSteps().done(function () {
-          App.logger.logTimerIfMoreThan(consoleMsg.format(2));
-          wizardStep2Controller.set('wizardController', controller);
-          controller.connectOutlet('wizardStep5', controller.get('content'));
+      var wizardSelectMpacksController = router.get('wizardSelectMpacksController');
+      wizardSelectMpacksController.set('wizardController', controller);
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardSelectMpacks', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('selectMpacks connectOutlets');
+      });
+    },
+
+    backTransition: function (router) {
+      var controller = router.get('addServiceController');
+      controller.clearErrors();
+      router.transitionTo('configureDownload');
+    },
+
+    next: function (router, context) {
+      console.time('selectMpacks next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        var controller = router.get('addServiceController');
+        controller.save('selectedServiceNames');
+        controller.save('selectedServices');
+        controller.save('selectedMpacks');
+        controller.save('mpacksToRegister');
+        controller.save('serviceGroups');
+        controller.save('addedServiceGroups');
+        controller.save('serviceInstances');
+        controller.save('addedServiceInstances');
+        controller.save('advancedMode');
+        var wizardSelectMpacksController = router.get('wizardSelectMpacksController');
+        // Clear subsequent settings if user changed service selections
+        if (!wizardSelectMpacksController.get('isSaved')) {
+          router.get('wizardStep5Controller').clearRecommendations();
+          controller.setDBProperty('recommendations', undefined);
+          controller.set('content.masterComponentHosts', undefined);
+          controller.setDBProperty('masterComponentHosts', undefined);
+          controller.clearEnhancedConfigs();
+          controller.setDBProperty('slaveComponentHosts', undefined);
+          router.get('wizardStep6Controller').set('isClientsSet', false);
+        }
+        controller.setStepSaved('selectMpacks');
+        if (controller.get('content.mpacksToRegister').length > 0) {
+          const downloadConfig = controller.get('content.downloadConfig');
+          if (downloadConfig && downloadConfig.useCustomRepo) {
+            router.transitionTo('customMpackRepos');
+          } else {
+            router.transitionTo('downloadMpacks');
+          }
+        } else {
+          router.transitionTo('step5');
+        }
+        console.timeEnd('selectMpacks next');
+      }
+    },
+  }),
+
+  customMpackRepos: App.StepRoute.extend({
+    route: '/customMpackRepos',
+    breadcrumbs: { label: Em.I18n.translations['installer.customMpackRepos.header'] },
+    connectOutlets: function (router) {
+      console.time('customMpackRepos connectOutlets');
+      var self = this;
+      var controller = router.get('addServiceController');
+      const downloadConfig = controller.get('content.downloadConfig');
+      
+      //do not allow navigation to this step unless we are using custom repos
+      if (downloadConfig && !downloadConfig.useCustomRepo) {
+        Em.run.next(function () {
+          router.transitionTo('downloadMpacks');
         });
+      }  
+
+      var customMpackReposController = router.get('wizardCustomMpackReposController');
+      customMpackReposController.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('customMpackRepos');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('customMpackRepos');
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardCustomMpackRepos', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('customMpackRepos connectOutlets');
       });
+    },
+
+    backTransition: function (router) {
+      var controller = router.get('addServiceController');
+      controller.clearErrors();
+      router.transitionTo('selectMpacks');
+    },
 
+    next: function (router) {
+      console.time('customMpackRepos next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        const controller = router.get('addServiceController');
+        controller.save('mpacksToRegister');
+        controller.setStepSaved('customMpackRepos');
+        router.transitionTo('downloadMpacks');
+        console.timeEnd('customMpackRepos next');
+      }  
+    }
+  }),
+  
+  downloadMpacks: App.StepRoute.extend({
+    route: '/downloadMpacks',
+    breadcrumbs: { label: Em.I18n.translations['installer.downloadMpacks.header'] },
+    connectOutlets: function (router) {
+      console.time('downloadMpacks connectOutlets');
+      var self = this;
+      var controller = router.get('addServiceController');
+      var downloadMpacksController = router.get('wizardDownloadMpacksController');
+      downloadMpacksController.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('downloadMpacks');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('downloadMpacks');
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardDownloadMpacks', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('downloadMpacks connectOutlets');
+      });
     },
 
     backTransition: function (router) {
-      router.transitionTo('step1');
+      const controller = router.get('addServiceController');
+      controller.clearErrors();
+      const downloadConfig = controller.get('content.downloadConfig');
+      if (downloadConfig && downloadConfig.useCustomRepo) {
+        router.transitionTo('customMpackRepos');
+      } else {
+        router.transitionTo('selectMpacks');
+      }
     },
 
     next: function (router) {
-      var addServiceController = router.get('addServiceController');
-      var wizardStep5Controller = router.get('wizardStep5Controller');
-      var wizardStep6Controller = router.get('wizardStep6Controller');
-      addServiceController.saveMasterComponentHosts(wizardStep5Controller);
-      addServiceController.setDBProperty('slaveComponentHosts', undefined);
-      addServiceController.setDBProperty('recommendations', wizardStep5Controller.get('content.recommendations'));
-      wizardStep6Controller.set('isClientsSet', false);
-      router.transitionTo('step3');
+      console.time('downloadMpacks next');
+      if(router.get('btnClickInProgress')) {
+        return;
+      }
+      App.set('router.nextBtnClickInProgress', true);
+      const controller = router.get('addServiceController');
+      controller.save('registeredMpacks');
+      controller.save('selectedStack');
+      const downloadConfig = controller.get('content.downloadConfig');
+      if (downloadConfig && downloadConfig.useCustomRepo) {
+        router.transitionTo('customProductRepos');
+      } else {
+        router.transitionTo('step5');
+      }  
+      console.timeEnd('downloadMpacks next');
     }
   }),
 
-  step3: App.StepRoute.extend({
-    route: '/step3',
+  customProductRepos: App.StepRoute.extend({
+    route: '/customProductRepos',
+    breadcrumbs: { label: Em.I18n.translations['installer.customProductRepos.header'] },
     connectOutlets: function (router) {
-      App.logger.setTimer(consoleMsg.format(3));
+      console.time('customProductRepos connectOutlets');
+      var self = this;
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('3');
-      router.get('mainController').isLoading.call(router.get('clusterController'), 'isServiceContentFullyLoaded').done(function () {
-        controller.loadAllPriorSteps().done(function () {
-          var wizardStep6Controller = router.get('wizardStep6Controller');
-          wizardStep6Controller.set('wizardController', controller);
-          App.logger.logTimerIfMoreThan(consoleMsg.format(3));
-          controller.connectOutlet('wizardStep6', controller.get('content'));
+      const downloadConfig = controller.get('content.downloadConfig');
+      
+      //disable navigation to this step unless we are using custom repos
+      if (downloadConfig && !downloadConfig.useCustomRepo) {
+        Em.run.next(function () {
+          router.transitionTo('verifyProducts');
         });
+      }  
+      
+      var customMpackReposController = router.get('wizardCustomMpackReposController');
+      customMpackReposController.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('customProductRepos');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('customProductRepos');
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardCustomProductRepos', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('customProductRepos connectOutlets');
       });
     },
 
     backTransition: function (router) {
       var controller = router.get('addServiceController');
-      if (!controller.get('content.skipMasterStep')) {
-        return router.transitionTo('step2');
+      controller.clearErrors();
+      router.transitionTo('downloadMpacks');
+    },
+
+    next: function (router) {
+      console.time('customProductRepos next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        const controller = router.get('addServiceController');
+        controller.clearErrors();
+        controller.save('selectedMpacks');
+        controller.save('registeredMpacks');
+        controller.setStepSaved('customProductRepos');
+        router.transitionTo('verifyProducts');
+        console.timeEnd('customProductRepos next');
+      }
+    }
+  }),
+
+  verifyProducts: App.StepRoute.extend({
+    route: '/verifyProducts',
+    breadcrumbs: { label: Em.I18n.translations['installer.verifyProducts.header'] },
+    connectOutlets: function (router) {
+      console.time('verifyProducts connectOutlets');
+      var self = this;
+      var controller = router.get('addServiceController');
+      var verifyProductsController = router.get('wizardVerifyProductsController');
+      verifyProductsController.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('verifyProducts');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('verifyProducts');
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardVerifyProducts', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('verifyProducts connectOutlets');
+      });
+    },
+
+    backTransition: function (router) {
+      const controller = router.get('addServiceController');
+      controller.clearErrors();
+      const downloadConfig = controller.get('content.downloadConfig');
+      if (downloadConfig && downloadConfig.useCustomRepo) {
+        router.transitionTo('customProductRepos');
+      } else {
+        router.transitionTo('downloadMpacks');
       }
-      return router.transitionTo('step1');
     },
 
     next: function (router) {
+      console.time('verifyProducts next');
+      if (router.get('btnClickInProgress')) {
+        return;
+      }
       App.set('router.nextBtnClickInProgress', true);
-      var addServiceController = router.get('addServiceController');
-      var wizardStep6Controller = router.get('wizardStep6Controller');
+      const controller = router.get('addServiceController');
+      router.transitionTo(controller.getNextStepName());
+      console.timeEnd('verifyProducts next');
+    }
+  }),
 
-      addServiceController.saveSlaveComponentHosts(wizardStep6Controller);
-      addServiceController.get('content').set('serviceConfigProperties', null);
-      addServiceController.setDBProperties({
-        groupsToDelete: null,
-        recommendationsHostGroups: wizardStep6Controller.get('content.recommendationsHostGroups'),
-        recommendationsConfigs: null
+  step5: App.StepRoute.extend({
+    route: '/step5',
+    breadcrumbs: { label: Em.I18n.translations['installer.step5.header'] },
+    connectOutlets: function (router, context) {
+      console.time('step5 connectOutlets');
+      var self = this;
+      var controller = router.get('addServiceController');
+      var wizardStep5Controller = router.get('wizardStep5Controller');
+      wizardStep5Controller.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('step5');
+      router.setNavigationFlow(newStepIndex);
+      wizardStep5Controller.setProperties({
+        servicesMasters: [],
+        isInitialLayout: true
       });
-      router.get('wizardStep7Controller').set('recommendationsConfigs', null);
-      router.get('wizardStep7Controller').clearAllRecommendations();
-      addServiceController.setDBProperty('serviceConfigGroups', undefined);
-      App.ServiceConfigGroup.find().clear();
-      addServiceController.clearServiceConfigProperties();
-      if (App.get('isKerberosEnabled')) {
-        addServiceController.setDBProperty('kerberosDescriptorConfigs', null);
+      controller.setCurrentStep('step5');
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardStep5', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('step5 connectOutlets');
+      });
+    },
+    
+    backTransition: function (router) {
+      var controller = router.get('addServiceController');
+      controller.clearErrors();
+      const downloadConfig = controller.get('content.downloadConfig');
+      if (controller.get('content.mpacksToRegister').length > 0) {
+        if (downloadConfig && downloadConfig.useCustomRepo) {
+          router.transitionTo('verifyProducts');
+        } else {
+          router.transitionTo('downloadMpacks');
+        }
+      } else {
+        router.transitionTo('selectMpacks');
+      }
+    },
+    
+    next: function (router) {
+      console.time('step5 next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        var controller = router.get('addServiceController');
+        var wizardStep5Controller = router.get('wizardStep5Controller');
+        controller.saveMasterComponentHosts(wizardStep5Controller);
+        controller.setDBProperty('recommendations', wizardStep5Controller.get('content.recommendations') || wizardStep5Controller.get('recommendations'));
+        // Clear subsequent steps if user made changes
+        if (!wizardStep5Controller.get('isSaved')) {
+          controller.setDBProperty('slaveComponentHosts', undefined);
+          var wizardStep6Controller = router.get('wizardStep6Controller');
+          wizardStep6Controller.set('isClientsSet', false);
+        }
+        controller.setStepSaved('step5');
+        router.transitionTo('step6');
       }
-      router.transitionTo('step4');
+      console.timeEnd('step5 next');
     }
   }),
 
-  step4: App.StepRoute.extend({
-    route: '/step4',
-    connectOutlets: function (router) {
-      App.logger.setTimer(consoleMsg.format(4));
+  step6: App.StepRoute.extend({
+    route: '/step6',
+    breadcrumbs: { label: Em.I18n.translations['installer.step6.header'] },
+    connectOutlets: function (router, context) {
+      console.time('step6 connectOutlets');
+      var self = this;
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('4');
-      controller.dataLoading().done(function () {
-        var wizardStep7Controller = router.get('wizardStep7Controller');
-        controller.loadAllPriorSteps().done(function () {
-          wizardStep7Controller.getConfigTags(true);
-          wizardStep7Controller.set('wizardController', controller);
-          controller.usersLoading().done(function () {
-            router.get('mainController').isLoading.call(router.get('clusterController'), 'isClusterNameLoaded').done(function () {
-              router.get('mainController').isLoading.call(router.get('clusterController'), 'isServiceContentFullyLoaded').done(function () {
-                App.logger.logTimerIfMoreThan(consoleMsg.format(4));
-                controller.connectOutlet('wizardStep7', controller.get('content'));
-              });
+      var wizardStep6Controller = router.get('wizardStep6Controller');
+      wizardStep6Controller.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('step6');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('step6');
+      wizardStep6Controller.set('hosts', []);
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardStep6', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('step6 connectOutlets');
+      });
+    },
+    
+    backTransition: function(router) {
+      var controller = router.get('addServiceController');
+      controller.clearErrors();
+      router.transitionTo('step5');
+    },
+
+    next: function (router) {
+      console.time('step6 next');
+      var controller = router.get('addServiceController');
+      var wizardStep6Controller = router.get('wizardStep6Controller');
+      if (!wizardStep6Controller.get('submitDisabled')) {
+        if (!router.get('btnClickInProgress')) {
+          App.set('router.nextBtnClickInProgress', true);
+          controller.saveSlaveComponentHosts(wizardStep6Controller);
+          controller.get('content').set('serviceConfigProperties', null);
+          controller.get('content').set('componentsFromConfigs', []);
+          // Clear subsequent steps if user made changes
+          if (!wizardStep6Controller.get('isSaved')) {
+            controller.setDBProperties({
+              serviceConfigGroups: null,
+              recommendationsHostGroups: wizardStep6Controller.get('content.recommendationsHostGroups'),
+              recommendationsConfigs: null,
+              componentsFromConfigs: []
             });
-          });
-        });
+            controller.clearServiceConfigProperties();
+            if (App.get('isKerberosEnabled')) {
+              controller.setDBProperty('kerberosDescriptorConfigs', null);
+            }      
+          }
+          controller.setStepSaved('step6');
+          router.transitionTo('step7');
+          console.timeEnd('step6 next');
+        }
+      }
+    }
+  }),
+
+  step7: App.StepRoute.extend({
+    route: '/step7',
+    breadcrumbs: { label: Em.I18n.translations['installer.step7.header'] },
+    connectOutlets: function (router, context) {
+      console.time('step7 connectOutlets');
+      var self = this;
+      var controller = router.get('addServiceController');
+      var wizardStep7Controller = router.get('wizardStep7Controller');
+      wizardStep7Controller.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('step7');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('step7');     
+      router.get('preInstallChecksController').loadStep();
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardStep7', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('step7 connectOutlets');
       });
     },
 
     backTransition: function (router) {
+      console.time('step7 back');
       var controller = router.get('addServiceController');
+      controller.clearErrors();
+
+      var step = router.get('addServiceController.content.skipSlavesStep') ? 'step5' : 'step6';
       var wizardStep7Controller = router.get('wizardStep7Controller');
-      var step = 'step1';
-      if (!controller.get('content.skipSlavesStep')) {
-        step = 'step3';
-      }
-      else if (!controller.get('content.skipMasterStep')) {
-        step = 'step2';
-      }
+
       var goToPreviousStep = function() {
         router.transitionTo(step);
       };
+
       if (wizardStep7Controller.hasChanges()) {
         wizardStep7Controller.showChangesWarningPopup(goToPreviousStep);
       } else {
         goToPreviousStep();
       }
+      console.timeEnd('step7 back');
     },
+
     next: function (router) {
-      var addServiceController = router.get('addServiceController');
-      var wizardStep7Controller = router.get('wizardStep7Controller');
-      var kerberosDescriptor = addServiceController.get('kerberosDescriptor');
-      wizardStep7Controller.checkDescriptor().always(function (data, status) {
-        wizardStep7Controller.storeClusterDescriptorStatus(status === 'success');
-        if (App.get('isKerberosEnabled')) {
-          wizardStep7Controller.updateKerberosDescriptor(kerberosDescriptor, wizardStep7Controller.getDescriptorConfigs());
-          addServiceController.saveKerberosDescriptorConfigs(kerberosDescriptor);
-          if (router.get('mainAdminKerberosController.isManualKerberos')) {
-            router.get('wizardStep8Controller').set('wizardController', router.get('addServiceController'));
-            router.get('wizardStep8Controller').updateKerberosDescriptor(true);
+      console.time('step7 next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        var controller = router.get('addServiceController');
+        var wizardStep7Controller = router.get('wizardStep7Controller');
+        var kerberosDescriptor = controller.get('kerberosDescriptor');
+        wizardStep7Controller.checkDescriptor().always(function (data, status) {
+          wizardStep7Controller.storeClusterDescriptorStatus(status === 'success');
+          if (App.get('isKerberosEnabled')) {
+            wizardStep7Controller.updateKerberosDescriptor(kerberosDescriptor, wizardStep7Controller.getDescriptorConfigs());
+            addServiceController.saveKerberosDescriptorConfigs(kerberosDescriptor);
+            if (router.get('mainAdminKerberosController.isManualKerberos')) {
+              router.get('wizardStep8Controller').set('wizardController', router.get('addServiceController'));
+              router.get('wizardStep8Controller').updateKerberosDescriptor(true);
+            }
           }
-        }
-        addServiceController.saveServiceConfigGroups(wizardStep7Controller, true);
-        addServiceController.saveServiceConfigProperties(wizardStep7Controller);
-        router.transitionTo('step5');
-      });
+          controller.saveServiceConfigProperties(wizardStep7Controller);
+          controller.saveServiceConfigGroups(wizardStep7Controller);
+          controller.setDBProperty('recommendationsConfigs', wizardStep7Controller.get('recommendationsConfigs'));
+          controller.saveComponentsFromConfigs(controller.get('content.componentsFromConfigs'));
+          controller.setDBProperty('recommendationsHostGroup', wizardStep7Controller.get('content.recommendationsHostGroup'));
+          controller.setDBProperty('masterComponentHosts', wizardStep7Controller.get('content.masterComponentHosts'));
+          App.clusterStatus.setClusterStatus({
+            localdb: App.db.data
+          });
+          router.transitionTo('step8');
+        });
+        console.timeEnd('step7 next');
+      }
     }
   }),
 
-  step5: App.StepRoute.extend({
-    route: '/step5',
-    connectOutlets: function (router) {
-      App.logger.setTimer(consoleMsg.format(5));
+  step8: App.StepRoute.extend({
+    route: '/step8',
+    breadcrumbs: { label: Em.I18n.translations['installer.step8.header'] },
+    connectOutlets: function (router, context) {
+      console.time('step8 connectOutlets');
+      var self = this;
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('5');
-      controller.dataLoading().done(function () {
-        controller.loadAllPriorSteps().done(function () {
-          var wizardStep8Controller = router.get('wizardStep8Controller');
-          wizardStep8Controller.set('wizardController', controller);
-          App.logger.logTimerIfMoreThan(consoleMsg.format(5));
-          controller.connectOutlet('wizardStep8', controller.get('content'));
-        });
+      var wizardStep8Controller = router.get('wizardStep8Controller');
+      wizardStep8Controller.set('wizardController', controller);
+      var newStepIndex = controller.getStepIndex('step8');
+      router.setNavigationFlow(newStepIndex);
+      controller.setCurrentStep('step8');
+      controller.loadAllPriorSteps().done(function () {
+        controller.setStepsEnable();
+        controller.connectOutlet('wizardStep8', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('step8 connectOutlets');
       });
       if(!!App.get('router.mainAdminKerberosController.kdc_type')){
         router.get('kerberosWizardStep5Controller').getCSVData(true);
       }
     },
+    
     backTransition: function (router) {
-      var controller = router.get('addServiceController');
-      if (!controller.get('content.skipConfigStep')) {
-        return router.transitionTo('step4');
-      }
-      if (!controller.get('content.skipSlavesStep')) {
-        return router.transitionTo('step3');
-      }
-      if (!controller.get('content.skipMasterStep')) {
-        return router.transitionTo('step2');
+      if (router.get('wizardStep8Controller.isBackBtnDisabled') == false) {
+        var controller = router.get('addServiceController');
+        controller.clearErrors();
+
+        router.transitionTo('step7');
       }
-      return router.transitionTo('step1');
     },
+    
     next: function (router) {
-      var addServiceController = router.get('addServiceController');
-      addServiceController.installServices(function () {
-        router.get('wizardStep8Controller').set('servicesInstalled', true);
-        addServiceController.setInfoForStep9();
-        addServiceController.saveClusterState('ADD_SERVICES_INSTALLING_3');
-        App.router.transitionTo('step6');
-      });
+      console.time('step8 next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        var controller = router.get('addServiceController');
+        var wizardStep8Controller = router.get('wizardStep8Controller');
+        // invoke API call to install selected services
+        controller.installServices(false, function () {
+          controller.setInfoForStep9();
+          // We need to do recovery based on whether we are in Add Host or Installer wizard
+          controller.saveClusterState('CLUSTER_INSTALLING_3');
+          wizardStep8Controller.set('servicesInstalled', true);
+          router.transitionTo('step9');
+          console.timeEnd('step8 next');
+        });
+      }
     }
   }),
 
-  step6: Em.Route.extend({
-    route: '/step6',
+  step9: Em.Route.extend({
+    route: '/step9',
+    breadcrumbs: { label: Em.I18n.translations['installer.step9.header'] },
     connectOutlets: function (router, context) {
-      App.logger.setTimer(consoleMsg.format(6));
+      console.time('step9 connectOutlets');
+      var self = this;
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('6');
-      if (!App.get('testMode')) {              //if test mode is ON don't disable prior steps link.
-        controller.setLowerStepsDisable(6);
-      }
-      controller.dataLoading().done(function () {
-        controller.loadAllPriorSteps().done(function () {
-          var wizardStep9Controller = router.get('wizardStep9Controller');
-          wizardStep9Controller.set('wizardController', controller);
-          App.logger.setTimer(consoleMsg.format(6));
+      var wizardStep9Controller = router.get('wizardStep9Controller');
+      wizardStep9Controller.set('wizardController', controller);
+      controller.loadAllPriorSteps().done(function () {
+        wizardStep9Controller.loadDoServiceChecksFlag().done(function () {
+          var newStepIndex = controller.getStepIndex('step7');
+          router.setNavigationFlow(newStepIndex);
+          controller.setCurrentStep('step9');
+          controller.setStepsEnable();
+          if (!App.get('testMode')) {
+            controller.setLowerStepsDisable(9);
+          }
           controller.connectOutlet('wizardStep9', controller.get('content'));
+          self.scrollTop();
+          console.timeEnd('step9 connectOutlets');
         });
       });
     },
-    back: Em.Router.transitionTo('step5'),
-    retry: function (router, context) {
-      var addServiceController = router.get('addServiceController');
+
+    backTransition: function (router) {
+      var controller = router.get('addServiceController');
+      controller.clearErrors();
+      router.transitionTo('step8');
+    },
+
+    retry: function (router) {
+      console.time('step9 retry');
+      var controller = router.get('addServiceController');
       var wizardStep9Controller = router.get('wizardStep9Controller');
       if (wizardStep9Controller.get('showRetry')) {
         if (wizardStep9Controller.get('content.cluster.status') === 'INSTALL FAILED') {
-          addServiceController.installServices(function () {
-            addServiceController.setInfoForStep9();
+          var isRetry = true;
+          controller.installServices(isRetry, function () {
+            controller.setInfoForStep9();
             wizardStep9Controller.resetHostsForRetry();
             // We need to do recovery based on whether we are in Add Host or Installer wizard
-            addServiceController.saveClusterState('ADD_SERVICES_INSTALLING_3');
+            controller.saveClusterState('CLUSTER_INSTALLING_3');
             wizardStep9Controller.navigateStep();
           });
         } else {
           wizardStep9Controller.navigateStep();
         }
+        console.timeEnd('step9 retry');
       }
     },
-    unroutePath: function () {
-      return false;
+
+    unroutePath: function (router, context) {
+      // exclusion for transition to Admin view or Views view
+      if (context === '/adminView' ||
+          context === '/main/views.index' || context === '/main/view.index') {
+        this._super(router, context);
+      } else {
+        return false;
+      }
     },
-    next: function (router) {
-      var addServiceController = router.get('addServiceController');
-      var wizardStep9Controller = router.get('wizardStep9Controller');
-      addServiceController.saveInstalledHosts(wizardStep9Controller);
 
-      // We need to do recovery based on whether we are in Add Host or Installer wizard
-      addServiceController.saveClusterState('ADD_SERVICES_INSTALLED_4');
-      router.transitionTo('step7');
+    next: function (router) {
+      console.time('step9 next');
+      if(!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        var controller = router.get('addServiceController');
+        var wizardStep9Controller = router.get('wizardStep9Controller');
+        controller.saveInstalledHosts(wizardStep9Controller);
+        controller.saveClusterState('CLUSTER_INSTALLED_4');
+        router.transitionTo('step10');
+        console.timeEnd('step9 next');
+      }
     }
   }),
 
-  step7: Em.Route.extend({
-    route: '/step7',
+  step10: Em.Route.extend({
+    route: '/step10',
+    breadcrumbs: { label: Em.I18n.translations['installer.step10.header'] },
     connectOutlets: function (router, context) {
+      var self = this;
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('7');
-      controller.setLowerStepsDisable(7);
-      controller.dataLoading().done(function () {
-        controller.loadAllPriorSteps().done(function () {
-          controller.connectOutlet('wizardStep10', controller.get('content'));
-        });
+      var wizardStep10Controller = router.get('wizardStep10Controller');
+      wizardStep10Controller.set('wizardController', controller);
+      controller.loadAllPriorSteps().done(function () {
+        if (!App.get('testMode')) {
+          var newStepIndex = controller.getStepIndex('step10');
+          router.setNavigationFlow(newStepIndex);
+          controller.setCurrentStep('step10');
+          controller.setStepsEnable();
+          controller.setLowerStepsDisable(10);
+        }
+        controller.connectOutlet('wizardStep10', controller.get('content'));
+        self.scrollTop();
       });
     },
-    back: Em.Router.transitionTo('step6'),
+    
+    backTransition: function (router) {
+      var controller = router.get('addServiceController');
+      controller.clearErrors();
+      router.transitionTo('step9');
+    },
+    
     complete: function (router, context) {
-      var addServiceController = router.get('addServiceController');
-      addServiceController.get('popup').onClose();
+      var controller = router.get('addServiceController');
+      controller.get('popup').onClose();
+      controller.finish();
     }
   }),
 
-  backToServices: function (router) {
-    App.router.get('updateController').set('isWorking', true);
-    router.transitionTo('services');
-  }
+  gotoStep0: Em.Router.transitionTo('step0'),
+
+  gotoStep2: Em.Router.transitionTo('step2'),
+
+  gotoStep3: Em.Router.transitionTo('step3'),
+
+  gotoStep5: Em.Router.transitionTo('step5'),
+
+  gotoStep6: Em.Router.transitionTo('step6'),
+
+  gotoStep7: Em.Router.transitionTo('step7'),
+
+  gotoStep8: Em.Router.transitionTo('step8'),
+
+  gotoStep9: Em.Router.transitionTo('step9'),
+
+  gotoStep10: Em.Router.transitionTo('step10'),
+
+  gotoConfigureDownload: Em.Router.transitionTo('configureDownload'),
+
+  gotoSelectMpacks: Em.Router.transitionTo('selectMpacks'),
+
+  gotoCustomMpackRepos: Em.Router.transitionTo('customMpackRepos'),
+
+  gotoDownloadMpacks: Em.Router.transitionTo('downloadMpacks'),
+
+  gotoCustomProductRepos: Em.Router.transitionTo('customProductRepos'),
+
+  gotoVerifyProducts: Em.Router.transitionTo('verifyProducts')
 
 });
diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js
index 9c1f26f..fabadc0 100644
--- a/ambari-web/app/routes/installer.js
+++ b/ambari-web/app/routes/installer.js
@@ -303,6 +303,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         controller.save('selectedServiceNames');
         controller.save('selectedServices');
         controller.save('selectedMpacks');
+        controller.save('mpacksToRegister');
         controller.save('serviceGroups');
         controller.save('addedServiceGroups');
         controller.save('serviceInstances');
@@ -372,7 +373,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       if (!router.get('btnClickInProgress')) {
         App.set('router.nextBtnClickInProgress', true);
         const controller = router.get('installerController');
-        controller.save('selectedMpacks');
+        controller.save('mpacksToRegister');
         controller.setStepSaved('customMpackRepos');
         router.transitionTo('downloadMpacks');
         console.timeEnd('customMpackRepos next');
diff --git a/ambari-web/app/templates/main/service/add.hbs b/ambari-web/app/templates/main/service/add.hbs
index 28dc83a..efe0d57 100644
--- a/ambari-web/app/templates/main/service/add.hbs
+++ b/ambari-web/app/templates/main/service/add.hbs
@@ -19,15 +19,20 @@
 <div class="wizard">
   <div class="wizard-body row">
     <div class="wizard-nav col-md-3">
-      <ul class="nav nav-pills nav-stacked">
-        <li {{bindAttr class="isStep1:active view.isStep1Disabled:disabled view.isStep1Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep1 target="controller"}}><i class="step-marker"><span class="step-index">1</span></i><p class="step-name">{{t installer.step4.header}}</p></a></li>
-        <li {{bindAttr class="isStep2:active view.isStep2Disabled:disabled view.isStep2Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep2 target="controller"}}><i class="step-marker"><span class="step-index">2</span></i><p class="step-name">{{t installer.step5.header}}</p></a></li>
-        <li {{bindAttr class="isStep3:active view.isStep3Disabled:disabled view.isStep3Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep3 target="controller"}}><i class="step-marker"><span class="step-index">3</span></i><p class="step-name">{{t installer.step6.header}}</p></a></li>
-        <li {{bindAttr class="isStep4:active view.isStep4Disabled:disabled view.isStep4Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep4 target="controller"}}><i class="step-marker"><span class="step-index">4</span></i><p class="step-name">{{t installer.step7.header}}</p></a></li>
-        <li {{bindAttr class="isStep6:active view.isStep5Disabled:disabled view.isStep5Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}><i class="step-marker"><span class="step-index">5</span></i><p class="step-name">{{t installer.step8.header}}</p></a></li>
-        <li {{bindAttr class="isStep7:active view.isStep6Disabled:disabled view.isStep6Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep6 target="controller"}}><i class="step-marker"><span class="step-index">6</span></i><p class="step-name">{{t installer.step9.header}}</p></a></li>
-        <li {{bindAttr class="isStep8:active view.isStep7Disabled:disabled view.isStep7Completed:completed"}}><a href="javascript:void(null);"  {{action gotoStep7 target="controller"}}><i class="step-marker"><span class="step-index">7</span></i><p class="step-name">{{t installer.step10.header}}</p></a></li>
-      </ul>
+      <ol class="nav nav-pills nav-stacked">
+        <li {{bindAttr class="isConfigureDownload:active view.isConfigureDownloadDisabled:disabled view.isConfigureDownloadCompleted:completed"}}><a href="javascript:void(null);" {{action gotoConfigureDownload target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.configureDownload.header}}</p></a></li>
+        <li {{bindAttr class="isSelectMpacks:active view.isSelectMpacksDisabled:disabled view.isSelectMpacksCompleted:completed"}}><a href="javascript:void(null);" {{action gotoSelectMpacks target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.selectMpacks.header}}</p></a></li>
+        <li {{bindAttr class="isCustomMpackRepos:active view.isCustomMpackReposDisabled:disabled view.isCustomMpackReposCompleted:completed"}}><a href="javascript:void(null);" {{action gotoCustomMpackRepos target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.customMpackRepos.header}}</p></a></li>
+        <li {{bindAttr class="isDownloadMpacks:active view.isDownloadMpacksDisabled:disabled view.isDownloadMpacksCompleted:completed"}}><a href="javascript:void(null);" {{action gotoDownloadMpacks target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.downloadMpacks.header}}</p></a></li>
+        <li {{bindAttr class="isCustomProductRepos:active view.isCustomProductReposDisabled:disabled view.isCustomProductReposCompleted:completed"}}><a href="javascript:void(null);" {{action gotoCustomProductRepos target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.customProductRepos.header}}</p></a></li>
+        <li {{bindAttr class="isVerifyProducts:active view.isVerifyProductsDisabled:disabled view.isVerifyProductsCompleted:completed"}}><a href="javascript:void(null);" {{action gotoVerifyProducts target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.verifyProducts.header}}</p></a></li>
+        <li {{bindAttr class="isStep5:active view.isStep5Disabled:disabled view.isStep5Completed:completed"}}><a href="javascript:void(null);" {{action gotoStep5 target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.step5.header}}</p></a></li>
+        <li {{bindAttr class="isStep6:active view.isStep6Disabled:disabled view.isStep6Completed:completed"}}><a href="javascript:void(null);" {{action gotoStep6 target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.step6.header}}</p></a></li>
+        <li {{bindAttr class="isStep7:active view.isStep7Disabled:disabled view.isStep7Completed:completed"}}><a href="javascript:void(null);" {{action gotoStep7 target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.step7.header}}</p></a></li>
+        <li {{bindAttr class="isStep8:active view.isStep8Disabled:disabled view.isStep8Completed:completed"}}><a href="javascript:void(null);" {{action gotoStep8 target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.step8.header}}</p></a></li>
+        <li {{bindAttr class="isStep9:active view.isStep9Disabled:disabled view.isStep9Completed:completed"}}><a href="javascript:void(null);" {{action gotoStep9 target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.step9.header}}</p></a></li>
+        <li {{bindAttr class="isStep10:active view.isStep10Disabled:disabled view.isStep10Completed:completed"}}><a href="javascript:void(null);" {{action gotoStep10 target="controller"}}><i class="step-marker"><span class="step-index"></span></i><p class="step-name">{{t installer.step10.header}}</p></a></li>
+      </ol>
     </div>
     {{outlet}}
   </div>
diff --git a/ambari-web/app/templates/wizard/configureDownload.hbs b/ambari-web/app/templates/wizard/configureDownload.hbs
index 9cc9b8a..41b0550 100644
--- a/ambari-web/app/templates/wizard/configureDownload.hbs
+++ b/ambari-web/app/templates/wizard/configureDownload.hbs
@@ -77,12 +77,14 @@
 </div>
 
 <div class="wizard-footer col-md-12">
-  <button type="button" class="btn btn-default pull-left installer-back-btn" {{bindAttr disabled="App.router.btnClickInProgress"}} {{action backTransition}} {{QAAttr "wizard-back"}}>
-    &larr; {{t common.back}}
-    {{#if App.router.backBtnClickInProgress}}
-      {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
-    {{/if}}
-  </button>
+  {{#unless view.parentView.controller.hideBackButton}}
+    <button type="button" class="btn btn-default pull-left installer-back-btn" {{bindAttr disabled="App.router.btnClickInProgress"}} {{action backTransition}} {{QAAttr "wizard-back"}}>
+      &larr; {{t common.back}}
+      {{#if App.router.backBtnClickInProgress}}
+        {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
+      {{/if}}
+    </button>
+  {{/unless}}
   <button type="button" class="btn btn-success pull-right" {{bindAttr disabled="view.isSubmitDisabled"}} {{action submit target="controller"}} {{QAAttr "wizard-next"}}>
     {{#if App.router.nextBtnClickInProgress}}
       {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
diff --git a/ambari-web/app/templates/wizard/selectMpacks.hbs b/ambari-web/app/templates/wizard/selectMpacks.hbs
index 5ca727a..b51cce4 100644
--- a/ambari-web/app/templates/wizard/selectMpacks.hbs
+++ b/ambari-web/app/templates/wizard/selectMpacks.hbs
@@ -104,7 +104,7 @@
     </div>
     <!-- Selection -->
     <div class="panel panel-default col-md-4 selected-list display-flex direction-col">
-      <div class="panel-heading">{{t installer.selectMpacks.body.selected.header}}&nbsp;({{controller.selectedMpackVersions.length}})</div>
+      <div class="panel-heading">{{t installer.selectMpacks.body.selected.header}}&nbsp;({{controller.allServiceGroups.length}})</div>
       <div class="panel-body flex-fill">
         {{#if view.hasServiceGroups}}
           {{#each serviceGroup in view.serviceGroups}}
@@ -123,12 +123,14 @@
 </div>
 
 <div class="wizard-footer col-md-12">
-  <button type="button" class="btn btn-default pull-left installer-back-btn" {{bindAttr disabled="App.router.btnClickInProgress"}} {{action back}} {{QAAttr "wizard-back"}}>
-    &larr; {{t common.back}}
-    {{#if App.router.backBtnClickInProgress}}
-      {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
-    {{/if}}
-  </button>
+  {{#unless view.parentView.controller.hideBackButton}}
+    <button type="button" class="btn btn-default pull-left installer-back-btn" {{bindAttr disabled="App.router.btnClickInProgress"}} {{action back}} {{QAAttr "wizard-back"}}>
+      &larr; {{t common.back}}
+      {{#if App.router.backBtnClickInProgress}}
+        {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
+      {{/if}}
+    </button>
+  {{/unless}}
   <button type="button" class="btn btn-success pull-right" {{bindAttr disabled="controller.isSubmitDisabled"}} {{action submit target="controller"}} {{QAAttr "wizard-next"}}>
     {{#if App.router.nextBtnClickInProgress}}
       {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 652b68c..2d14470 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -1406,7 +1406,7 @@ var urls = {
     }
   },
   'cluster.load_cluster_name': {
-    'real': '/clusters?fields=Clusters/security_type,Clusters/version',
+    'real': '/clusters?fields=Clusters/security_type,Clusters/version,Clusters/cluster_id',
     'mock': '/data/clusters/info.json'
   },
   'cluster.load_last_upgrade': {
@@ -1679,7 +1679,7 @@ var urls = {
   },
 
   'admin.stack_upgrade.do_poll': {
-    'real': '/clusters/{cluster}/requests/{requestId}?fields=tasks/*',
+    'real': '/clusters/{clusterName}/requests/{requestId}?fields=tasks/*',
     'mock': '/data/wizard/{mock}'
   },
   'admin.upgrade.data': {
@@ -2003,11 +2003,11 @@ var urls = {
     'mock': '/data/stacks/HDP-2.1/service_components.json'
   },
   'wizard.step9.installer.get_host_status': {
-    'real': '/clusters/{cluster}/hosts?fields=Hosts/host_state,host_components/HostRoles/state',
+    'real': '/clusters/{clusterName}/hosts?fields=Hosts/host_state,host_components/HostRoles/state',
     'mock': '/data/wizard/deploy/5_hosts/get_host_state.json'
   },
   'wizard.step9.load_log': {
-    'real': '/clusters/{cluster}/requests/{requestId}?fields=tasks/Tasks/command,tasks/Tasks/command_detail,tasks/Tasks/ops_display_name,tasks/Tasks/exit_code,tasks/Tasks/start_time,tasks/Tasks/end_time,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status&minimal_response=true',
+    'real': '/clusters/{clusterName}/requests/{requestId}?fields=tasks/Tasks/command,tasks/Tasks/command_detail,tasks/Tasks/ops_display_name,tasks/Tasks/exit_code,tasks/Tasks/start_time,tasks/Tasks/end_time,tasks/Tasks/host_name,tasks/Tasks/id,tasks/Tasks/role,tasks/Tasks/status&minimal_response=true',
     'mock': '/data/wizard/deploy/5_hosts/poll_{numPolls}.json',
     'format': function () {
       return {
@@ -2022,7 +2022,7 @@ var urls = {
   },
 
   'wizard.step8.create_cluster': {
-    'real': '/clusters/{cluster}',
+    'real': '/clusters/{clusterName}',
     'mock': '',
     'format': function (data) {
       return {
@@ -2034,7 +2034,7 @@ var urls = {
   },
 
   'wizard.step8.create_service_group': {
-    'real': '/clusters/{cluster}/servicegroups',
+    'real': '/clusters/{clusterName}/servicegroups',
     'mock': '',
     'format': function (data) {
       return {
@@ -2047,7 +2047,7 @@ var urls = {
 
   'wizard.step8.create_selected_services': {
     'type': 'POST',
-    'real': '/clusters/{cluster}/servicegroups/{serviceGroupName}/services',
+    'real': '/clusters/{clusterName}/servicegroups/{serviceGroupName}/services',
     'mock': '/data/stacks/HDP-2.1/recommendations.json',
     'format': function (data) {
       return {
@@ -2058,7 +2058,7 @@ var urls = {
   },
 
   'wizard.step8.create_components': {
-    'real': '/clusters/{cluster}/servicegroups/{serviceGroupName}/services/{serviceName}/components',
+    'real': '/clusters/{clusterName}/servicegroups/{serviceGroupName}/services/{serviceName}/components',
     'mock': '',
     'format': function (data) {
       return {
@@ -2070,7 +2070,7 @@ var urls = {
   },
 
   'wizard.step8.register_host_to_cluster': {
-    'real': '/clusters/{cluster}/hosts',
+    'real': '/clusters/{clusterName}/hosts',
     'mock': '',
     'format': function (data) {
       return {
@@ -2082,7 +2082,7 @@ var urls = {
   },
 
   'wizard.step8.register_host_to_component': {
-    'real': '/clusters/{cluster}/hosts',
+    'real': '/clusters/{clusterName}/hosts',
     'mock': '',
     'format': function (data) {
       return {
@@ -2094,7 +2094,7 @@ var urls = {
   },
 
   'wizard.step8.apply_configuration_groups': {
-    'real': '/clusters/{cluster}/config_groups',
+    'real': '/clusters/{clusterName}/config_groups',
     'mock': '',
     'format': function (data) {
       return {
@@ -2284,7 +2284,7 @@ var urls = {
     mock: '/data/users/privileges_{userName}.json'
   },
   'router.login.clusters': {
-    'real': '/clusters?fields=Clusters/provisioning_state,Clusters/security_type,Clusters/version',
+    'real': '/clusters?fields=Clusters/provisioning_state,Clusters/security_type,Clusters/version,Clusters/cluster_id',
     'mock': '/data/clusters/info.json'
   },
   'router.login.message': {
diff --git a/ambari-web/app/utils/config.js b/ambari-web/app/utils/config.js
index addf269..eba7d19 100644
--- a/ambari-web/app/utils/config.js
+++ b/ambari-web/app/utils/config.js
@@ -218,7 +218,7 @@ App.config = Em.Object.create({
       });
     });
     return ret;
-  }.property(),
+  }.property().volatile(),
 
   /**
    * Generate configs collection with Ember or plain config objects
@@ -867,7 +867,7 @@ App.config = Em.Object.create({
    *
    * @param configs
    */
-  addYarnCapacityScheduler: function(configs) {
+  addYarnCapacityScheduler: function (configs) {
     var value = '', savedValue = '', recommendedValue = '',
       excludedConfigs = App.config.getPropertiesFromTheme('YARN');
 
diff --git a/ambari-web/test/controllers/main/service/add_controller_test.js b/ambari-web/test/controllers/main/service/add_controller_test.js
index 0616a96..b8415d0 100644
--- a/ambari-web/test/controllers/main/service/add_controller_test.js
+++ b/ambari-web/test/controllers/main/service/add_controller_test.js
@@ -16,389 +16,528 @@
  * limitations under the License.
  */
 
+
 var App = require('app');
+require('models/cluster');
 require('controllers/wizard');
 require('controllers/main/service/add_controller');
-var addServiceController = null;
-var testHelpers = require('test/helpers');
 
-describe('App.AddServiceController', function() {
+describe('App.AddServiceController', function () {
+
+  var addServiceController = App.AddServiceController.create();
 
-  beforeEach(function () {
-    addServiceController = App.AddServiceController.create({});
+  after(function () {
+    addServiceController.destroy();
   });
 
-  describe('#generateDataForInstallServices', function() {
-    var tests = [{
-      selected: ["YARN","HBASE"],
-      res: {
-        "context": Em.I18n.t('requestInfo.installServices'),
-        "ServiceInfo": {"state": "INSTALLED"},
-        "urlParams": "ServiceInfo/service_name.in(YARN,HBASE)"
-      }
-    },
-    {
-      selected: ['OOZIE'],
-      res: {
-        "context": Em.I18n.t('requestInfo.installServices'),
-        "ServiceInfo": {"state": "INSTALLED"},
-        "urlParams": "ServiceInfo/service_name.in(OOZIE,HDFS,YARN,MAPREDUCE2)"
+  describe('#init', function () {
+    var c;
+    beforeEach(function () {
+      c = App.AddServiceController.create({});
+    });
+    it('all steps are disabled by default', function () {
+      expect(c.get('isStepDisabled.length')).to.eq(c.get('totalSteps'));
+      for (var i = 0, length = c.get('isStepDisabled.length'); i < length; i++) {
+        expect(c.get('isStepDisabled').findProperty('step', i).get('value')).to.eq(true);
       }
-    }];
-    tests.forEach(function(t){
-      it('should generate data with ' + t.selected.join(","), function () {
-        expect(addServiceController.generateDataForInstallServices(t.selected)).to.be.eql(t.res);
-      });
     });
   });
 
-  describe('#saveServices', function() {
+  describe('#setLowerStepsDisable', function() {
+
+    beforeEach(function () {
+      var steps = Em.A([
+        Em.Object.create({
+          step: 0,
+          value: false
+        }),
+        Em.Object.create({
+          step: 1,
+          value: false
+        }),
+        Em.Object.create({
+          step: 2,
+          value: false
+        }),
+        Em.Object.create({
+          step: 3,
+          value: false
+        }),
+        Em.Object.create({
+          step: 4,
+          value: false
+        })
+      ]);
+      addServiceController.set('isStepDisabled', steps);
+      addServiceController.setLowerStepsDisable(3);
+    });
+
+    it('Should disable lower steps', function() {
+      var expected = [
+        {
+          "step": 0,
+          "value": true
+        },
+        {
+          "step": 1,
+          "value": true
+        },
+        {
+          "step": 2,
+          "value": true
+        },
+        {
+          "step": 3,
+          "value": false
+        },
+        {
+          "step": 4,
+          "value": false
+        }
+      ];
+      var res = JSON.parse(JSON.stringify(addServiceController.get('isStepDisabled')));
+      expect(res).to.eql(expected);
+    });
+  });
+
+  describe('#totalSteps', function() {
     beforeEach(function() {
-      sinon.stub(addServiceController, 'setDBProperty', Em.K);
+      addServiceController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
     });
 
-    afterEach(function() {
-      addServiceController.setDBProperty.restore();
+    it('Should return the number of steps', function() {
+      var totalSteps = addServiceController.get('totalSteps');
+      expect(totalSteps).to.eq(addServiceController.get("steps").length);
     });
+  })
 
-    var tests = [
-      {
-        appService: [
-          Em.Object.create({ serviceName: 'HDFS' }),
-          Em.Object.create({ serviceName: 'KERBEROS' })
-        ],
-        stepCtrlContent: Em.Object.create({
-          content: Em.A([
-            Em.Object.create({ serviceName: 'HDFS', isInstalled: true, isSelected: true }),
-            Em.Object.create({ serviceName: 'YARN', isInstalled: false, isSelected: true })
-          ])
+  describe('#setStepsEnable', function() {
+    beforeEach(function () {
+      var steps = Em.A([
+        Em.Object.create({
+          step: 0,
+          value: true
         }),
-        e: {
-          selected: ['YARN'],
-          installed: ['HDFS', 'KERBEROS']
-        }
-      },
-      {
-        appService: [
-          Em.Object.create({ serviceName: 'HDFS' }),
-          Em.Object.create({ serviceName: 'STORM' })
-        ],
-        stepCtrlContent: Em.Object.create({
-          content: Em.A([
-            Em.Object.create({ serviceName: 'HDFS', isInstalled: true, isSelected: true }),
-            Em.Object.create({ serviceName: 'YARN', isInstalled: false, isSelected: true }),
-            Em.Object.create({ serviceName: 'MAPREDUCE2', isInstalled: false, isSelected: true })
-          ])
+        Em.Object.create({
+          step: 1,
+          value: true
         }),
-        e: {
-          selected: ['YARN', 'MAPREDUCE2'],
-          installed: ['HDFS', 'STORM']
-        }
-      }
-    ];
-
-    var message = '{0} installed, {1} selected. Installed list should be {2} and selected - {3}';
-    tests.forEach(function(test) {
+        Em.Object.create({
+          step: 2,
+          value: true
+        }),
+        Em.Object.create({
+          step: 3,
+          value: true
+        }),
+        Em.Object.create({
+          step: 4,
+          value: true
+        })
+      ]);
+      
+      addServiceController.set('isStepDisabled', steps);
+      
+      addServiceController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
+      
+      addServiceController.set('currentStep', 2);
+    });
 
-      var installed = test.appService.mapProperty('serviceName');
-      var selected = test.stepCtrlContent.get('content').filterProperty('isSelected', true)
-        .filterProperty('isInstalled', false).mapProperty('serviceName');
+    it('Should enable next steps', function() {
+      var stepController = Em.Object.create({
+        isStepDisabled: function () {
+          return false;
+        }
+      });
 
-      describe(message.format(installed, selected, test.e.installed, test.e.selected), function() {
+      sinon.stub(addServiceController, 'getStepController').returns(stepController);
 
-        beforeEach(function () {
-          sinon.stub(App.Service, 'find').returns(test.appService);
-          addServiceController.saveServices(test.stepCtrlContent);
-          this.savedServices = addServiceController.setDBProperty.withArgs('services').args[0][1];
-        });
+      var expected = [
+        {
+          "step": 0,
+          "value": false
+        },
+        {
+          "step": 1,
+          "value": false
+        },
+        {
+          "step": 2,
+          "value": false
+        },
+        {
+          "step": 3,
+          "value": false
+        },
+        {
+          "step": 4,
+          "value": false
+        }
+      ];
 
-        afterEach(function () {
-          App.Service.find.restore();
-        });
+      addServiceController.setStepsEnable();
 
-        it(JSON.stringify(test.e.selected) + ' are in the selectedServices', function () {
-          expect(this.savedServices.selectedServices).to.have.members(test.e.selected);
-        });
+      var res = JSON.parse(JSON.stringify(addServiceController.get('isStepDisabled')));
+      expect(res).to.eql(expected);
 
-        it(JSON.stringify(test.e.installed) + ' are in the installedServices', function () {
-          expect(this.savedServices.installedServices).to.have.members(test.e.installed);
-        });
+      addServiceController.getStepController.restore();
+    });
+  });
 
+  describe('#saveMasterComponentHosts', function() {
+    beforeEach(function () {
+      sinon.stub(addServiceController, 'getDBProperty').returns({
+        'h1': {
+          id: 11
+        },
+        'h3': {
+          id: 13
+        },
+        'h2': {
+          id: 12
+        }
+      });
+    });
+    afterEach(function () {
+      addServiceController.getDBProperty.restore();
+    });
+    it ('Should return hosts', function() {
+      var stepController = Em.Object.create({
+        selectedServicesMasters: Em.A([
+          Em.Object.create({
+            display_name: 'n1',
+            component_name: 'c1',
+            serviceId: 1,
+            selectedHost: 'h1',
+            mpackInstance: 'm1'
+          })
+        ])
       });
+      addServiceController.saveMasterComponentHosts(stepController);
+      expect(addServiceController.get('content.masterComponentHosts')).to.eql([
+        {
+          "display_name": "n1",
+          "component": "c1",
+          "serviceId": 1,
+          "isInstalled": false,
+          "host_id": 11,
+          "serviceGroupName": "m1"
+        }
+      ]);
     });
   });
 
-  describe('#loadHosts', function () {
-
-    var cases = [
-      {
-        hosts: {},
-        isAjaxRequestSent: false,
-        title: 'hosts are already loaded'
-      },
-      {
-        areHostsLoaded: false,
-        isAjaxRequestSent: true,
-        title: 'hosts aren\'t yet loaded'
-      }
-    ];
-
+  describe('#loadConfirmedHosts', function() {
+    beforeEach(function () {
+      sinon.stub(addServiceController, 'getDBProperty').returns({
+        'h1': {
+          id: 11
+        },
+        'h3': {
+          id: 13
+        },
+        'h2': {
+          id: 12
+        }
+      });
+    });
     afterEach(function () {
       addServiceController.getDBProperty.restore();
     });
-
-    cases.forEach(function (item) {
-      describe(item.title, function () {
-
-        beforeEach(function () {
-          sinon.stub(addServiceController, 'getDBProperty').withArgs('hosts').returns(item.hosts);
-          addServiceController.loadHosts();
-          this.args = testHelpers.findAjaxRequest('name', 'hosts.confirmed');
-        });
-
-        it('request is ' + (item.isAjaxRequestSent ? '' : 'not') + ' sent', function () {
-          expect(Em.isNone(this.args)).to.be.equal(!item.isAjaxRequestSent);
-        });
+    it ('Should load hosts from db', function() {
+      addServiceController.loadConfirmedHosts();
+      expect(addServiceController.get('content.hosts')).to.eql({
+        'h1': {
+          id: 11
+        },
+        'h3': {
+          id: 13
+        },
+        'h2': {
+          id: 12
+        }
       });
     });
-
   });
 
-  describe('#loadHostsSuccessCallback', function () {
-
-    it('should load hosts to local db and model', function () {
-      var hostComponents = [
-          [
+  describe('#loadMasterComponentHosts', function() {
+    beforeEach(function () {
+      addServiceController.set('content.masterComponentHosts', null);
+      sinon.stub(addServiceController, 'getDBProperties', function() {
+        return {
+          masterComponentHosts: Em.A([
             {
-              HostRoles: {
-                component_name: 'c0',
-                state: 'STARTED'
-              }
+              hostName: '',
+              host_id: 11
+            }
+          ]),
+          hosts: {
+            'h1': {
+              id: 11
             },
-            {
-              HostRoles: {
-                component_name: 'c1',
-                state: 'INSTALLED'
-              }
+            'h3': {
+              id: 13
+            },
+            'h2': {
+              id: 12
             }
-          ],
-          [
-            {
-              HostRoles: {
-                component_name: 'c2',
-                state: 'STARTED'
-              }
+          }
+        }
+      });
+    });
+    afterEach(function () {
+      addServiceController.getDBProperties.restore();
+    });
+    it('Should load hosts', function() {
+      addServiceController.loadMasterComponentHosts();
+      expect(addServiceController.get('content.masterComponentHosts')).to.eql([
+        {
+          "hostName": "h1",
+          "host_id": 11
+        }
+      ]);
+    });
+  });
+
+  describe('#loadSlaveComponentHosts', function() {
+    beforeEach(function () {
+      sinon.stub(addServiceController, 'getDBProperties', function() {
+        return {
+          hosts: {
+            'h1': {
+              id: 11
             },
-            {
-              HostRoles: {
-                component_name: 'c3',
-                state: 'INSTALLED'
-              }
+            'h3': {
+              id: 13
+            },
+            'h2': {
+              id: 12
             }
-          ]
-        ],
-        response = {
-          items: [
+          },
+          slaveComponentHosts: Em.A([
             {
-              Hosts: {
-                host_name: 'h0',
-              },
-              host_components: hostComponents[0]
-            },
+              hosts: Em.A([
+                {
+                  hostName: '',
+                  host_id: 11
+                }
+              ])
+            }
+          ])
+        };
+      });
+    });
+    afterEach(function () {
+      addServiceController.getDBProperties.restore();
+    });
+    it ('Should load slave hosts', function() {
+      addServiceController.loadSlaveComponentHosts();
+      expect(addServiceController.get('content.slaveComponentHosts')).to.eql([
+        {
+          "hosts": [
             {
-              Hosts: {
-                host_name: 'h1'
-              },
-              host_components: hostComponents[1]
+              "hostName": "h1",
+              "host_id": 11
             }
           ]
-        },
-        expected = {
-          h0: {
-            name: 'h0',
-            bootStatus: 'REGISTERED',
-            isInstalled: true,
-            hostComponents: hostComponents[0],
-            id: 0
-          },
-          h1: {
-            name: 'h1',
-            bootStatus: 'REGISTERED',
-            isInstalled: true,
-            hostComponents: hostComponents[1],
-            id: 1
-          }
-        };
-      addServiceController.loadHostsSuccessCallback(response);
-      var hostsInDb = addServiceController.getDBProperty('hosts');
-      var hostsInModel = addServiceController.get('content.hosts');
-      expect(hostsInDb).to.eql(expected);
-      expect(hostsInModel).to.eql(expected);
+        }
+      ]);
     });
-
   });
 
-  describe('#loadServices', function() {
-    var mock = {
-      db: {}
-    };
+  describe('#setStepSaved', function() {
     beforeEach(function() {
-      this.controller = App.AddServiceController.create({});
-      this.mockGetDBProperty = sinon.stub(this.controller, 'getDBProperty');
-      sinon.stub(this.controller, 'setDBProperty', function(key, value) {
-        mock.db = value;
+      addServiceController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
+
+      addServiceController.set('content.stepsSavedState', null);
+    });
+
+    it('Should save step and unsave all subsequent steps when step is not saved yet', function() {
+      var expected = Em.Object.create({
+        "1": true,
+        "2": false,
+        "3": false,
+        "4": false
       });
-      sinon.stub(this.controller, 'hasDependentSlaveComponent');
-      sinon.stub(App.store, 'fastCommit', Em.K);
-      this.mockStackService = sinon.stub(App.StackService, 'find');
-      this.mockService = sinon.stub(App.Service, 'find');
+
+      addServiceController.setStepSaved('step1');
+      var actual = addServiceController.get('content.stepsSavedState');
+      expect(actual).to.deep.equal(expected);
+      expect(addServiceController.getStepSavedState('step1')).to.be.true;
     });
 
-    afterEach(function() {
-      this.mockGetDBProperty.restore();
-      this.controller.setDBProperty.restore();
-      this.controller.hasDependentSlaveComponent.restore();
-      this.mockStackService.restore();
-      this.mockService.restore();
-      App.store.fastCommit.restore();
+    it('Should do nothing when step is already saved', function() {
+      var expected = Em.Object.create({
+        "1": true,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+      addServiceController.set('content.stepsSavedState', expected);
+
+      addServiceController.setStepSaved('step1');
+      var actual = addServiceController.get('content.stepsSavedState');
+      expect(actual).to.deep.equal(expected);
+      expect(addServiceController.getStepSavedState('step1')).to.be.true;
     });
+  });
 
-    var tests = [
-      {
-        appStackService: [
-          Em.Object.create({ id: 'HDFS', serviceName: 'HDFS', coSelectedServices: []}),
-          Em.Object.create({ id: 'YARN', serviceName: 'YARN', coSelectedServices: ['MAPREDUCE2']}),
-          Em.Object.create({ id: 'MAPREDUCE2', serviceName: 'MAPREDUCE2', coSelectedServices: []}),
-          Em.Object.create({ id: 'FALCON', serviceName: 'FALCON', coSelectedServices: []}),
-          Em.Object.create({ id: 'STORM', serviceName: 'STORM', coSelectedServices: []})
-        ],
-        appService: [
-          Em.Object.create({ id: 'HDFS', serviceName: 'HDFS'}),
-          Em.Object.create({ id: 'STORM', serviceName: 'STORM'})
-        ],
-        servicesFromDB: false,
-        serviceToInstall: 'MAPREDUCE2',
-        e: {
-          selectedServices: ['HDFS', 'YARN', 'MAPREDUCE2', 'STORM'],
-          installedServices: ['HDFS', 'STORM']
-        },
-        m: 'MapReduce selected on Admin -> Stack Versions Page, Yarn service should be selected because it coselected'
-      },
-      {
-        appStackService: [
-          Em.Object.create({ id: 'HDFS', serviceName: 'HDFS', coSelectedServices: []}),
-          Em.Object.create({ id: 'YARN', serviceName: 'YARN', coSelectedServices: ['MAPREDUCE2']}),
-          Em.Object.create({ id: 'HBASE', serviceName: 'HBASE', coSelectedServices: []}),
-          Em.Object.create({ id: 'STORM', serviceName: 'STORM', coSelectedServices: []})
-        ],
-        appService: [
-          Em.Object.create({ id: 'HDFS', serviceName: 'HDFS'}),
-          Em.Object.create({ id: 'STORM', serviceName: 'STORM'})
-        ],
-        servicesFromDB: {
-          selectedServices: ['HBASE'],
-          installedServices: ['HDFS', 'STORM']
-        },
-        serviceToInstall: null,
-        e: {
-          selectedServices: ['HDFS', 'HBASE', 'STORM'],
-          installedServices: ['HDFS', 'STORM']
-        },
-        m: 'HDFS and STORM are installed. Select HBASE'
-      }
-    ];
+  describe('#setStepUnsaved', function() {
+    beforeEach(function() {
+      addServiceController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
 
-    tests.forEach(function(test) {
-      describe(test.m, function() {
+      var initial = Em.Object.create({
+        "1": true,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+      addServiceController.set('content.stepsSavedState', initial);
+    });
 
-        beforeEach(function () {
-          this.mockStackService.returns(test.appStackService);
-          this.mockService.returns(test.appService);
-          this.mockGetDBProperty.withArgs('services').returns(test.servicesFromDB);
-          this.controller.set('serviceToInstall', test.serviceToInstall);
-          this.controller.loadServices();
-        });
+    it('Should set step to unsaved', function() {
+      var expected = Em.Object.create({
+        "1": false,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+
+      addServiceController.setStepUnsaved('step1');
+      var actual = addServiceController.get('content.stepsSavedState');
+      expect(actual).to.deep.equal(expected);
+      expect(addServiceController.getStepSavedState('step1')).to.be.false;
+    });
+  });
 
-        if (test.servicesFromDB) {
-          // verify values for App.StackService
-          it(JSON.stringify(test.e.selectedServices) + ' are selected', function () {
-            expect(test.appStackService.filterProperty('isSelected', true).mapProperty('serviceName')).to.be.eql(test.e.selectedServices);
-          });
-          it(JSON.stringify(test.e.installedServices) + ' are installed', function () {
-            expect(test.appStackService.filterProperty('isInstalled', true).mapProperty('serviceName')).to.be.eql(test.e.installedServices);
-          });
-        }
-        else {
-          // verify saving to local db on first enter to the wizard
-          it('selectedServices are saced', function () {
-            expect(mock.db.selectedServices).to.be.eql(test.e.selectedServices);
-          });
-          it('installedServices are saved', function () {
-            expect(mock.db.installedServices).to.be.eql(test.e.installedServices);
-          });
+  describe('#getStepSavedState', function() {
+    beforeEach(function() {
+      addServiceController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
 
-        }
+      var initial = Em.Object.create({
+        "1": true,
+        "2": false
+      })
+      addServiceController.set('content.stepsSavedState', initial);
+    });
 
-        it('serviceToInstall is null', function () {
-          expect(this.controller.get('serviceToInstall')).to.be.null;
-        });
+    it('Should return false for bad step name', function() {
+      expect(addServiceController.getStepSavedState('step5')).to.be.false;
+    });
 
-      });
-    }, this);
+    it('Should return false for step that was never saved', function() {
+      expect(addServiceController.getStepSavedState('step0')).to.be.false;
+    });
   });
 
-  describe('#loadServiceConfigGroups', function () {
+  describe('#hasErrors', function () {
+    before(function () {
+      addServiceController.addError("There is an error.");
+    });
 
-    var dbMock,
-      dbMock2,
-      cases = [
-        {
-          serviceConfigGroups: null,
-          areInstalledConfigGroupsLoaded: false,
-          title: 'config groups not yet loaded'
-        },
-        {
-          serviceConfigGroups: [],
-          areInstalledConfigGroupsLoaded: true,
-          title: 'config groups already loaded'
-        }
-      ];
+    it('Should return true if there are errors.', function () {
+      var hasErrors = addServiceController.get('hasErrors');
 
-    beforeEach(function () {
-      dbMock = sinon.stub(addServiceController, 'getDBProperties');
-      dbMock2 = sinon.stub(addServiceController, 'getDBProperty');
+      expect(hasErrors).to.be.true;
     });
 
-    afterEach(function () {
-      dbMock.restore();
-      dbMock2.restore();
+    it('Should return false if there are no errors.', function () {
+      addServiceController.clearErrors();
+      var hasErrors = addServiceController.get('hasErrors');
+
+      expect(hasErrors).to.be.false;
     });
+  });
 
-    cases.forEach(function (item) {
-      it(item.title, function () {
-        dbMock.withArgs(['serviceConfigGroups', 'hosts']).returns({
-          hosts: {},
-          serviceConfigGroups: item.serviceConfigGroups
-        });
-        dbMock2.withArgs('hosts').returns({}).
-          withArgs('serviceConfigGroups').returns(item.serviceConfigGroups);
-        addServiceController.loadServiceConfigGroups();
-        expect(addServiceController.get('areInstalledConfigGroupsLoaded')).to.equal(item.areInstalledConfigGroupsLoaded);
-      });
+  describe('#getStepController', function () {
+    var wizardStep0Controller = {};
+    var wizardStep2Controller = {};
+
+    before(function () {
+      addServiceController.set('steps', [
+        "step0",
+        "step1",
+        "step2"
+      ]);
+
+      App.router.set('wizardStep0Controller', wizardStep0Controller);
+      App.router.set('wizardStep2Controller', wizardStep2Controller);
+    });
+
+    it('Should return controller for the step number provided.', function () {
+      var stepController = addServiceController.getStepController(2);
+      expect(stepController).to.equal(wizardStep2Controller);
     });
 
+    it('Should return controller for the step name provided.', function () {
+      var stepController = addServiceController.getStepController("step0");
+      expect(stepController).to.equal(wizardStep0Controller);
+    });
   });
 
-  describe('#clearStorageData', function () {
-    it('areInstalledConfigGroupsLoaded should be false', function () {
-      addServiceController.set('areInstalledConfigGroupsLoaded', true);
-      addServiceController.clearStorageData();
-      expect(addServiceController.get('areInstalledConfigGroupsLoaded')).to.be.false;
+  describe('#finish', function() {
+    beforeEach(function() {
+      sinon.stub(addServiceController, 'setCurrentStep');
+      sinon.stub(addServiceController, 'clearStorageData');
+      sinon.stub(addServiceController, 'clearServiceConfigProperties');
+      sinon.stub(App.themesMapper, 'resetModels');
+      addServiceController.finish();
+    });
+    afterEach(function() {
+      addServiceController.setCurrentStep.restore();
+      addServiceController.clearStorageData.restore();
+      addServiceController.clearServiceConfigProperties.restore();
+      App.themesMapper.resetModels.restore();
+    });
+
+    it('setCurrentStep should be called', function() {
+      expect(addServiceController.setCurrentStep.calledWith('0')).to.be.true;
+    });
+
+    it('clearStorageData should be called', function() {
+      expect(addServiceController.clearStorageData.calledOnce).to.be.true;
+    });
+
+    it('clearServiceConfigProperties should be called', function() {
+      expect(addServiceController.clearServiceConfigProperties.calledOnce).to.be.true;
+    });
+
+    it('App.themesMapper.resetModels should be called', function() {
+      expect(App.themesMapper.resetModels.calledOnce).to.be.true;
     });
   });
 
   describe('#loadClients', function () {
-
     var cases = [
       {
         clients: null,
@@ -415,129 +554,21 @@ describe('App.AddServiceController', function() {
     ];
 
     cases.forEach(function (item) {
-
       describe(item.title, function () {
-
         beforeEach(function () {
           sinon.stub(addServiceController, 'getDBProperty').withArgs('clients').returns(item.clients);
-          sinon.stub(addServiceController, 'saveClients', Em.K);
           addServiceController.set('content.clients', []);
           addServiceController.loadClients();
         });
 
         afterEach(function () {
           addServiceController.getDBProperty.restore();
-          addServiceController.saveClients.restore();
         });
 
         it('content.clients', function () {
           expect(addServiceController.get('content.clients', [])).to.eql(item.contentClients);
         });
-
-        it('saveClients call', function () {
-          expect(addServiceController.saveClients.callCount).to.equal(item.saveClientsCallCount);
-        });
-
       });
-
-    });
-
-  });
-
-  describe('#getServicesBySelectedSlaves', function () {
-
-    beforeEach(function () {
-      sinon.stub(App.StackServiceComponent, 'find').returns([
-        Em.Object.create({
-          componentName: 'c1',
-          serviceName: 's1'
-        }),
-        Em.Object.create({
-          componentName: 'c2',
-          serviceName: 's2'
-        }),
-        Em.Object.create({
-          componentName: 'c3',
-          serviceName: 's3'
-        }),
-        Em.Object.create({
-          componentName: 'c4',
-          serviceName: 's1'
-        })
-      ]);
     });
-
-    [
-      {
-        title: 'should return empty array',
-        sch: [],
-        expect: []
-      },
-      {
-        title: 'should return empty array if component is absent in StackServiceComponent model',
-        sch: [
-          {
-            componentName: 'c5',
-            hosts: [
-              {
-                isInstalled: false
-              },
-              {
-                isInstalled: true
-              }
-            ]
-          },
-        ],
-        expect: []
-      },
-      {
-        title: 'should return services for not installed slaves',
-        sch: [
-          {
-            componentName: 'c1',
-            hosts: [
-              {
-                isInstalled: false
-              },
-              {
-                isInstalled: true
-              }
-            ]
-          },
-          {
-            componentName: 'c2',
-            hosts: [
-              {
-                isInstalled: false
-              },
-              {
-                isInstalled: true
-              }
-            ]
-          },
-          {
-            componentName: 'c4',
-            hosts: [
-              {
-                isInstalled: false
-              },
-              {
-                isInstalled: true
-              }
-            ]
-          }
-        ],
-        expect: ['s1', 's2']
-      }
-    ].forEach(function (test) {
-          describe(test.title, function () {
-            it(function () {
-              addServiceController.set('content.slaveComponentHosts', test.sch);
-              expect(addServiceController.getServicesBySelectedSlaves()).to.eql(test.expect);
-            });
-          })
-        });
-
   });
-
 });
diff --git a/ambari-web/test/controllers/wizard/selectMpacks_test.js b/ambari-web/test/controllers/wizard/selectMpacks_test.js
index ee6420e..4a96761 100644
--- a/ambari-web/test/controllers/wizard/selectMpacks_test.js
+++ b/ambari-web/test/controllers/wizard/selectMpacks_test.js
@@ -477,7 +477,8 @@ describe('App.WizardSelectMpacksController', function () {
       content: Em.Object.create({
         selectedServices: null,
         selectedServiceNames: null,
-        selectedMpacks: null
+        selectedMpacks: null,
+        registeredMpacks: []
       }),
       wizardController: wizardController      
     });
@@ -493,20 +494,26 @@ describe('App.WizardSelectMpacksController', function () {
 
   describe('#loadStep', function () {
     it('adds previously selected services to selection', function () {
-      // wizardSelectMpacksController.set('content.selectedServices', [
-      //   { id: "HDPCORE1.0.0-b85ZOOKEEPER" },
-      //   { id: "HDPCORE1.0.0-b85HDFS" }
-      // ]);
+      wizardSelectMpacksController.set('content.serviceGroups', [
+        {
+          name: "HDPCORE",
+          mpackVersionId: "HDPCORE-1.0.0-b85"
+        }
+      ]);
 
-      // wizardSelectMpacksController.loadStep();
-      
-      // var service = wizardSelectMpacksController.getServiceVersionById("HDPCORE1.0.0-b85ZOOKEEPER");
-      // expect(service.get('selected')).to.be.true;
-      // expect(service.get('mpackVersion.selected')).to.be.true;
+      wizardSelectMpacksController.set('content.serviceInstances', [
+        {
+          name: "ZOOKEEPER",
+          serviceGroupName: "HDPCORE",
+          serviceName: "ZOOKEEPER"
+        }
+      ]);
 
-      // var service = wizardSelectMpacksController.getServiceVersionById("HDPCORE1.0.0-b85HDFS");
-      // expect(service.get('selected')).to.be.true;
-      // expect(service.get('mpackVersion.selected')).to.be.true;
+      wizardSelectMpacksController.loadStep();
+      
+      var service = wizardSelectMpacksController.getServiceVersionById("HDPCORE-1.0.0-b85-ZOOKEEPER");
+      expect(service.get('selected')).to.be.true;
+      expect(service.get('mpackVersion.selected')).to.be.true;
     });
   });
 
@@ -807,7 +814,7 @@ describe('App.WizardSelectMpacksController', function () {
       var canRemove = true;
 
       var expected = Em.Object.create({
-        id: serviceGroup.get('name') + name,
+        id: serviceGroup.get('name') + '-' + name,
         name: name,
         service: service,
         serviceGroup: serviceGroup,
@@ -939,29 +946,51 @@ describe('App.WizardSelectMpacksController', function () {
   });
 
   describe('#addMpackHandler', function () {
-    it('should make all calls required to add an mpack', function () {
-      var stub1 = sinon.stub(wizardSelectMpacksController, 'addMpackVersion').returns(Em.Object.create({
+    var stub1, stub2, stub3, stub4, stub5;
+
+    beforeEach(function () {
+      stub1 = sinon.stub(wizardSelectMpacksController, 'addMpackVersion').returns(Em.Object.create({
         services: [
           Em.Object.create({}),
           Em.Object.create({}),
           Em.Object.create({})
         ]
       }));
-      var stub2 = sinon.stub(wizardSelectMpacksController, 'addServiceGroup').returns(true);
-      var stub3 = sinon.stub(wizardSelectMpacksController, 'addServiceInstance');
-      var stub4 = sinon.stub(wizardController, 'setStepUnsaved');
+      stub2 = sinon.stub(wizardSelectMpacksController, 'addServiceGroup').returns(true);
+      stub3 = sinon.stub(wizardSelectMpacksController, 'addServiceInstance');
+      stub4 = sinon.stub(wizardController, 'setStepUnsaved');
+    });
+
+    afterEach(function () {
+      wizardSelectMpacksController.addMpackVersion.restore();
+      wizardSelectMpacksController.addServiceGroup.restore();
+      wizardSelectMpacksController.addServiceInstance.restore();
+      wizardController.setStepUnsaved.restore();
+      wizardSelectMpacksController.getServiceGroup.restore();
+    })
+
+    it('should make all calls required to add an mpack (service group exists)', function () {
+      stub5 = sinon.stub(wizardSelectMpacksController, 'getServiceGroup').returns(true);
 
       wizardSelectMpacksController.addMpackHandler();
 
       expect(stub1.called).to.be.true;
-      expect(stub2.called).to.be.true;
+      expect(stub2.called).to.be.false;
       expect(stub3.calledThrice).to.be.true;
       expect(stub4.called).to.be.true;
+      expect(stub5.called).to.be.true;
+    });
 
-      wizardSelectMpacksController.addMpackVersion.restore();
-      wizardSelectMpacksController.addServiceGroup.restore();
-      wizardSelectMpacksController.addServiceInstance.restore();
-      wizardController.setStepUnsaved.restore();
+    it('should make all calls required to add an mpack (service group does not exist)', function () {
+      stub5 = sinon.stub(wizardSelectMpacksController, 'getServiceGroup').returns(false);
+
+      wizardSelectMpacksController.addMpackHandler();
+
+      expect(stub1.called).to.be.true;
+      expect(stub2.called).to.be.true;
+      expect(stub3.calledThrice).to.be.true;
+      expect(stub4.called).to.be.true;
+      expect(stub5.called).to.be.true;
     });
   });
 
@@ -1209,7 +1238,7 @@ describe('App.WizardSelectMpacksController', function () {
   });
 
   describe('#submit', function () {
-    it('should populate content.addedServiceGroups, content.addedServiceInstances, and content.selectedMpacks', function () {
+    it('should populate content.addedServiceGroups, content.addedServiceInstances, and content.selectedMpacks, and content.mpacksToRegister', function () {
       wizardSelectMpacksController.set('addedServiceGroups', [
         Em.Object.create({
           name: 'name',
@@ -1241,6 +1270,23 @@ describe('App.WizardSelectMpacksController', function () {
         })
       ]);
       
+      wizardSelectMpacksController.set('content.registeredMpacks', [
+        {
+          MpackInfo: {
+            mpack_name: "mpackName1",
+            mpack_version: "1.0.0.0"
+          },
+          currentWizard: false
+        },
+        {
+          MpackInfo: {
+            mpack_name: "mpackName2",
+            mpack_version: "1.0.0.0"
+          },
+          currentWizard: true
+        }
+      ]);
+
       wizardSelectMpacksController.set('selectedMpackVersions', [
         Em.Object.create({
           mpack: {
@@ -1302,6 +1348,18 @@ describe('App.WizardSelectMpacksController', function () {
         }
       ];
 
+      var expectedMpacksToRegister = [
+        {
+          id: "mpackName2-1.0.0.0",
+          name: "mpackName2",
+          displayName: "displayName2",
+          publicUrl: "http://someurl.com/mpack2",
+          downloadUrl: "http://someurl.com/mpack2",
+          version: "1.0.0.0",
+          registryId: 1
+        }
+      ]
+
       sinon.stub(App.router, 'send');
       wizardSelectMpacksController.submit();
       App.router.send.restore();
@@ -1309,6 +1367,7 @@ describe('App.WizardSelectMpacksController', function () {
       expect(wizardSelectMpacksController.get('content.addedServiceGroups')).to.deep.equal(expectedAddedServiceGroups);
       expect(wizardSelectMpacksController.get('content.addedServiceInstances')).to.deep.equal(expectedAddedServiceInstances);
       expect(wizardSelectMpacksController.get('content.selectedMpacks')).to.deep.equal(expectedSelectedMpacks);
+      expect(wizardSelectMpacksController.get('content.mpacksToRegister')).to.deep.equal(expectedMpacksToRegister);
     });
   });
 
diff --git a/ambari-web/test/controllers/wizard_test.js b/ambari-web/test/controllers/wizard_test.js
index e0d577f..779f830 100644
--- a/ambari-web/test/controllers/wizard_test.js
+++ b/ambari-web/test/controllers/wizard_test.js
@@ -437,6 +437,87 @@ describe('App.WizardController', function () {
     });
   });
 
+  describe('#getStepName', function() {
+    var index = 1;
+
+    it('Should return the step name at the given index', function () {
+      var wizardController = App.WizardController.create({
+        steps: [
+          "dog",
+          "cat",
+          "fish"
+        ]
+      });
+
+      var stepName = wizardController.getStepName(index);
+
+      expect(stepName).to.eq("cat");
+    });
+
+    it('Should return "step" + index if there is no steps array for legacy support', function () {
+      var wizardController = App.WizardController.create();
+
+      var stepName = wizardController.getStepName(1);
+
+      expect(stepName).to.eq("step" + index);
+    });
+  });
+
+  describe('#firstStepName', function() {
+    it('Should return the step name at index 0', function () {
+      var wizardController = App.WizardController.create({
+        steps: [
+          "dog",
+          "cat",
+          "fish"
+        ]
+      });
+
+      var stepName = wizardController.get('firstStepName');
+
+      expect(stepName).to.eq("dog");
+    });
+
+    it('Should return "step0" if there is no steps array for legacy support', function () {
+      var wizardController = App.WizardController.create();
+
+      var stepName = wizardController.get('firstStepName');
+
+      expect(stepName).to.eq("step0");
+    });
+  });
+
+  describe('#lastStepName', function() {
+    it('Should return the step name at the last index', function () {
+      var wizardController = App.WizardController.create({
+        steps: [
+          "dog",
+          "cat",
+          "fish"
+        ]
+      });
+
+      var stepName = wizardController.get('lastStepName');
+
+      expect(stepName).to.eq("fish");
+    });
+
+    it('Should return "step" + totalSteps if there is no steps array for legacy support', function () {
+      var totalSteps = 10;
+      var wizardController = App.WizardController.create({ totalSteps: totalSteps });
+      var stepName = wizardController.get('lastStepName');
+
+      expect(stepName).to.eq("step" + totalSteps);
+    });
+
+    it('Should return null if there is no steps array and no totalSteps value', function () {
+      var wizardController = App.WizardController.create();
+      var stepName = wizardController.get('lastStepName');
+
+      expect(stepName).to.be.null;
+    });
+  });
+
   describe('#getStepIndex when steps is not defined', function() {
     it('Should return name when name does not contain an integer', function() {
       var index = c.getStepIndex("cat");
@@ -1572,7 +1653,7 @@ describe('App.WizardController', function () {
     });
   });
 
-  describe('#loadConfigThemes', function () {
+  describe('#loadServiceConfigs', function () {
     beforeEach(function () {
       sinon.stub(wizardController, 'loadConfigThemeForServices').returns({
         always: Em.clb
@@ -1597,15 +1678,15 @@ describe('App.WizardController', function () {
       App.themesMapper.generateAdvancedTabs.restore();
       wizardController.loadConfigThemeForServices.restore();
     });
-    it('Should load config themes', function(done) {
+    it('Should load service configs', function(done) {
       this.stub.returns(true);
-      wizardController.loadConfigThemes().then(function() {
+      wizardController.loadServiceConfigs().then(function() {
         done();
       });
     });
     it('Should load config themes (2)', function(done) {
       this.stub.returns(false);
-      wizardController.loadConfigThemes().then(function() {
+      wizardController.loadServiceConfigs().then(function() {
         done();
       });
     });
@@ -2050,9 +2131,11 @@ describe('App.WizardController', function () {
   describe("#loadServiceGroupsCallback", function() {
     it("should call setDBProperty and populate content.serviceGroups and content.serviceInstances", function () {
       var data = [{
-        service_group_name: 'name',
-        mpack_name: 'mpackName',
-        mpack_version: 'version',
+        ServiceGroupInfo: {
+          service_group_name: 'name',
+          mpack_name: 'mpackName',
+          mpack_version: 'version'
+        },
         services: [{
           ServiceInfo: {
             service_name: 'serviceName',
@@ -2066,11 +2149,12 @@ describe('App.WizardController', function () {
       expect(stub.calledTwice).to.be.true;
       expect(wizardController.get('content.serviceGroups')).to.deep.equal([{
         name: 'name',
-        mpackVersionId: 'mpackNameversion'
+        mpackVersionId: 'mpackName-version'
       }]);
       expect(wizardController.get('content.serviceInstances')).to.deep.equal([{
         name: 'serviceName',
-        serviceGroupName: 'name'
+        serviceGroupName: 'name',
+        serviceName: 'serviceName'
       }]);
 
       wizardController.setDBProperty.restore();
diff --git a/ambari-web/test/mixins/common/configs/config_recommendation_parser_test.js b/ambari-web/test/mixins/common/configs/config_recommendation_parser_test.js
index e128ed5..d950b17 100644
--- a/ambari-web/test/mixins/common/configs/config_recommendation_parser_test.js
+++ b/ambari-web/test/mixins/common/configs/config_recommendation_parser_test.js
@@ -228,7 +228,7 @@ describe('App.ConfigRecommendationParser', function() {
     cases.forEach(function(c) {
       describe('update recommendation', function() {
         beforeEach(function() {
-          sinon.spy(instanceObject, 'applyRecommendation');
+          sinon.stub(instanceObject, 'applyRecommendation');
           sinon.stub(instanceObject, 'getRecommendation').returns(false);
           sinon.stub(instanceObject, 'allowUpdateProperty').returns(c.allowUpdateProperty);
           sinon.stub(instanceObject, 'updateInitialOnRecommendations').returns(c.updateInitialOnRecommendations);
@@ -264,7 +264,7 @@ describe('App.ConfigRecommendationParser', function() {
 
   describe('#_createNewProperty', function() {
     beforeEach(function() {
-      sinon.spy(instanceObject, 'applyRecommendation');
+      sinon.stub(instanceObject, 'applyRecommendation');
       sinon.stub(instanceObject, '_getCoreProperties').returns({
         'value': 'recommendedValue',
         'recommendedValue': 'recommendedValue',
@@ -306,7 +306,7 @@ describe('App.ConfigRecommendationParser', function() {
 
   describe('#_removeConfigByRecommendation', function() {
     beforeEach(function() {
-      sinon.spy(instanceObject, 'applyRecommendation');
+      sinon.stub(instanceObject, 'applyRecommendation');
     });
     afterEach(function() {
       instanceObject.applyRecommendation.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 451cccf..41d59b3 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
@@ -132,10 +132,11 @@ describe('App.HostComponentRecommendationMixin', function() {
             componentName: 'c1',
             hostName: 'h1',
             mpackInstance: 'm1',
-            serviceInstance: 'si1'
+            serviceInstance: 'si1',
+            serviceGroupName: 'sg1',
+            serviceName: 's1'
           })],
-          blueprint: null,
-          mpack_instances: []
+          blueprint: null
         },
         e: {
           data: {
@@ -149,13 +150,12 @@ describe('App.HostComponentRecommendationMixin', function() {
                     components: [
                       {
                         name: 'c1',
-                        mpack_instance: 'm1',
-                        service_instance: 'si1'
+                        mpack_instance: 'sg1',
+                        service_instance: 's1'
                       }
                     ]
                   }
-                ],
-                mpack_instances: []
+                ]
               },
               blueprint_cluster_binding: {
                 host_groups: [
@@ -171,17 +171,14 @@ describe('App.HostComponentRecommendationMixin', function() {
         options: {
           hosts: ['h1'],
           components: [Em.Object.create({componentName: 'c1', hostName: 'h1'})],
-          blueprint: { blueprint: {} },
-          mpack_instances: []
+          blueprint: { blueprint: {} }
         },
         e: {
           data: {
             recommend: 'host_groups',
             hosts: ['h1'],
             recommendations: {
-              blueprint: {
-                mpack_instances: []
-              }
+              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 236210c..b215708 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
@@ -133,7 +133,9 @@ describe('App.HostComponentValidationMixin', function() {
               componentName: 'c1',
               hostName: 'h1',
               mpackInstance: 'm1',
-              serviceInstance: 'si1'
+              serviceInstance: 'si1',
+              serviceGroupName: 'sg1',
+              serviceName: 's1'
             })
           ],
           blueprint: null,
@@ -149,8 +151,8 @@ describe('App.HostComponentValidationMixin', function() {
                   {
                     name: 'host-group-1', components: [{
                       name: 'c1',
-                      mpack_instance: 'm1',
-                      service_instance: 'si1' 
+                      mpack_instance: 'sg1',
+                      service_instance: 's1' 
                     }]
                   }
                 ],