You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2017/10/27 20:47:26 UTC

[2/2] ambari git commit: AMBARI-22311 - Initial implementation of mpack install wizard (Jason Golieb via jonathanhurley)

AMBARI-22311 - Initial implementation of mpack install wizard (Jason Golieb via jonathanhurley)


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

Branch: refs/heads/branch-feature-AMBARI-14714-ui
Commit: 153477463207d78182dca46a9048f1ac97e611fd
Parents: 1578226b
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Fri Oct 27 16:47:10 2017 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Fri Oct 27 16:47:10 2017 -0400

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   1 +
 ambari-web/app/controllers.js                   |   1 +
 ambari-web/app/controllers/installer.js         | 235 +++++++++++--------
 ambari-web/app/controllers/wizard.js            |  30 ++-
 .../wizard/configureDownload_controller.js      | 102 +++-----
 .../wizard/downloadProducts_controller.js       | 125 +++++++---
 .../wizard/selectMpacks_controller.js           | 203 ++++++++++++++++
 .../app/controllers/wizard/step7_controller.js  |   9 +-
 .../app/controllers/wizard/step8_controller.js  |  99 ++++----
 ambari-web/app/mappers.js                       |   3 +-
 ambari-web/app/mappers/mpack_service_mapper.js  | 114 +++++++++
 ambari-web/app/mappers/stack_mapper.js          |  11 +-
 ambari-web/app/messages.js                      |   6 +
 .../app/mixins/wizard/wizard_menu_view.js       |  10 +-
 ambari-web/app/models/stack.js                  |   2 -
 ambari-web/app/router.js                        |   6 +-
 ambari-web/app/routes/installer.js              | 200 +++++++++-------
 ambari-web/app/templates/installer.hbs          |  17 +-
 .../app/templates/wizard/configureDownload.hbs  |  63 +++--
 .../app/templates/wizard/downloadProducts.hbs   |   4 +-
 .../app/templates/wizard/selectMpacks.hbs       |  70 ++++++
 .../app/templates/wizard/selectMpacks/mpack.hbs |  31 +++
 .../selectMpacks/selectedMpackVersion.hbs       |  32 +++
 ambari-web/app/utils/ajax/ajax.js               |  66 ++++++
 ambari-web/app/views.js                         |   3 +
 .../app/views/wizard/configureDownload_view.js  |   8 +-
 .../app/views/wizard/selectMpacks/mpack_view.js |  31 +++
 .../selectMpacks/selectedMpackVersion_view.js   |  27 +++
 .../app/views/wizard/selectMpacks_view.js       |  28 +++
 ambari-web/test/controllers/installer_test.js   |  71 ------
 .../controllers/wizard/selectMpacks_test.js     |  42 ++++
 .../test/controllers/wizard/step8_test.js       |  90 +++++--
 .../test/mixins/routers/redirections_test.js    |   8 +-
 ambari-web/test/views/installer_test.js         |  16 +-
 34 files changed, 1252 insertions(+), 512 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 7c636d4..b60e17a 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -135,6 +135,7 @@ var files = [
   'test/controllers/login_controller_test',
   'test/controllers/experimental_test',
   'test/controllers/wizard_test',
+  'test/controllers/wizard/selectMpacks_test',
   'test/controllers/wizard/step0_test',
   'test/controllers/wizard/step1_test',
   'test/controllers/wizard/step2_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js
index 82b0f66..5af0937 100644
--- a/ambari-web/app/controllers.js
+++ b/ambari-web/app/controllers.js
@@ -160,6 +160,7 @@ require('controllers/wizard/step7/pre_install_checks_controller');
 require('controllers/wizard/step8_controller');
 require('controllers/wizard/step9_controller');
 require('controllers/wizard/step10_controller');
+require('controllers/wizard/selectMpacks_controller');
 require('controllers/global/cluster_controller');
 require('controllers/global/update_controller');
 require('controllers/global/configuration_controller');

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js
index d4f656b..5dd0941 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -42,9 +42,10 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     "step2",
     "step3",
     "configureDownload",
+	  "selectMpacks",
     "downloadProducts",
-    "step1",
-    "step4",
+    //"step1",
+    //"step4",
     "step5",
     "step6",
     "step7",
@@ -78,7 +79,10 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
      * (uses for host groups validation and to load recommended configs)
      */
     recommendationsHostGroups: null,
-    controllerName: 'installerController'
+    controllerName: 'installerController',
+    mpacks: [],
+    mpackVersions: [],
+    mpackServices: []
   }),
 
   /**
@@ -109,7 +113,11 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     'recommendationsConfigs',
     'componentsFromConfigs',
     'operatingSystems',
-    'repositories'
+    'repositories',
+    'selectedMpacks',
+    'selectedServices',
+    'selectedStack',
+    'downloadConfig'
   ],
 
   init: function () {
@@ -177,6 +185,74 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
   },
 
   /**
+   * Load data for stacks from selected mpacks. This just tells the server to populate the version_definitions endpoint for the mpack that was registered.
+   *
+   * @param  {string} stackName
+   * @param  {string} stackVersion
+   * @param  {string} serviceName
+   */
+  createMpackStackVersion: function (stackName, stackVersion) {
+    return App.ajax.send({
+      name: 'mpack.create_version_definition',
+      sender: this,
+      data: {
+        name: stackName,
+        version: stackVersion
+      }
+    })
+  },
+
+  /**
+   * Loads stack version data (including supported OSes and repos) from the version_definitions endpoint
+   */
+  getMpackStackVersions: function () {
+    return App.ajax.send({
+      name: 'mpack.get_version_definitions',
+      sender: this
+    })
+  },
+
+  loadMpackStackInfoSuccess: function (versionDefinition) {
+    App.stackMapper.map(versionDefinition);
+  },
+
+  //TODO: report error in UI
+  loadMpackStackInfoError: function(request, status, error) {
+    console.log(`Failed to load stack info. ${status} - ${error}`);
+  },
+
+  /**
+   * 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
+   */
+  loadMpackServiceInfo: function (stackName, stackVersion, serviceName) {
+    return App.ajax.send({
+      name: 'wizard.mpack_service_components',
+      sender: this,
+      data: {
+        stackName: stackName || "HDP", //TODO: mpacks - Remove default when this value is provided API
+        stackVersion: stackVersion,
+        serviceName: serviceName
+      },
+      success: 'loadMpackServiceInfoSuccess',
+      error: 'loadMpackServiceInfoError'
+    })
+  },
+
+  loadMpackServiceInfoSuccess: function (serviceInfo) {
+    serviceInfo.StackServices.is_selected = true;
+    App.MpackServiceMapper.map(serviceInfo);
+  },
+
+  //TODO: report error in UI
+  loadMpackServiceInfoError: function(request, status, error) {
+    console.log(`Failed to load mpack service info. ${status} - ${error}`);
+  },
+
+  /**
    * total set of hosts registered to cluster, analog of App.Host model,
    * used in Installer wizard until hosts are installed
    */
@@ -972,87 +1048,55 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
           return dfd.promise();
         }
       },
-      {
-        type: 'async',
-        callback: function (stacksLoaded) {
-          var dfd = $.Deferred();
-
-          if (!stacksLoaded) {
-            $.when.apply(this, this.loadStacksVersions()).done(function () {
-              dfd.resolve(true);
-            });
-          } else {
-            dfd.resolve(stacksLoaded);
-          }
-
-          return dfd.promise();
-        }
-      }
-    ],
-    'downloadProducts': [
-      {
-        type: 'async',
-        callback: function () {
-          var dfd = $.Deferred();
-
-          this.loadStacks().done(function(stacksLoaded) {
-            App.router.get('clusterController').loadAmbariProperties().always(function() {
-              dfd.resolve(stacksLoaded);
-            });
-          });
-
-          return dfd.promise();
-        }
-      },
-      {
-        type: 'async',
-        callback: function (stacksLoaded) {
-          var dfd = $.Deferred();
-
-          if (!stacksLoaded) {
-            $.when.apply(this, this.loadStacksVersions()).done(function () {
-              dfd.resolve(true);
-            });
-          } else {
-            dfd.resolve(stacksLoaded);
-          }
-
-          return dfd.promise();
-        }
-      }
-    ],
-    'step1': [
-      {
-        type: 'async',
-        callback: function () {
-          var dfd = $.Deferred();
-
-          this.loadStacks().done(function(stacksLoaded) {
-            App.router.get('clusterController').loadAmbariProperties().always(function() {
-              dfd.resolve(stacksLoaded);
-            });
-          });
-
-          return dfd.promise();
-        }
-      },
-      {
-        type: 'async',
-        callback: function (stacksLoaded) {
-          var dfd = $.Deferred();
-
-          if (!stacksLoaded) {
-            $.when.apply(this, this.loadStacksVersions()).done(function () {
-              dfd.resolve(true);
-            });
-          } else {
-            dfd.resolve(stacksLoaded);
-          }
-
-          return dfd.promise();
-        }
-      }
+      // {
+      //   type: 'async',
+      //   callback: function (stacksLoaded) {
+      //     var dfd = $.Deferred();
+      //
+      //     if (!stacksLoaded) {
+      //       $.when.apply(this, this.loadStacksVersions()).done(function () {
+      //         dfd.resolve(true);
+      //       });
+      //     } else {
+      //       dfd.resolve(stacksLoaded);
+      //     }
+      //
+      //     return dfd.promise();
+      //   }
+      // }
     ],
+    // 'step1': [
+    //   {
+    //     type: 'async',
+    //     callback: function () {
+    //       var dfd = $.Deferred();
+    //
+    //       this.loadStacks().done(function(stacksLoaded) {
+    //         App.router.get('clusterController').loadAmbariProperties().always(function() {
+    //           dfd.resolve(stacksLoaded);
+    //         });
+    //       });
+    //
+    //       return dfd.promise();
+    //     }
+    //   },
+    //   {
+    //     type: 'async',
+    //     callback: function (stacksLoaded) {
+    //       var dfd = $.Deferred();
+    //
+    //       if (!stacksLoaded) {
+    //         $.when.apply(this, this.loadStacksVersions()).done(function () {
+    //           dfd.resolve(true);
+    //         });
+    //       } else {
+    //         dfd.resolve(stacksLoaded);
+    //       }
+    //
+    //       return dfd.promise();
+    //     }
+    //   }
+    // ],
     'step3': [
       {
         type: 'sync',
@@ -1061,20 +1105,19 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         }
       }
     ],
-
-    'step4': [
-      {
-        type: 'async',
-        callback: function () {
-          return this.loadServices();
-        }
-      }
-    ],
+    // 'step4': [
+    //   {
+    //     type: 'async',
+    //     callback: function () {
+    //       return this.loadServices();
+    //     }
+    //   }
+    // ],
     'step5': [
       {
         type: 'sync',
         callback: function () {
-          this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), 6);
+          this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), 6); //TODO: something needs to be changed here
           this.loadMasterComponentHosts();
           this.loadConfirmedHosts();
           this.loadComponentsFromConfigs();
@@ -1180,6 +1223,10 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     this.gotoStep('downloadProducts');
   },
 
+  gotoSelectMpacks: function () {
+    this.gotoStep('selectMpacks');
+  },
+
   isStep0: function () {
     return this.get('currentStep') == this.getStepIndex('step0');
   }.property('currentStep'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/wizard.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index 9db923e..540cc63 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -323,7 +323,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
       App.ModalPopup.show({
         header: Em.I18n.t('installer.navigation.warning.header'),
         onPrimary: function () {
-          App.router.send('gotoStep' + step);
+          App.router.send('goto' + stepName.capitalize());
           this.hide();
         },
         body: "If you proceed to go back to Step " + step + ", you will lose any changes you have made beyond this step"
@@ -379,14 +379,6 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     this.gotoStep(10);
   },
 
-  gotoConfigureDownload: function () {
-    this.gotoStep("configureDownload");
-  },
-
-  gotoDownloadProducts: function () {
-    this.gotoStep("downloadProducts");
-  },
-
   /**
    * Initialize host status info for step9
    */
@@ -629,6 +621,12 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     this.set('content.' + name, result);
   },
 
+
+  /**
+   * Save value from content to database. Converts Ember objects to plain objects first.
+   *
+   * @param  {type} name
+   */
   save: function (name) {
     var convertedValue = this.toJSInstance(this.get('content.' + name));
     this.setDBProperty(name, convertedValue);
@@ -1572,6 +1570,20 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     this.setDBProperty('kerberosDescriptorConfigs', kerberosDescriptorConfigs);
     this.set('kerberosDescriptorConfigs', kerberosDescriptorConfigs);
   },
+
+  getStack: function (name, version) {
+    const stacks = App.Stack.find();
+
+    for (let i = 0, length = stacks.get('length'); i < length; i++) {
+      const stack = stacks.objectAt(i);
+      if (stack.get('stackName') === name && stack.get('stackVersion') === version) {
+        return stack;
+      }
+    }
+
+    return null;
+  },
+
   /**
    * reset stored wizard data and reload App
    * @param {App.WizardController} controller - wizard controller

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/wizard/configureDownload_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/configureDownload_controller.js b/ambari-web/app/controllers/wizard/configureDownload_controller.js
index a80cf25..f3a8b1b 100644
--- a/ambari-web/app/controllers/wizard/configureDownload_controller.js
+++ b/ambari-web/app/controllers/wizard/configureDownload_controller.js
@@ -17,38 +17,11 @@
  */
 
 var App = require('app');
-var arrayUtils = require('utils/array_utils');
-
-/**
- * @typedef {Em.Object} StackType
- * @property {string} stackName
- * @property {App.Stack[]} stacks
- * @property {boolean} isSelected
- */
-
-/**
- * @type {Em.Object}
- */
-var StackType = Em.Object.extend({
-  stackName: '',
-  stacks: [],
-  isSelected: Em.computed.someBy('stacks', 'isSelected', true)
-});
 
 App.WizardConfigureDownloadController = Em.Controller.extend({
 
   name: 'wizardConfigureDownloadController',
 
-  /**
-   * @type {App.Stack}
-   */
-  selectedStack: Em.computed.findBy('content.stacks', 'isSelected', true),
-
-  /**
-   * @type {App.ServiceSimple[]}
-   */
-  servicesForSelectedStack: Em.computed.filterBy('selectedStack.stackServices', 'isHidden', false),
-
   optionsToSelect: {
     'usePublicRepo': {
       index: 0,
@@ -75,55 +48,50 @@ App.WizardConfigureDownloadController = Em.Controller.extend({
     }
   },
 
+  loadStep: function () {
+    if (!this.get('content.downloadConfig')) {
+      let downloadConfig = this.get('wizardController').getDBProperty('downloadConfig');
+
+      if (!downloadConfig) {
+        downloadConfig = {
+          useRedhatSatellite: false,
+          usePublicRepo: true
+        };
+      }
+
+      this.set('content.downloadConfig', downloadConfig);
+    }
+  },
+
   /**
    * Restore base urls for selected stack when user select to use public repository
    */
   usePublicRepo: function () {
-    var selectedStack = this.get('selectedStack');
-    if (selectedStack) {
-      selectedStack.setProperties({
-        useRedhatSatellite: false,
-        usePublicRepo: true,
-        useLocalRepo: false
-      });
-      selectedStack.restoreReposBaseUrls();
-    }
+    this.set('content.downloadConfig', {
+      useRedhatSatellite: false,
+      usePublicRepo: true
+    });
   },
 
-  /**
-   * Clean base urls for selected stack when user select to use local repository
-   */
   useLocalRepo: function () {
-    var selectedStack = this.get('selectedStack');
-    if (selectedStack) {
-      selectedStack.setProperties({
-        usePublicRepo: false,
-        useLocalRepo: true
-      });
-      selectedStack.cleanReposBaseUrls();
-    }
+    this.set('content.downloadConfig', {
+      useRedhatSatellite: false,
+      usePublicRepo: false
+    });
   },
 
-    /**
-     * List of stacks grouped by <code>stackNameVersion</code>
-     *
-     * @type {StackType[]}
-     */
-    availableStackTypes: function () {
-      var stacks = this.get('content.stacks');
-      return stacks ? stacks.mapProperty('stackNameVersion').uniq().sort().reverse().map(function (stackName) {
-        return StackType.create({
-          stackName: stackName,
-          stacks: stacks.filterProperty('stackNameVersion', stackName).sort(arrayUtils.sortByIdAsVersion).reverse()
-        })
-      }) : [];
-    }.property('content.stacks.@each.stackNameVersion'),
-
-    /**
-     * @type {StackType}
-     */
-    selectedStackType: Em.computed.findBy('availableStackTypes', 'isSelected', true),
+  /**
+   * Onclick handler for <code>Next</code> button.
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
+   * @method submit
+   */
+  submit: function () {
+    if (App.get('router.nextBtnClickInProgress')) {
+      return;
+    }
 
-    isLoadingComplete: Em.computed.equal('wizardController.loadStacksRequestsCounter', 0)
+    this.get('wizardController').setDBProperty('downloadConfig', this.get('content.downloadConfig'));
 
+    App.router.send('next');
+  }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/wizard/downloadProducts_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/downloadProducts_controller.js b/ambari-web/app/controllers/wizard/downloadProducts_controller.js
index 7e95acf..165debb 100644
--- a/ambari-web/app/controllers/wizard/downloadProducts_controller.js
+++ b/ambari-web/app/controllers/wizard/downloadProducts_controller.js
@@ -16,49 +16,26 @@
  */
 
 var App = require('app');
-var arrayUtils = require('utils/array_utils');
 
 App.WizardDownloadProductsController = Em.Controller.extend({
 
   name: 'wizardDownloadProductsController',
 
-  selectedMpacks:
-    [
-      {
-        "mpackName" : "HDP",
-        "displayName": "Hortonworks Data Platform Core",
-        "mpackVersion" : "3.0.0",
-        "registryId"  : "1"
-      },
-      {
-        "mpackName" : "EDW",
-        "displayName": "Data Warehousing",
-        "mpackVersion": "1.0.0",
-        "registryId": "1"
-      },
-      {
-        "mpackName" : "HDS",
-        "displayName": "Data Science and Machine Learning",
-        "mpackVersion" : "3.0.0.0",
-        "registryId" : "1"
-      }
-    ],
-
   mpacks: [],
 
   addMpacks: function () {
-    var self = this;
-    this.get('selectedMpacks').forEach( function (mpack) {
-      self.get('mpacks').pushObject(Em.Object.create({
-        name: mpack.mpackName,
-        displayName: mpack.displayName,
-        version: mpack.mpackVersion,
-        registryId:mpack.registryId,
+    const selectedMpacks = this.get('content.selectedMpacks') || this.get('wizardController').getDBProperty('selectedMpacks');
+
+    selectedMpacks.forEach(mpack => {
+      this.get('mpacks').pushObject(Em.Object.create({
+        name: mpack.name,
+        displayName: mpack.displayName || mpack.name, //TODO: remove default when displayName is available
+        url: mpack.url,
         inProgress: true,
         failed: false,
         success: false
       }));
-    });
+    }, this);
   },
 
   registerMpacks: function () {
@@ -72,12 +49,11 @@ App.WizardDownloadProductsController = Em.Controller.extend({
   downloadMpack: function (mpack) {
     console.log("downloading mpacks");
     App.ajax.send({
-      name:'mpack.download',
+      name: 'mpack.download_by_url',
       sender: this,
       data: {
         name: mpack.name,
-        version: mpack.version,
-        registry: mpack.registryId
+        url: mpack.url
       },
       success: 'downloadMpackSuccess',
       error: 'downloadMpackError',
@@ -105,6 +81,83 @@ App.WizardDownloadProductsController = Em.Controller.extend({
     mpack.set('inProgress', true);
     mpack.set('failed', false);
     this.downloadMpack(mpack);
-  }
+  },
+
+  getRegisteredMpacks: function () {
+    return App.ajax.send({
+      name: 'mpack.get_registered_mpacks',
+      sender: this
+    });
+  },
 
-});
\ No newline at end of file
+  isSubmitDisabled: function () {
+    const mpacks = this.get('mpacks');
+    return mpacks.filterProperty('success', false).length > 0 || App.get('router.btnClickInProgress');
+  }.property('mpacks.@each.success', 'App.router.btnClickInProgress'),
+
+  /**
+   * Onclick handler for <code>Next</code> button.
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
+   * @method submit
+   */
+  submit: function () {
+    const self = this;
+
+    if (App.get('router.nextBtnClickInProgress')) {
+      return;
+    }
+
+    if (!this.get('isSubmitDisabled')) {
+      //TODO: mpacks
+      //get info about stacks from version definitions and save to Stack model
+      this.getRegisteredMpacks().then(mpacks => {
+        const stackVersionsRegistered = mpacks.items.map(mpack => this.get('wizardController').createMpackStackVersion
+          (
+            mpack.MpackInfo.stack_name || "HDP", //TODO: mpacks - remove fallback when stack info is included in API response
+            mpack.MpackInfo.stack_version || "3.0.0" //TODO: mpacks - remove fallback when stack info is included in API response
+          )
+        );
+
+        //var versionData = installerController.getSelectedRepoVersionData(); //This would be used to post a VDF xml for a local repo (I think), but do we still need to do this when we will just be using mpacks?
+        $.when(...stackVersionsRegistered).always(() => { //this uses always() because the api call made by createMpackStackVersion will return a 500 error
+                                                          //if the stack version has already been registered, but we want to proceed anyway
+          self.get('wizardController').getMpackStackVersions().then(data => {
+            data.items.forEach(versionDefinition => App.stackMapper.map(versionDefinition))
+
+            //get info about services from specific stack versions and save to StackService model
+            const selectedServices = self.get('content.selectedServices') || self.get('wizardController').getDBProperty('selectedServices');
+            const servicePromises = selectedServices.map(service => self.get('wizardController').loadMpackServiceInfo(service.stackName, service.stackVersion, service.name));
+
+            $.when(...servicePromises).then(() => {
+              const serviceInfo = App.StackService.find();
+              self.set('content.services', serviceInfo);
+
+              const clients = [];
+              serviceInfo.forEach(service => {
+                const client = service.get('serviceComponents').filterProperty('isClient', true);
+                client.forEach(clientComponent => {
+                  clients.pushObject({
+                    component_name: clientComponent.get('componentName'),
+                    display_name: clientComponent.get('displayName'),
+                    isInstalled: false
+                  });
+                });
+              });
+              self.set('content.clients', clients);
+              self.get('wizardController').setDBProperty('clientInfo', clients);
+
+              // - for now, pull the stack from the single mpack that we can install
+              // - when we can support multiple mpacks, make this an array of selectedStacks (or just use the selectedServices array?) and add the repo data to it
+              const selectedService = selectedServices[0];
+              const selectedStack = self.get('wizardController').getStack(selectedService.stackName, selectedService.stackVersion);
+              self.set('content.selectedStack', selectedStack);
+              self.get('wizardController').setDBProperty('selectedStack', selectedStack);
+
+              App.router.send('next');
+            });
+          });
+        });
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/wizard/selectMpacks_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/selectMpacks_controller.js b/ambari-web/app/controllers/wizard/selectMpacks_controller.js
new file mode 100644
index 0000000..9ab5a12
--- /dev/null
+++ b/ambari-web/app/controllers/wizard/selectMpacks_controller.js
@@ -0,0 +1,203 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.WizardSelectMpacksController = Em.Controller.extend({
+
+  name: 'wizardSelectMpacksController',
+
+  //mpacks: Em.computed.alias('content.mpacks'),
+
+  getMpacks: function () {
+    App.ajax.send({
+      name: 'registry.mpacks.versions',
+      showLoadingPopup: true,
+      sender: this,
+      success: 'getMpacksSucceeded',
+      error: 'getMpacksFailed'
+    });
+  },
+
+  getMpacksSucceeded: function (data) {
+    const mpacks = data.items.reduce(
+      (mpacks, registry) => mpacks.concat(
+        registry.mpacks.map(mpack => {
+          return Em.Object.create({
+            name: mpack.RegistryMpackInfo.mpack_name,
+            description: mpack.RegistryMpackInfo.mpack_description,
+            logoUrl: mpack.RegistryMpackInfo.mpack_logo_url,
+            versions: mpack.versions ? mpack.versions.map(version => {
+              return Em.Object.create({
+                selected: false,
+                id: mpack.RegistryMpackInfo.mpack_name + version.RegistryMpackVersionInfo.mpack_version,
+                version: version.RegistryMpackVersionInfo.mpack_version,
+                docUrl: version.RegistryMpackVersionInfo.mpack_dock_url,
+                mpackUrl: version.RegistryMpackVersionInfo.mpack_url,
+                stackName: version.RegistryMpackVersionInfo.stack_name || "HDP", //TODO: remove default when stack_name is available
+                stackVersion: version.RegistryMpackVersionInfo.stack_version || "3.0.0", //TODO: remove default when stack_version is available
+                services: version.RegistryMpackVersionInfo.services ? version.RegistryMpackVersionInfo.services.map(service => {
+                  return Em.Object.create({
+                    selected: false,
+                    id: mpack.RegistryMpackInfo.mpack_name + version.RegistryMpackVersionInfo.mpack_version + service.name,
+                    name: service.name,
+                    version: service.version
+                  })
+                }) : []
+              })
+            }) : []
+          })
+        })
+      ),
+    []);
+
+    const mpackVersions = mpacks.reduce(
+      (versions, mpack) => versions.concat(
+        mpack.versions.map(version => {
+          version.mpack = mpack;
+          return version;
+        })
+      ),
+    []);
+
+    const mpackServices = mpackVersions.reduce(
+      (services, mpackVersion) => services.concat(
+        mpackVersion.services.map(service => {
+          service.mpackVersion = mpackVersion;
+          return service;
+        })
+      ),
+    []);
+
+    this.set('content.mpacks', mpacks);
+    this.set('content.mpackVersions', mpackVersions);
+    this.set('content.mpackServices', mpackServices);
+  },
+
+  getMpacksFailed: function () {
+    this.set('content.mpacks', []);
+  },
+
+  loadStep: function () {
+    const mpacks = this.get('content.mpacks');
+    const mpackVersions = this.get('content.mpackVersions');
+    const mpackServices = this.get('content.mpackServices');
+
+    if (!mpacks || mpacks.length === 0 || !mpackVersions || mpackVersions.length === 0 || !mpackServices || mpackServices.length === 0) {
+      //this.showLoadingSpinner();
+      this.getMpacks();
+    }
+  },
+
+  isSubmitDisabled: function () {
+    const mpackServices = this.get('content.mpackServices');
+    return mpackServices.filterProperty('selected', true).length === 0 || App.get('router.btnClickInProgress');
+  }.property('content.mpackServices.@each.selected', 'App.router.btnClickInProgress'),
+
+  loadSelectionFailed: function () {
+    App.showAlertPopup(
+      Em.I18n.t('common.error'), //header
+      Em.I18n.t('installer.selectMpacks.loadSelectionFailed') //body
+    );
+  },
+
+  /**
+   * Adds service to selection.
+   *
+   * @param  {string} serviceName
+   */
+  selectService: function (event) {
+    const serviceId = event.context;
+    const mpackServices = this.get('content.mpackServices');
+    const byServiceId = service => service.id === serviceId;
+
+    const service = mpackServices.find(byServiceId);
+    service.set('selected', true);
+    service.set('mpackVersion.selected', true);
+  },
+
+  removeService: function (event) {
+    const serviceId = event.context;
+    const mpackServices = this.get('content.mpackServices');
+    const byServiceId = service => service.id === serviceId;
+
+    const service = mpackServices.find(byServiceId);
+    service.set('selected', false);
+
+    service.set('mpackVersion.selected', service.get('mpackVersion.services').some(s => s.get('selected') === true));
+  },
+
+  selectedServices: function () {
+    const mpackServices = this.get('content.mpackServices');
+    return mpackServices ? mpackServices.filter(s => s.get('selected') === true) : [];
+  }.property('content.mpackServices.@each.selected'),
+
+  selectedMpackVersions: function () {
+    const versions = this.get('content.mpackVersions');
+    return versions ? versions.filter(v => v.get('selected') === true) : [];
+  }.property('content.mpackVersions.@each.selected', 'selectedServices'),
+
+  hasSelectedMpackVersions: function () {
+    const versions = this.get('content.mpackVersions');
+    return versions ? versions.some(v => v.get('selected') === true) : false;
+  }.property('content.mpackVersions.@each.selected', 'selectedServices'),
+
+  /**
+   * Onclick handler for <code>Next</code> button.
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
+   * @method submit
+   */
+  submit: function () {
+    const self = this;
+
+    if(App.get('router.nextBtnClickInProgress')) {
+      return;
+    }
+
+    if (!this.get('isSubmitDisabled')) {
+      const selectedServices = this.get('selectedServices').map(service =>
+        ({
+          name: service.name,
+          mpackName: service.mpackVersion.name,
+          mpackVersion: service.mpackVersion.version,
+          stackName: service.mpackVersion.stackName,
+          stackVersion: service.mpackVersion.stackVersion
+        })
+      );
+      self.set('content.selectedServices', selectedServices);
+      self.get('wizardController').setDBProperty('selectedServices', selectedServices);
+
+      const selectedServiceNames = selectedServices.map(service => service.name);
+      self.set('content.selectedServiceNames', selectedServiceNames);
+      self.get('wizardController').setDBProperty('selectedServiceNames', selectedServiceNames);
+
+      const selectedMpacks = self.get('selectedMpackVersions').map(mpackVersion =>
+        ({
+          name: mpackVersion.mpack.name,
+          displayName: mpackVersion.mpack.displayName,
+          url: mpackVersion.mpackUrl,
+          version: mpackVersion.version
+        })
+      );
+      self.set('content.selectedMpacks', selectedMpacks);
+      self.get('wizardController').setDBProperty('selectedMpacks', selectedMpacks);
+
+      App.router.send('next');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/wizard/step7_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js
index 3b71305..3def063 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -845,11 +845,12 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
     var localDB = {
       hosts: this.get('wizardController.content.hosts'),
       masterComponentHosts: this.get('wizardController.content.masterComponentHosts'),
-      slaveComponentHosts: this.get('wizardController.content.slaveComponentHosts'),
-      selectedStack: {}
+      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') &&
@@ -861,9 +862,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
     } else {
       selectedRepoVersion = Em.getWithDefault(App.Stack.find().findProperty('isSelected', true) || {}, 'repositoryVersion', false);
     }
-    if (selectedRepoVersion) {
-      localDB.selectedStack = selectedRepoVersion;
-    }
+
     var configsByService = {}, dependencies = this.get('configDependencies');
 
     configs.forEach(function (_config) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/controllers/wizard/step8_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step8_controller.js b/ambari-web/app/controllers/wizard/step8_controller.js
index 2a24925..0c72295 100644
--- a/ambari-web/app/controllers/wizard/step8_controller.js
+++ b/ambari-web/app/controllers/wizard/step8_controller.js
@@ -137,19 +137,34 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
    */
   configGroups: [],
 
-  /**
-   * List of selected but not installed services
-   * @type {Object[]}
-   */
   selectedServices: function () {
-    return this.get('content.services').filterProperty('isSelected', true).filterProperty('isInstalled', false);
-  }.property('content.services.@each.isSelected','content.services.@each.isInstalled').cacheable(),
+    const services = App.StackService.find().map(service => service);
+    return services;
+  }.property(),
 
-  /**
-   * List of installed services
-   * @type {Object[]}
-   */
-  installedServices: Em.computed.filterBy('content.services', 'isInstalled', true),
+  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() {
+    let selectedStack = this.get('content.selectedStack');
+
+    if (!selectedStack) {
+      const stack = this.get('wizardController').getDBProperty('selectedStack');
+      selectedStack = this.get('wizardController').getStack(stack.stack_name, stack.stack_version);
+    }
+
+    return selectedStack;
+  },
+
+  installedServices: function() {
+    const services = App.StackService.find().filter(service => service.get('isInstalled') === true);
+    return services;
+  }.property(),
 
   /**
    * Current cluster name
@@ -301,7 +316,8 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
       }
     } else {
       // from install wizard
-      var selectedStack = App.Stack.find().findProperty('isSelected');
+      var downloadConfig = this.get('downloadConfig');
+      var selectedStack = this.getSelectedStack();
       var allRepos = [];
       if (selectedStack && selectedStack.get('operatingSystems')) {
         selectedStack.get('operatingSystems').forEach(function (os) {
@@ -317,7 +333,7 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
         }, this);
       }
       allRepos.set('display_name', Em.I18n.t("installer.step8.repoInfo.displayName"));
-      this.get('clusterInfo').set('useRedhatSatellite', selectedStack.get('useRedhatSatellite'));
+      this.get('clusterInfo').set('useRedhatSatellite', downloadConfig.useRedhatSatellite);
       this.get('clusterInfo').set('repoInfo', allRepos);
     }
   },
@@ -635,7 +651,7 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
         self.set('isBackBtnDisabled', false);
         wizardController.setStepsEnable();
         if (self.get('isAddService')) {
-          wizardController.setSkipSlavesStep(wizardController.getDBProperty('selectedServiceNames'), 3);
+          wizardController.setSkipSlavesStep(wizardController.getDBProperty('selectedServiceNames'), 3); //TODO: something
         }
       });
     } else {
@@ -687,14 +703,16 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
     // delete any existing clusters to start from a clean slate
     // before creating a new cluster in install wizard
     // TODO: modify for multi-cluster support
-    this.getExistingClusterNames().complete(function () {
-      var clusterNames = self.get('clusterNames');
-      if (self.get('isInstaller') && !App.get('testMode') && clusterNames.length) {
-        self.deleteClusters(clusterNames);
-      } else {
-        self.getExistingVersions();
-      }
-    });
+    // this.getExistingClusterNames().complete(function () {
+    //   var clusterNames = self.get('clusterNames');
+    //   if (self.get('isInstaller') && !App.get('testMode') && clusterNames.length) {
+    //     self.deleteClusters(clusterNames);
+    //   } else {
+    //     self.getExistingVersions();
+    //   }
+    // });
+
+    this.startDeploy();
   },
 
   /**
@@ -902,28 +920,17 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
    * To Start deploy process
    * @method startDeploy
    */
+   //TODO: mpacks
   startDeploy: function () {
+    const self = this;
+
     if (!this.get('isInstaller')) {
       this._startDeploy();
     } else {
-      var installerController = App.router.get('installerController');
-      var versionData = installerController.getSelectedRepoVersionData();
-      if (versionData) {
-        var self = this;
-        installerController.postVersionDefinitionFileStep8(versionData.isXMLdata, versionData.data).done(function (versionInfo) {
-          if (versionInfo.id && versionInfo.stackName && versionInfo.stackVersion) {
-            var selectedStack = App.Stack.find().findProperty('isSelected', true);
-            if (selectedStack) {
-              selectedStack.set('versionInfoId', versionInfo.id);
-            }
-            installerController.updateRepoOSInfo(versionInfo, selectedStack).done(function() {
-              self._startDeploy();
-            });
-          }
-        });
-      } else {
-        this._startDeploy();
-      }
+      const selectedStack = this.getSelectedStack();
+      this.get('wizardController').updateRepoOSInfo({ id: selectedStack.get('id'), stackName: selectedStack.get('stackName'), stackVersion: selectedStack.get('stackVersion') }, selectedStack).done(function() {
+        self._startDeploy();
+      });
     }
   },
 
@@ -975,13 +982,14 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
    * Queued request
    * @method createCluster
    */
+   //TODO: mpacks
   createCluster: function () {
     if (!this.get('isInstaller')) return;
-    var stackVersion = this.get('content.installOptions.localRepo') ? App.currentStackVersion.replace(/(-\d+(\.\d)*)/ig, "Local$&") : App.currentStackVersion;
+    const selectedStack = this.getSelectedStack()
     this.addRequestToAjaxQueue({
       name: 'wizard.step8.create_cluster',
       data: {
-        data: JSON.stringify({ "Clusters": {"version": stackVersion}})
+        data: JSON.stringify({ "Clusters": {"version": selectedStack.get('stackNameVersion')}})
       },
       success: 'createClusterSuccess'
     });
@@ -1028,13 +1036,14 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
    * @returns {Object[]}
    * @method createSelectedServicesData
    */
+   //TODO: mpacks
   createSelectedServicesData: function () {
     var selectedStack;
     if (this.get('isInstaller')) {
-      selectedStack = App.Stack.find().findProperty('isSelected', true);
+      selectedStack = this.getSelectedStack();
     }
     return this.get('selectedServices').map(service => selectedStack ?
-      {"ServiceInfo": { "service_name": service.get('serviceName'), "service_type": service.get('serviceName'), "service_group_name": App.get('defaultServiceGroupName'), "desired_repository_version_id": selectedStack.get('versionInfoId') }} :
+      {"ServiceInfo": { "service_name": service.get('serviceName'), "service_type": service.get('serviceName'), "service_group_name": App.get('defaultServiceGroupName'), "desired_repository_version_id": selectedStack.get('id') }} :
       {"ServiceInfo": { "service_name": service.get('serviceName'), "service_type": service.get('serviceName'), "service_group_name": App.get('defaultServiceGroupName'), }});
   },
 
@@ -2017,7 +2026,7 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
       }, this);
     }, this);
 
-    var selectedStack = App.Stack.find().findProperty('isSelected', true);
+    var selectedStack = this.getSelectedStack();
     blueprint = {
         'configurations': totalConf,
         'host_groups': host_groups.filter(function (item) { return item.cardinality > 0; }),

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/mappers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js
index cde9bcc..38d9c08 100644
--- a/ambari-web/app/mappers.js
+++ b/ambari-web/app/mappers.js
@@ -19,6 +19,7 @@
 //load all mappers
 require('mappers/server_data_mapper');
 require('mappers/stack_service_mapper');
+require('mappers/mpack_service_mapper');
 require('mappers/stack_mapper');
 require('mappers/stack_version_mapper');
 require('mappers/configs/themes_mapper');
@@ -44,4 +45,4 @@ require('mappers/alert_notification_mapper');
 require('mappers/root_service_mapper');
 require('mappers/widget_mapper');
 require('mappers/widget_layout_mapper');
-require('mappers/stack_upgrade_history_mapper');
\ No newline at end of file
+require('mappers/stack_upgrade_history_mapper');

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/mappers/mpack_service_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/mpack_service_mapper.js b/ambari-web/app/mappers/mpack_service_mapper.js
new file mode 100644
index 0000000..72cffa3
--- /dev/null
+++ b/ambari-web/app/mappers/mpack_service_mapper.js
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var App = require('app');
+
+App.MpackServiceMapper = App.QuickDataMapper.create({
+  model: App.StackService,
+  component_model: App.StackServiceComponent,
+
+  config: {
+    id: 'service_name',
+    stack_id: 'stack_id',
+    service_name: 'service_name',
+    service_type: 'service_type',
+    display_name: 'display_name',
+    config_types: 'config_types',
+    comments: 'comments',
+    service_version: 'service_version',
+    stack_name: 'stack_name',
+    stack_version: 'stack_version',
+    selection: 'selection',
+    is_mandatory: 'is_mandatory',
+    is_selected: 'is_selected',
+    is_installed: 'is_installed',
+    is_installable: 'is_installable',
+    is_service_with_widgets: 'is_service_with_widgets',
+    required_services: 'required_services',
+    service_check_supported: 'service_check_supported',
+    support_delete_via_ui: 'support_delete_via_ui',
+    service_components_key: 'service_components',
+    service_components_type: 'array',
+    service_components: {
+      item: 'id'
+    }
+  },
+
+  component_config: {
+    id: 'component_name',
+    component_name: 'component_name',
+    display_name: 'display_name',
+    cardinality: 'cardinality',
+    custom_commands: 'custom_commands',
+    reassign_allowed : 'reassign_allowed',
+    decommission_allowed: 'decommission_allowed',
+    has_bulk_commands_definition: 'has_bulk_commands_definition',
+    bulk_commands_display_name: 'bulk_commands_display_name',
+    bulk_commands_master_component_name: 'bulk_commands_master_component_name',
+    service_name: 'service_name',
+    component_category: 'component_category',
+    rolling_restart_supported: 'rolling_restart_supported',
+    is_master: 'is_master',
+    is_client: 'is_client',
+    stack_name: 'stack_name',
+    stack_version: 'stack_version',
+    stack_service_id: 'service_name',
+    dependencies_key: 'dependencies',
+    dependencies_type: 'array',
+    dependencies: {
+      item: 'Dependencies'
+    }
+  },
+
+  map: function (service) {
+    var model = this.get('model');
+    var result = [];
+    var stackServiceComponents = [];
+    var nonInstallableServices = ['KERBEROS'];
+    var displayOrderLength = App.StackService.displayOrder.length;
+    var stackService = service.StackServices;
+    var serviceComponents = [];
+    service.components.forEach(function (serviceComponent) {
+      var dependencies = serviceComponent.dependencies.map(function (dependecy) {
+        return { Dependencies: App.keysUnderscoreToCamelCase(App.permit(dependecy.Dependencies, ['component_name', 'scope', 'service_name'])) };
+      });
+      serviceComponent.StackServiceComponents.id = serviceComponent.StackServiceComponents.component_name;
+      serviceComponent.StackServiceComponents.dependencies = dependencies;
+      serviceComponents.push(serviceComponent.StackServiceComponents);
+      var parsedResult = this.parseIt(serviceComponent.StackServiceComponents, this.get('component_config'));
+      if (parsedResult.id == 'MYSQL_SERVER') {
+        parsedResult.custom_commands = parsedResult.custom_commands.without('CLEAN');
+      }
+      stackServiceComponents.push(parsedResult);
+    }, this);
+    stackService.stack_id = stackService.stack_name + '-' + stackService.stack_version;
+    stackService.service_components = serviceComponents;
+    stackService.is_service_with_widgets = service.artifacts.someProperty('Artifacts.artifact_name', 'widgets_descriptor');
+    // @todo: replace with server response value after API implementation
+    if (nonInstallableServices.contains(stackService.service_name)) {
+      stackService.is_installable = false;
+      stackService.is_selected = false;
+    }
+    if(stackService.selection === "MANDATORY") {
+      stackService.is_mandatory = true;
+    }
+    result.push(this.parseIt(stackService, this.get('config')));
+
+    App.store.safeLoadMany(this.get('component_model'), stackServiceComponents);
+    App.store.safeLoadMany(model, result);
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/mappers/stack_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/stack_mapper.js b/ambari-web/app/mappers/stack_mapper.js
index 07fb137..415845e 100644
--- a/ambari-web/app/mappers/stack_mapper.js
+++ b/ambari-web/app/mappers/stack_mapper.js
@@ -22,7 +22,7 @@ App.stackMapper = App.QuickDataMapper.create({
   modelOS: App.OperatingSystem,
   modelRepo: App.Repository,
   modelServices: App.ServiceSimple,
-  
+
   configStack: {
     id: 'id',
     stack_name: 'stack_name',
@@ -51,7 +51,7 @@ App.stackMapper = App.QuickDataMapper.create({
       item: 'id'
     }
   },
-  
+
   configOS: {
     id: 'id',
     os_type: 'os_type',
@@ -72,7 +72,7 @@ App.stackMapper = App.QuickDataMapper.create({
     display_name: 'display_name',
     latest_version: 'latest_version'
   },
-  
+
   configRepository: {
     id: 'id',
     base_url: 'base_url',
@@ -89,7 +89,7 @@ App.stackMapper = App.QuickDataMapper.create({
     components: 'components',
     distribution: 'distribution'
   },
-  
+
   map: function(json) {
     var modelStack = this.get('modelStack');
     var modelOS = this.get('modelOS');
@@ -104,6 +104,7 @@ App.stackMapper = App.QuickDataMapper.create({
     if (!stack.id) {
       stack.id = stack.stack_name + '-' + stack.stack_version + '-' + stack.repository_version; //HDP-2.5-2.5.0.0
     }
+
     var operatingSystemsArray = [];
     var servicesArray = [];
 
@@ -125,7 +126,7 @@ App.stackMapper = App.QuickDataMapper.create({
       operatingSystems.is_selected = ops.isSelected == true || ops.isSelected == undefined;
       resultOS.push(this.parseIt(operatingSystems, this.get('configOS')));
       operatingSystemsArray.pushObject(operatingSystems);
-        
+
     }, this);
 
     stack.stack_services.forEach(function(service) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index f74c560..bb8b218 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -597,6 +597,12 @@ Em.I18n.translations = {
   'installer.controls.serviceConfigMasterHosts.header':'{0} Hosts',
   'installer.controls.slaveComponentChangeGroupName.error':'group with this name already exist',
 
+  'installer.selectMpacks.loadSelectionFailed': 'Could not load Management Packs. The software registry may not be available.',
+  'installer.selectMpacks.header': 'Select Management Packs',
+  'installer.selectMpacks.body.header': 'Select Management Packs',
+  'installer.selectMpacks.noMpacksAvailable': 'No management packs are available.',
+  'installer.selectMpacks.noMpacksSelected': 'Select from the list of available products.',
+
   'installer.step0.header':'Get Started',
   'installer.step0.body.header':'Get Started',
   'installer.step0.body':'This wizard will walk you through the cluster installation process.  First, start by naming your new cluster.',

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/mixins/wizard/wizard_menu_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/wizard/wizard_menu_view.js b/ambari-web/app/mixins/wizard/wizard_menu_view.js
index c2959e9..6dd7394 100644
--- a/ambari-web/app/mixins/wizard/wizard_menu_view.js
+++ b/ambari-web/app/mixins/wizard/wizard_menu_view.js
@@ -34,7 +34,13 @@ App.WizardMenuMixin = Em.Mixin.create({
 
   isStepDisabled: function (stepName) {
     let index = this.get('controller').getStepIndex(stepName);
-    return this.get('controller.isStepDisabled').findProperty('step', index).get('value');
+    let step = this.get('controller.isStepDisabled').findProperty('step', index);
+
+    if (step) {
+      return step.get('value');
+    }
+
+    return false;
   },
 
   isStepCompleted(stepName) {
@@ -48,6 +54,7 @@ App.WizardMenuMixin = Em.Mixin.create({
   isStep3Disabled: isStepDisabled("step3"),
   isConfigureDownloadDisabled: isStepDisabled("configureDownload"),
   isDownloadProductsDisabled: isStepDisabled("downloadProducts"),
+  isSelectMpacksDisabled: isStepDisabled("selectMpacks"),
   isStep4Disabled: isStepDisabled("step4"),
   isStep5Disabled: isStepDisabled("step5"),
   isStep6Disabled: isStepDisabled("step6"),
@@ -62,6 +69,7 @@ App.WizardMenuMixin = Em.Mixin.create({
   isStep3Completed: isStepCompleted("step3"),
   isConfigureDownloadCompleted: isStepCompleted("configureDownload"),
   isDownloadProductsCompleted: isStepCompleted("downloadProducts"),
+  isSelectMpacksCompleted: isStepCompleted("selectMpacks"),
   isStep4Completed: isStepCompleted("step4"),
   isStep5Completed: isStepCompleted("step5"),
   isStep6Completed: isStepCompleted("step6"),

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/models/stack.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/stack.js b/ambari-web/app/models/stack.js
index 657ee5c..1fbc329 100644
--- a/ambari-web/app/models/stack.js
+++ b/ambari-web/app/models/stack.js
@@ -34,8 +34,6 @@ App.Stack = DS.Model.extend({
   operatingSystems: DS.hasMany('App.OperatingSystem'),
   isSelected: DS.attr('boolean', {defaultValue: false}),
 
-  versionInfoId: null,
-
   stackNameVersion: Em.computed.concat('-', 'stackName', 'stackVersion'),
 
   isPatch: Em.computed.equal('type', 'PATCH'),

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/router.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/router.js b/ambari-web/app/router.js
index c2d2a75..d0b1714 100644
--- a/ambari-web/app/router.js
+++ b/ambari-web/app/router.js
@@ -45,7 +45,9 @@ App.WizardRoute = Em.Route.extend({
 
   gotoConfigureDownload: Em.Router.transitionTo('configureDownload'),
 
-  gotoDownloadProducts: Em.Router.transitionTo('configureDownload'),
+  gotoSelectMpacks: Em.Router.transitionTo('selectMpacks'),
+  
+  gotoDownloadProducts: Em.Router.transitionTo('downloadProducts'),
 
   isRoutable: function() {
     return typeof this.get('route') === 'string' && App.router.get('loggedIn');
@@ -208,7 +210,7 @@ App.Router = Em.Router.extend({
     } else {
       newStep = step;
     }
-    
+
     var previousStep = parseInt(this.getInstallerCurrentStep(), 10);
     this.set('isFwdNavigation', newStep >= previousStep);
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/routes/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js
index 5c3d806..5899ff3 100644
--- a/ambari-web/app/routes/installer.js
+++ b/ambari-web/app/routes/installer.js
@@ -119,66 +119,66 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     }
   }),
 
-  step1: Em.Route.extend({
-    route: '/step1',
-    connectOutlets: function (router) {
-      console.time('step1 connectOutlets');
-      var self = this;
-      var controller = router.get('installerController');
-      var wizardStep1Controller = router.get('wizardStep1Controller');
-      wizardStep1Controller.set('skipValidationChecked', false);
-      wizardStep1Controller.set('optionsToSelect', {
-        'usePublicRepo': {
-          index: 0,
-          isSelected: true
-        },
-        'useLocalRepo': {
-          index: 1,
-          isSelected: false,
-          'uploadFile': {
-            index: 0,
-            name: 'uploadFile',
-            file: '',
-            hasError: false,
-            isSelected: true
-          },
-          'enterUrl': {
-            index: 1,
-            name: 'enterUrl',
-            url: '',
-            placeholder: Em.I18n.t('installer.step1.useLocalRepo.enterUrl.placeholder'),
-            hasError: false,
-            isSelected: false
-          }
-        }
-      });
-      controller.setCurrentStep('step1');
-      controller.loadAllPriorSteps().done(function () {
-        wizardStep1Controller.set('wizardController', controller);
-        controller.connectOutlet('wizardStep1', controller.get('content'));
-        self.scrollTop();
-        console.timeEnd('step1 connectOutlets');
-      });
-    },
-    back: Em.Router.transitionTo('downloadProducts'),
-    next: function (router) {
-      console.time('step1 next');
-      if(router.get('btnClickInProgress')) {
-        return;
-      }
-      var wizardStep1Controller = router.get('wizardStep1Controller');
-      var installerController = router.get('installerController');
-      installerController.validateJDKVersion(function() {
-        installerController.checkRepoURL(wizardStep1Controller).done(function () {
-          App.set('router.nextBtnClickInProgress', true);
-          installerController.setDBProperty('service', undefined);
-          installerController.setStacks();
-          router.transitionTo('step4');
-          console.timeEnd('step1 next');
-        });
-      }, function() {});
-    }
-  }),
+  // step1: Em.Route.extend({
+  //   route: '/step1',
+  //   connectOutlets: function (router) {
+  //     console.time('step1 connectOutlets');
+  //     var self = this;
+  //     var controller = router.get('installerController');
+  //     var wizardStep1Controller = router.get('wizardStep1Controller');
+  //     wizardStep1Controller.set('skipValidationChecked', false);
+  //     wizardStep1Controller.set('optionsToSelect', {
+  //       'usePublicRepo': {
+  //         index: 0,
+  //         isSelected: true
+  //       },
+  //       'useLocalRepo': {
+  //         index: 1,
+  //         isSelected: false,
+  //         'uploadFile': {
+  //           index: 0,
+  //           name: 'uploadFile',
+  //           file: '',
+  //           hasError: false,
+  //           isSelected: true
+  //         },
+  //         'enterUrl': {
+  //           index: 1,
+  //           name: 'enterUrl',
+  //           url: '',
+  //           placeholder: Em.I18n.t('installer.step1.useLocalRepo.enterUrl.placeholder'),
+  //           hasError: false,
+  //           isSelected: false
+  //         }
+  //       }
+  //     });
+  //     controller.setCurrentStep('step1');
+  //     controller.loadAllPriorSteps().done(function () {
+  //       wizardStep1Controller.set('wizardController', controller);
+  //       controller.connectOutlet('wizardStep1', controller.get('content'));
+  //       self.scrollTop();
+  //       console.timeEnd('step1 connectOutlets');
+  //     });
+  //   },
+  //   back: Em.Router.transitionTo('selectMpacks'),
+  //   next: function (router) {
+  //     console.time('step1 next');
+  //     if(router.get('btnClickInProgress')) {
+  //       return;
+  //     }
+  //     var wizardStep1Controller = router.get('wizardStep1Controller');
+  //     var installerController = router.get('installerController');
+  //     installerController.validateJDKVersion(function() {
+  //       installerController.checkRepoURL(wizardStep1Controller).done(function () {
+  //         App.set('router.nextBtnClickInProgress', true);
+  //         installerController.setDBProperty('service', undefined);
+  //         installerController.setStacks();
+  //         router.transitionTo('step4');
+  //         console.timeEnd('step1 next');
+  //       });
+  //     }, function() {});
+  //   }
+  // }),
 
   step2: Em.Route.extend({
     route: '/step2',
@@ -262,7 +262,6 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     }
   }),
 
-
   configureDownload: Em.Route.extend({
     route: '/configureDownload',
     connectOutlets: function (router) {
@@ -272,31 +271,6 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var configureDownloadController = router.get('wizardConfigureDownloadController');
       var newStepIndex = controller.getStepIndex('configureDownload');
       router.setNavigationFlow(newStepIndex);
-      configureDownloadController.set('optionsToSelect', {
-        'usePublicRepo': {
-          index: 0,
-          isSelected: true
-        },
-        'useLocalRepo': {
-          index: 1,
-          isSelected: false,
-          'uploadFile': {
-            index: 0,
-            name: 'uploadFile',
-            file: '',
-            hasError: false,
-            isSelected: true
-          },
-          'enterUrl': {
-            index: 1,
-            name: 'enterUrl',
-            url: '',
-            placeholder: Em.I18n.t('installer.step1.useLocalRepo.enterUrl.placeholder'),
-            hasError: false,
-            isSelected: false
-          }
-        }
-      });
       controller.setCurrentStep('configureDownload');
       controller.loadAllPriorSteps().done(function () {
         configureDownloadController.set('wizardController', controller);
@@ -312,11 +286,44 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         return;
       }
       App.set('router.nextBtnClickInProgress', true);
-      router.transitionTo('downloadProducts');
+      var installerController = router.get('installerController');
+      installerController.setDBProperty('service', undefined);
+      router.transitionTo('selectMpacks');
       console.timeEnd('configureDownload next');
     }
   }),
 
+  selectMpacks: App.StepRoute.extend({
+    route: '/selectMpacks',
+    breadcrumbs: { label: Em.I18n.translations['installer.selectMpacks.header'] },
+    connectOutlets: function (router) {
+      console.time('selectMpacks connectOutlets');
+      var self = this;
+      var controller = router.get('installerController');
+      controller.setCurrentStep('selectMpacks');
+      controller.loadAllPriorSteps().done(function () {
+        var wizardSelectMpacksController = router.get('wizardSelectMpacksController');
+        wizardSelectMpacksController.set('wizardController', controller);
+        controller.connectOutlet('wizardSelectMpacks', controller.get('content'));
+        self.scrollTop();
+        console.timeEnd('selectMpacks connectOutlets');
+      });
+    },
+
+    backTransition: function (router) {
+      router.transitionTo('configureDownload');
+    },
+
+    next: function (router, context) {
+      console.time('selectMpacks next');
+      if (!router.get('btnClickInProgress')) {
+        App.set('router.nextBtnClickInProgress', true);
+        router.transitionTo('downloadProducts');
+        console.timeEnd('selectMpacks next');
+      }
+    },
+  }),
+
   downloadProducts: App.StepRoute.extend({
     route: '/downloadProducts',
     connectOutlets: function (router) {
@@ -336,7 +343,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     },
 
     backTransition: function (router) {
-      router.transitionTo('configureDownload');
+      router.transitionTo('selectMpacks');
     },
 
     next: function (router) {
@@ -345,7 +352,14 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         return;
       }
       App.set('router.nextBtnClickInProgress', true);
-      router.transitionTo('step1');
+      router.get('wizardStep5Controller').clearRecommendations(); // Force reload recommendation before step 5
+      var controller = router.get('installerController');
+      controller.setDBProperties({
+        recommendations: undefined,
+        masterComponentHosts: undefined
+      });
+      controller.clearEnhancedConfigs();
+      router.transitionTo('step5');
       console.timeEnd('downloadProducts next');
     }
   }),
@@ -414,7 +428,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       });
     },
     backTransition: function(router) {
-      router.transitionTo('step4');
+      router.transitionTo('downloadProducts');
     },
     next: function (router) {
       console.time('step5 next');
@@ -695,6 +709,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
 
   gotoStep10: Em.Router.transitionTo('step10'),
 
-  gotoConfigureDownload: Em.Router.transitionTo('configureDownload')
+  gotoConfigureDownload: Em.Router.transitionTo('configureDownload'),
+
+  gotoSelectMpacks: Em.Router.transitionTo('selectMpacks'),
+
+  gotoDownloadProducts: Em.Router.transitionTo('downloadProducts')
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/templates/installer.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/installer.hbs b/ambari-web/app/templates/installer.hbs
index 20cf109..47e63cc 100644
--- a/ambari-web/app/templates/installer.hbs
+++ b/ambari-web/app/templates/installer.hbs
@@ -29,15 +29,14 @@
           <li {{QAAttr "wizard-nav-step"}} {{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">1</span></i><p class="step-name">{{t installer.step2.header}}</p></a></li>
           <li {{QAAttr "wizard-nav-step"}} {{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">2</span></i><p class="step-name">{{t installer.step3.header}}</p></a></li>
           <li {{QAAttr "wizard-nav-step"}} {{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">3</span></i><p class="step-name">{{t installer.configureDownload.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{bindAttr class="isDownloadProducts:active view.isDownloadProductsDisabled:disabled view.isDownloadProductsCompleted:completed"}}><a href="javascript:void(null);" {{action gotoDownloadProducts target="controller"}}><i class="step-marker"><span class="step-index">4</span></i><p class="step-name">{{t installer.downloadProducts.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">5</span></i><p class="step-name">{{t installer.step1.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">6</span></i><p class="step-name">{{t installer.step4.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">7</span></i><p class="step-name">{{t installer.step5.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">8</span></i><p class="step-name">{{t installer.step6.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">9</span></i><p class="step-name">{{t installer.step7.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">10</span></i><p class="step-name">{{t installer.step8.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">11</span></i><p class="step-name">{{t installer.step9.header}}</p></a></li>
-          <li {{QAAttr "wizard-nav-step"}} {{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">12</span></i><p class="step-name">{{t installer.step10.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">4</span></i><p class="step-name">{{t installer.selectMpacks.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{bindAttr class="isDownloadProducts:active view.isDownloadProductsDisabled:disabled view.isDownloadProductsCompleted:completed"}}><a href="javascript:void(null);" {{action gotoDownloadProducts target="controller"}}><i class="step-marker"><span class="step-index">5</span></i><p class="step-name">{{t installer.downloadProducts.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">6</span></i><p class="step-name">{{t installer.step5.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">7</span></i><p class="step-name">{{t installer.step6.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">8</span></i><p class="step-name">{{t installer.step7.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">9</span></i><p class="step-name">{{t installer.step8.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">10</span></i><p class="step-name">{{t installer.step9.header}}</p></a></li>
+          <li {{QAAttr "wizard-nav-step"}} {{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">11</span></i><p class="step-name">{{t installer.step10.header}}</p></a></li>
         </ul>
       </div>
         {{! outlet includes body and footer }}

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/templates/wizard/configureDownload.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/configureDownload.hbs b/ambari-web/app/templates/wizard/configureDownload.hbs
index d178fcf..c28660e 100644
--- a/ambari-web/app/templates/wizard/configureDownload.hbs
+++ b/ambari-web/app/templates/wizard/configureDownload.hbs
@@ -20,43 +20,36 @@
 
   <div class="panel panel-default">
     <div class="panel-body">
-      {{#if isLoadingComplete}}
-      {{! left tabs }}
-
-        {{#if App.router.nextBtnClickInProgress}}
-          {{view App.SpinnerView}}
-        {{else}}
-          <form id="repoVersionInfoForm" class="form-horizontal" role="form" name="localVersionInfoForm" novalidate>
-
-            <div class="panel panel-default repos-panel">
-              <div class="step-title">
-                <p>{{t installer.configureDownload.body.title}}</p>
-              </div>
-              <p class="step-description">{{t installer.configureDownload.body.description}}</p>
-              <div class="panel-body version-contents-body">
-                <div class="row radio-group">
-                  {{! Public Repository radio }}
-                  <div {{bindAttr class=":col-sm-4 :radio :big-radio :public-radio :wizard-plain-text"}} {{action usePublicRepo target="controller"}}>
-                    {{view view.usePublicRepoRadioButton classNames="repo-checkbox" labelIdentifier="use-public-repo"}}
-                    <p>{{t installer.configureDownload.publicRepo.hoverDesc}}</p>
-                    <i class="icon icon-cloud-download"></i>
-                    <div class="repo-group">{{t installer.configureDownload.publicRepo}}</div>
-                  </div>
-                  {{!--Local repo radio--}}
-                  {{!--Not allowing any action on Local Repository for the first iteration. Will use action useLocalRepo later when this option is allowed--}}
-                  <div {{bindAttr class=":col-sm-4 :radio :big-radio :local-radio :wizard-plain-text"}}>
-                    {{view view.useLocalRepoRadioButton classNames="repo-checkbox" labelIdentifier="use-local-repo"}}
-                    <p>{{t installer.configureDownload.localRepo.hoverDesc}}</p>
-                    <i class="icon icon-tasks"></i>
-                    <div class="repo-group">{{t installer.configureDownload.localRepo}}</div>
-                  </div>
+      {{#if App.router.nextBtnClickInProgress}}
+        {{view App.SpinnerView}}
+      {{else}}
+        <form id="repoVersionInfoForm" class="form-horizontal" role="form" name="localVersionInfoForm" novalidate>
+          <div class="panel panel-default repos-panel">
+            <div class="step-title">
+              <p>{{t installer.configureDownload.body.title}}</p>
+            </div>
+            <p class="step-description">{{t installer.configureDownload.body.description}}</p>
+            <div class="panel-body version-contents-body">
+              <div class="row radio-group">
+                {{! Public Repository radio }}
+                <div {{bindAttr class=":col-sm-4 :radio :big-radio :public-radio :wizard-plain-text"}} {{action usePublicRepo target="controller"}}>
+                  {{view view.usePublicRepoRadioButton classNames="repo-checkbox" labelIdentifier="use-public-repo"}}
+                  <p>{{t installer.configureDownload.publicRepo.hoverDesc}}</p>
+                  <i class="icon icon-cloud-download"></i>
+                  <div class="repo-group">{{t installer.configureDownload.publicRepo}}</div>
+                </div>
+                {{!--Local repo radio--}}
+                {{!--Not allowing any action on Local Repository for the first iteration. Will use action useLocalRepo later when this option is allowed--}}
+                <div {{bindAttr class=":col-sm-4 :radio :big-radio :local-radio :wizard-plain-text"}}>
+                  {{view view.useLocalRepoRadioButton classNames="repo-checkbox" labelIdentifier="use-local-repo"}}
+                  <p>{{t installer.configureDownload.localRepo.hoverDesc}}</p>
+                  <i class="icon icon-tasks"></i>
+                  <div class="repo-group">{{t installer.configureDownload.localRepo}}</div>
                 </div>
               </div>
             </div>
-          </form>
-        {{/if}}
-      {{else}}
-        {{view App.SpinnerView}}
+          </div>
+        </form>
       {{/if}}
     </div>
   </div>
@@ -68,7 +61,7 @@
       {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
     {{/if}}
   </button>
-  <button type="button" class="btn btn-success pull-right" {{bindAttr disabled="view.isSubmitDisabled"}} {{action next}} {{QAAttr "wizard-next"}}>
+  <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"}}
     {{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/templates/wizard/downloadProducts.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/downloadProducts.hbs b/ambari-web/app/templates/wizard/downloadProducts.hbs
index 25be35d..a426225 100644
--- a/ambari-web/app/templates/wizard/downloadProducts.hbs
+++ b/ambari-web/app/templates/wizard/downloadProducts.hbs
@@ -88,10 +88,10 @@
       {{view App.SpinnerView tagName="span" classNames="service-button-spinner"}}
     {{/if}}
   </button>
-  <button type="button" class="btn btn-success pull-right" {{bindAttr disabled="view.isSubmitDisabled"}} {{action next}} {{QAAttr "wizard-next"}}>
+  <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"}}
     {{/if}}
     {{t common.next}} &rarr;
   </button>
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/templates/wizard/selectMpacks.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/selectMpacks.hbs b/ambari-web/app/templates/wizard/selectMpacks.hbs
new file mode 100644
index 0000000..b325b8c
--- /dev/null
+++ b/ambari-web/app/templates/wizard/selectMpacks.hbs
@@ -0,0 +1,70 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+<div id="select-mpacks" class="wizard-content col-md-9">
+  <h4 class="step-title">{{t installer.selectMpacks.body.header}}</h4>
+
+  <div class="panel panel-default">
+    <div class="panel-body">
+      <!-- Registry -->
+      <div class="col-md-6">
+        <div class="panel panel-default">
+          <div class="panel-body">
+            {{#if controller.content.mpacks}}
+              {{#each mpack in controller.content.mpacks}}
+                {{view App.WizardMpackView mpackBinding="mpack"}}
+              {{/each}}
+            {{else}}
+              {{t installer.selectMpacks.noMpacksAvailable}}
+            {{/if}}
+          </div>
+        </div>
+      </div>
+
+      <!-- Selection -->
+      <div class="col-md-6">
+        <div class="panel panel-default">
+          <div class="panel-body">
+            {{#if controller.hasSelectedMpackVersions}}
+              {{#each mpackVersion in controller.selectedMpackVersions}}
+                {{view App.WizardSelectedMpackVersionView mpackVersionBinding="mpackVersion"}}
+              {{/each}}
+            {{else}}
+              {{t installer.selectMpacks.noMpacksSelected}}
+            {{/if}}
+          </div>
+        </div>
+      </div>
+
+    </div>
+  </div>
+</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>
+  <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"}}
+    {{/if}}
+    {{t common.next}} &rarr;
+  </button>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/templates/wizard/selectMpacks/mpack.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/selectMpacks/mpack.hbs b/ambari-web/app/templates/wizard/selectMpacks/mpack.hbs
new file mode 100644
index 0000000..0f24859
--- /dev/null
+++ b/ambari-web/app/templates/wizard/selectMpacks/mpack.hbs
@@ -0,0 +1,31 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+<div class="panel panel-default">
+  <div class="panel-heading" role="tab" id="headingOne">
+    <h4 class="panel-title">{{mpack.name}} {{view.version}}</h4>
+    <p>{{mpack.description}}</p>
+  </div>
+  <div>
+  {{#each service in view.services}}
+    <button {{bindAttr id="service.name"}} class="btn-service" {{action "selectService" service.id target="controller"}}>
+      {{service.name}}
+      <span {{bindAttr class="selected:service-remove:service-add"}} {{bindAttr title="service.version"}}></span>
+    </button>
+  {{/each}}
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/15347746/ambari-web/app/templates/wizard/selectMpacks/selectedMpackVersion.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/selectMpacks/selectedMpackVersion.hbs b/ambari-web/app/templates/wizard/selectMpacks/selectedMpackVersion.hbs
new file mode 100644
index 0000000..8b24c25
--- /dev/null
+++ b/ambari-web/app/templates/wizard/selectMpacks/selectedMpackVersion.hbs
@@ -0,0 +1,32 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+<div class="panel panel-default">
+  <div class="panel-heading" role="tab" id="headingOne">
+    <h4 class="panel-title">{{mpackVersion.mpack.name}} {{mpackVersion.version}}</h4>
+  </div>
+  <div>
+  {{#each service in mpackVersion.services}}
+    {{#if service.selected}}
+    <button {{bindAttr id="service.name"}} class="btn-service" {{action "removeService" service.id target="controller"}}>
+      {{service.name}}
+      <span {{bindAttr class="selected:service-remove:service-add" title="service.version"}}></span>
+    </button>
+    {{/if}}
+  {{/each}}
+  </div>
+</div>