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/11/13 18:31:28 UTC

[1/2] ambari git commit: AMBARI-22420 - Enable mpack version selection (Jason Golieb via jonathanhurley)

Repository: ambari
Updated Branches:
  refs/heads/branch-feature-AMBARI-14714-ui 0d39eb4df -> 4087a1179


http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/wizard/selectMpacks_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/selectMpacks_test.js b/ambari-web/test/controllers/wizard/selectMpacks_test.js
index 73210b4..7102d91 100644
--- a/ambari-web/test/controllers/wizard/selectMpacks_test.js
+++ b/ambari-web/test/controllers/wizard/selectMpacks_test.js
@@ -17,26 +17,651 @@
  */
 
 var App = require('app');
+require('controllers/installer');
 require('controllers/wizard/selectMpacks_controller');
+
 var wizardSelectMpacksController;
+var registry = {
+  "href": "http://localhost:8080/api/v1/registries?fields=mpacks/*,mpacks/versions/RegistryMpackVersionInfo/*",
+  "items": [
+    {
+      "href": "http://localhost:8080/api/v1/registries/1",
+      "RegistryInfo": {
+        "registry_id": 1
+      },
+      "mpacks": [
+        {
+          "href": "http://localhost:8080/api/v1/registries/1/mpacks/EDW",
+          "RegistryMpackInfo": {
+            "mpack_description": "Buzz word buzz word, buzz word buzz word buzz word buzz wording buzzy buzz word.",
+            "mpack_display_name": null,
+            "mpack_logo_url": "https://public-repo-1.hortonworks.com/logos/edw-logo.png",
+            "mpack_name": "EDW",
+            "registry_id": 1
+          },
+          "versions": [
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/EDW/versions/1.0.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": [
+                  {
+                    "name": "HDPCore",
+                    "minVersion": "3.2.0",
+                    "maxVersion": "3.2.99"
+                  }
+                ],
+                "mpack_buildnum": "1.0.0.0-111",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/EDW1/EDW-1.0.0/index.html",
+                "mpack_name": "EDW",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/EDW/1.0.0.0-111/edw-ambari-mpack-1.0.0.0-111.tar.gz",
+                "mpack_version": "1.0.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HBASE",
+                    "version": "2.0.0"
+                  },
+                  {
+                    "name": "HIVE",
+                    "version": "2.0.0"
+                  }
+                ]
+              }
+            }
+          ]
+        },
+        {
+          "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore",
+          "RegistryMpackInfo": {
+            "mpack_description": "The latest Hortonworks release for Hortonworks Data Platlform Core (HDFS, ZooKeeper, YARN, MapReduce 2) and Hortonworks SmartSenseā„¢.",
+            "mpack_display_name": null,
+            "mpack_logo_url": "https://public-repo-1.hortonworks.com/logos/hdpcore-logo.png",
+            "mpack_name": "HDPCore",
+            "registry_id": 1
+          },
+          "versions": [
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.0.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.0.0.0-247",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.0.0/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "http://localhost:8080/resources/mpack-repo/hdp-ambari-mpack-3.0.0.0-247.tar.gz",
+                "mpack_version": "3.0.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.1.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.1.0.0-234",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.1.0/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDPCore/3.1.0.0-234/hdpcore-ambari-mpack-3.1.0.0-234.tar.gz",
+                "mpack_version": "3.1.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.1.1",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.1.1.0-111",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.1.1/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDPCore/3.1.1.0-111/hdpcore-ambari-mpack-3.1.1.0-111.tar.gz",
+                "mpack_version": "3.1.1",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.2.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.2.0.0-345",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.2.0/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDPCore/3.2.0.0-345/hdpcore-ambari-mpack-3.2.0.0-345.tar.gz",
+                "mpack_version": "3.2.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.2.1",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.2.1.0-333",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.2.1/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDPCore/3.2.1.0-333/hdpcore-ambari-mpack-3.2.1.0-333.tar.gz",
+                "mpack_version": "3.2.1",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.3.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.3.0.0-456",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.3.0/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDPCore/3.3.0.0-456/hdpcore-ambari-mpack-3.3.0.0-456.tar.gz",
+                "mpack_version": "3.3.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDPCore/versions/3.4.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": null,
+                "mpack_buildnum": "3.4.0.0-567",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDPCore3/HDPCore-3.4.0/index.html",
+                "mpack_name": "HDPCore",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDPCore/3.4.0.0-567/hdpcore-ambari-mpack-3.4.0.0-567.tar.gz",
+                "mpack_version": "3.4.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "HDFS",
+                    "version": "3.0.0"
+                  },
+                  {
+                    "name": "ZOOKEEPER",
+                    "version": "3.0.0"
+                  }
+                ]
+              }
+            }
+          ]
+        },
+        {
+          "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDS",
+          "RegistryMpackInfo": {
+            "mpack_description": "Buzz word buzz word, buzz word buzz word buzz word buzz wording buzzy buzz word.",
+            "mpack_display_name": null,
+            "mpack_logo_url": "https://public-repo-1.hortonworks.com/logos/hds-logo.png",
+            "mpack_name": "HDS",
+            "registry_id": 1
+          },
+          "versions": [
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDS/versions/3.0.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": [
+                  {
+                    "name": "HDPCore",
+                    "minVersion": "3.1.0",
+                    "maxVersion": "3.2.99"
+                  }
+                ],
+                "mpack_buildnum": "3.0.0.0-247",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDS4/HDS-4.3.0/index.html",
+                "mpack_name": "HDS",
+                "mpack_url": "http://localhost:8080/resources/mpack-repo/hds-ambari-mpack-3.0.0.0-247.tar.gz",
+                "mpack_version": "3.0.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "SPARK",
+                    "version": "2.0.0"
+                  },
+                  {
+                    "name": "ZEPPELIN",
+                    "version": "1.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDS/versions/4.3.0",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": [
+                  {
+                    "name": "HDPCore",
+                    "minVersion": "3.1.0",
+                    "maxVersion": "3.2.99"
+                  }
+                ],
+                "mpack_buildnum": "4.3.0.0-444",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDS4/HDS-4.3.0/index.html",
+                "mpack_name": "HDS",
+                "mpack_url": "file:///var/lib/ambari-server/resources/mpack-repo/hds-ambari-mpack-4.3.0.0-444.tar.gz",
+                "mpack_version": "4.3.0",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "SPARK",
+                    "version": "2.0.0"
+                  },
+                  {
+                    "name": "ZEPPELIN",
+                    "version": "1.0.0"
+                  }
+                ]
+              }
+            },
+            {
+              "href": "http://localhost:8080/api/v1/registries/1/mpacks/HDS/versions/4.3.1",
+              "RegistryMpackVersionInfo": {
+                "compatible_mpacks": [
+                  {
+                    "name": "HDPCore",
+                    "minVersion": "3.2.0",
+                    "maxVersion": "3.3.99"
+                  }
+                ],
+                "mpack_buildnum": "4.3.1.0-555",
+                "mpack_doc_url": "http://docs.hortonworks.com/HDPDocuments/HDS4/HDS-4.3.1/index.html",
+                "mpack_name": "HDS",
+                "mpack_url": "https://public-repo-1.hortonworks.com/mpacks/HDS/4.3.1.0-555/hds-ambari-mpack-4.3.1.0-555.tar.gz",
+                "mpack_version": "4.3.1",
+                "registry_id": 1,
+                "services": [
+                  {
+                    "name": "SPARK",
+                    "version": "2.0.0"
+                  },
+                  {
+                    "name": "ZEPPELIN",
+                    "version": "1.0.0"
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+};
 
 describe('App.WizardSelectMpacksController', function () {
 
   before(function () {
-    wizardSelectMpacksController = App.WizardSelectMpacksController.create();
+    wizardController = App.InstallerController.create();
+    wizardController.set('steps', [
+      "step0",
+      "selectMpacks",
+      "step2",
+      "step3",
+      "step4"
+    ]);
+
+    wizardSelectMpacksController = App.WizardSelectMpacksController.create({
+      isSubmitDisabled: false,
+      selectedServices: null,
+      selectedMpackVersions: null,
+      content: {
+        selectedServices: null,
+        selectedServiceNames: null,
+        selectedMpacks: null
+      },
+      wizardController: wizardController      
+    });
+    wizardSelectMpacksController.loadRegistrySucceeded(registry);
   });
 
-  describe('#getMPacks', function () {
+  describe('#loadStep', function () {
+    it('adds previously selected services to selection', function () {
+      wizardSelectMpacksController.set('content.selectedServices', [
+        { id: "HDPCore3.0.0ZOOKEEPER" },
+        { id: "HDPCore3.0.0HDFS" }
+      ]);
+
+      wizardSelectMpacksController.loadStep();
+      
+      var service = wizardSelectMpacksController.getServiceById("HDPCore3.0.0ZOOKEEPER");
+      expect(service.get('selected')).to.be.true;
+      expect(service.get('mpackVersion.selected')).to.be.true;
+
+      var service = wizardSelectMpacksController.getServiceById("HDPCore3.0.0HDFS");
+      expect(service.get('selected')).to.be.true;
+      expect(service.get('mpackVersion.selected')).to.be.true;
+    });
+  });
+
+  describe('#displayMpackVersion', function () {
+    var actual = {
+      mpack: {
+        versions: [
+          Em.Object.create({ id: "1", displayed: true }),
+          Em.Object.create({ id: "2", displayed: false }),
+          Em.Object.create({ id: "3", displayed: false }),
+          Em.Object.create({ id: "4", displayed: false }),
+        ]
+      }
+    };
+
     before(function () {
+      sinon.stub(wizardSelectMpacksController, 'getMpackVersionById').returns(actual);
+    });
+
+    it('should set chosen mpack version to displayed and set others to not displayed', function () {
+      var expected = {
+        mpack: {
+          versions: [
+            Em.Object.create({ id: "1", displayed: false }),
+            Em.Object.create({ id: "2", displayed: false }),
+            Em.Object.create({ id: "3", displayed: true }),
+            Em.Object.create({ id: "4", displayed: false }),
+          ]
+        }
+      };
+      
+      wizardSelectMpacksController.displayMpackVersion("3");
+      expect(actual).to.deep.equal(expected);
+    });
 
+    after(function () {
+      wizardSelectMpacksController.getMpackVersionById.restore();
     });
+  });
+
+  describe('#addServiceHandler', function () {
+    var actual;
+
+    before(function () {
+      var initial = Em.Object.create({
+        "0": true,
+        "1": true,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+      wizardSelectMpacksController.set('wizardController.content.stepsSavedState', initial);
+      
+      actual = Em.Object.create({
+        selected: false,
+        mpackVersion: {
+          selected: false,
+          services: [
+            actual,
+            Em.Object.create({ selected: false })
+          ]
+        }
+      });
 
+      sinon.stub(wizardSelectMpacksController, 'getServiceById').returns(actual);
+    });
+    
     after(function () {
+      wizardSelectMpacksController.getServiceById.restore();
     });
 
-    it('loads mpacks from registry', function () {
-    })
+    it('should set the service and its mpack to selected and set the step to unsaved', function () {
+      var expected = Em.Object.create({
+        selected: true,
+        mpackVersion: {
+          selected: true,
+          services: [
+            expected,
+            Em.Object.create({ selected: false })
+          ]
+        }
+      });
+      
+      wizardSelectMpacksController.addServiceHandler("HDPCore3.0.0ZOOKEEPER");
+      expect(actual).to.deep.equal(expected);
+      
+      var final = Em.Object.create({
+        "0": true,
+        "1": false,
+        "2": true,
+        "3": true,
+        "4": true
+      });
+      var savedState = wizardSelectMpacksController.get('wizardController.content.stepsSavedState');
+      expect(savedState).to.deep.equal(final);
+    });
+  });
+
+  describe('#removeServiceHandler', function () {
+    beforeEach(function () {
+      var initial = Em.Object.create({
+        "0": true,
+        "1": true,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+      wizardSelectMpacksController.set('wizardController.content.stepsSavedState', initial);
+    });
+
+    afterEach(function () {
+      var final = Em.Object.create({
+        "0": true,
+        "1": false,
+        "2": true,
+        "3": true,
+        "4": true
+      });
+      var savedState = wizardSelectMpacksController.get('wizardController.content.stepsSavedState');
+      expect(savedState).to.deep.equal(final);
+
+      wizardSelectMpacksController.getServiceById.restore();
+    });
+
+    it('should set only the service to not selected and set the step to unsaved', function () {
+      var actual = Em.Object.create({
+        selected: true,
+        mpackVersion: {
+          selected: true,
+          services: []
+        }
+      });
+      actual.set('mpackVersion.services', [
+        actual,
+        Em.Object.create({ selected: true })
+      ]);
+
+      sinon.stub(wizardSelectMpacksController, 'getServiceById').returns(actual);
+
+      wizardSelectMpacksController.removeServiceHandler("HDPCore3.0.0ZOOKEEPER");
+      expect(actual.get('selected')).to.be.false;
+      expect(actual.get('mpackVersion.selected')).to.be.true;
+    });
+
+    it('when removing the last service, it should set the service and its mpack to not selected and set the step to unsaved', function () {
+      var actual = Em.Object.create({
+        selected: true,
+        mpackVersion: {
+          selected: true,
+          services: []
+        }
+      });
+      actual.set('mpackVersion.services', [
+        actual,
+        Em.Object.create({ selected: false })
+      ]);
+
+      sinon.stub(wizardSelectMpacksController, 'getServiceById').returns(actual);
+
+      wizardSelectMpacksController.removeServiceHandler("HDPCore3.0.0ZOOKEEPER");
+      expect(actual.get('selected')).to.be.false;
+      expect(actual.get('mpackVersion.selected')).to.be.false;
+    });
+  });
+
+  describe('#clearSelection', function () {
+    it('should set all services and mpacks to be unselected', function () {
+      var servicesActual = [
+        Em.Object.create({ selected: true }),
+        Em.Object.create({ selected: true }),
+        Em.Object.create({ selected: true })
+      ];
+      wizardSelectMpacksController.set('content.mpackServices', servicesActual);
 
+      var versionsActual = [
+        Em.Object.create({ selected: true }),
+        Em.Object.create({ selected: true }),
+        Em.Object.create({ selected: true })
+      ];
+      wizardSelectMpacksController.set('content.mpackVersions', versionsActual);
+
+      var expected = [
+        Em.Object.create({ selected: false }),
+        Em.Object.create({ selected: false }),
+        Em.Object.create({ selected: false })
+      ];
+
+      wizardSelectMpacksController.clearSelection();
+      expect(servicesActual).to.deep.equal(expected);
+      expect(versionsActual).to.deep.equal(expected);
+    });
   });
 
+  describe('#submit', function () {
+    it('should populate content.selectedServices, content.selectedServiceNames, and content.selectedMpacks', function () {
+      wizardSelectMpacksController.set('selectedServices', [
+        {
+          id: "id1",
+          name: "name1",
+          mpackVersion: {
+            name: "mpackName1",
+            version: "1.0.0.0",
+            stackName: "stack1",
+            stackVersion: "1.0.0"
+          }
+        },
+        {
+          id: "id2",
+          name: "name2",
+          mpackVersion: {
+            name: "mpackName2",
+            version: "1.0.0.0",
+            stackName: "stack1",
+            stackVersion: "1.0.0"
+          }
+        }
+      ]);
+
+      wizardSelectMpacksController.set('selectedMpackVersions', [
+        {
+          mpack: {
+            name: "mpackName1",
+            displayName: "displayName1"
+          },
+          mpackUrl: "http://someurl.com/mpack1",
+          version: "1.0.0.0"
+        },
+        {
+          mpack: {
+            name: "mpackName2",
+            displayName: "displayName2"
+          },
+          mpackUrl: "http://someurl.com/mpack2",
+          version: "1.0.0.0"
+        }
+      ]);
+      
+      var expectedSelectedServices = [
+        {
+          id: "id1",
+          name: "name1",
+          mpackName: "mpackName1",
+          mpackVersion: "1.0.0.0",
+          stackName: "stack1",
+          stackVersion: "1.0.0"
+        },
+        {
+          id: "id2",
+          name: "name2",
+          mpackName: "mpackName2",
+          mpackVersion: "1.0.0.0",
+          stackName: "stack1",
+          stackVersion: "1.0.0"
+        }
+      ];
+
+      var expectedSelectedServiceNames = [
+        "name1",
+        "name2"
+      ];
+
+      var expectedSelectedMpacks = [
+        {
+          name: "mpackName1",
+          displayName: "displayName1",
+          url: "http://someurl.com/mpack1",
+          version: "1.0.0.0"
+        },
+        {
+          name: "mpackName2",
+          displayName: "displayName2",
+          url: "http://someurl.com/mpack2",
+          version: "1.0.0.0"
+        }
+      ];
+
+      sinon.stub(App.router, 'send');
+      wizardSelectMpacksController.submit();
+      App.router.send.restore();
+      
+      expect(wizardSelectMpacksController.get('content.selectedServices')).to.deep.equal(expectedSelectedServices);
+      expect(wizardSelectMpacksController.get('content.selectedServiceNames')).to.deep.equal(expectedSelectedServiceNames);
+      expect(wizardSelectMpacksController.get('content.selectedMpacks')).to.deep.equal(expectedSelectedMpacks);
+    })
+  })
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/wizard/step5_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step5_test.js b/ambari-web/test/controllers/wizard/step5_test.js
index 57c33b2..1cd9902 100644
--- a/ambari-web/test/controllers/wizard/step5_test.js
+++ b/ambari-web/test/controllers/wizard/step5_test.js
@@ -25,6 +25,7 @@ describe('App.WizardStep5Controller', function () {
 
   beforeEach(function () {
     c = App.WizardStep5Controller.create();
+    c.set('wizardController', App.InstallerController.create());
     sinon.stub(App.router, 'send', Em.K);
     App.set('router.nextBtnClickInProgress', false);
   });

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/wizard/step6_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step6_test.js b/ambari-web/test/controllers/wizard/step6_test.js
index 85b4fab..0369587 100644
--- a/ambari-web/test/controllers/wizard/step6_test.js
+++ b/ambari-web/test/controllers/wizard/step6_test.js
@@ -65,6 +65,7 @@ function getController() {
     m.push(obj);
   });
 
+  c.set('wizardController', App.InstallerController.create());
   c.set('content.hosts', h);
   c.set('content.masterComponentHosts', m);
   c.set('isMasters', false);
@@ -100,8 +101,8 @@ describe('App.WizardStep6Controller', function () {
       }
       controller.showValidationIssuesAcceptBox(callback);
       jQuery.when(deffer.promise()).then(function(data) {
-        expect(data).to.equal(true);    
-      }); 
+        expect(data).to.equal(true);
+      });
     });
   });
 
@@ -1856,7 +1857,7 @@ describe('App.WizardStep6Controller', function () {
     });
 
   });
-   
+
   describe('#anyHostErrors', function () {
 
     var tests = [
@@ -1880,11 +1881,11 @@ describe('App.WizardStep6Controller', function () {
         controller.set('hosts', test.host);
         expect(controller.get('anyHostErrors')).to.equal(test.result);
       })
-    });   
+    });
   });
 
 
-   
+
   describe('#anyHostWarnings', function () {
 
     var tests = [
@@ -1908,7 +1909,7 @@ describe('App.WizardStep6Controller', function () {
         controller.set('hosts', test.host);
         expect(controller.get('anyHostWarnings')).to.equal(test.result);
       })
-    });   
+    });
   });
 
   describe('#enableCheckboxesForDependentComponents', function () {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/wizard/step8_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step8_test.js b/ambari-web/test/controllers/wizard/step8_test.js
index fc977b7..c813746 100644
--- a/ambari-web/test/controllers/wizard/step8_test.js
+++ b/ambari-web/test/controllers/wizard/step8_test.js
@@ -55,65 +55,65 @@ var configs = Em.A([
 ]);
 
 var services = Em.A([
-        Em.Object.create({
-          serviceName: 's1',
-          isSelected: true,
-          isInstalled: false,
-          displayNameOnSelectServicePage: 's01',
-          isClientOnlyService: false,
-          serviceComponents: Em.A([
-            Em.Object.create({
-              isClient: true
-            })
-          ]),
-          configTypes: {
-            site1 : [],
-            site2 : []
-          },
-          isHiddenOnSelectServicePage: false
-        }),
-        Em.Object.create({
-          serviceName: 's2',
-          isSelected: true,
-          isInstalled: false,
-          displayNameOnSelectServicePage: 's02',
-          serviceComponents: Em.A([
-            Em.Object.create({
-              isMaster: true
-            })
-          ]),
-          configTypes: {
-            site3 : []
-          },
-          isHiddenOnSelectServicePage: false
-        }),
-        Em.Object.create({
-          serviceName: 's3',
-          isSelected: true,
-          isInstalled: false,
-          displayNameOnSelectServicePage: 's03',
-          serviceComponents: Em.A([
-            Em.Object.create({
-              isHAComponentOnly: true
-            })
-          ]),
-          configTypes: {},
-          isHiddenOnSelectServicePage: false
-        }),
-        Em.Object.create({
-          serviceName: 's4',
-          isSelected: true,
-          isInstalled: false,
-          displayNameOnSelectServicePage: 's03',
-          isClientOnlyService: true,
-          serviceComponents: Em.A([
-            Em.Object.create({
-              isClient: true
-            })
-          ]),
-          configTypes: {},
-          isHiddenOnSelectServicePage: false
-        })
+  Em.Object.create({
+    serviceName: 's1',
+    isSelected: true,
+    isInstalled: false,
+    displayNameOnSelectServicePage: 's01',
+    isClientOnlyService: false,
+    serviceComponents: Em.A([
+      Em.Object.create({
+        isClient: true
+      })
+    ]),
+    configTypes: {
+      site1 : [],
+      site2 : []
+    },
+    isHiddenOnSelectServicePage: false
+  }),
+  Em.Object.create({
+    serviceName: 's2',
+    isSelected: true,
+    isInstalled: false,
+    displayNameOnSelectServicePage: 's02',
+    serviceComponents: Em.A([
+      Em.Object.create({
+        isMaster: true
+      })
+    ]),
+    configTypes: {
+      site3 : []
+    },
+    isHiddenOnSelectServicePage: false
+  }),
+  Em.Object.create({
+    serviceName: 's3',
+    isSelected: true,
+    isInstalled: false,
+    displayNameOnSelectServicePage: 's03',
+    serviceComponents: Em.A([
+      Em.Object.create({
+        isHAComponentOnly: true
+      })
+    ]),
+    configTypes: {},
+    isHiddenOnSelectServicePage: false
+  }),
+  Em.Object.create({
+    serviceName: 's4',
+    isSelected: true,
+    isInstalled: false,
+    displayNameOnSelectServicePage: 's03',
+    isClientOnlyService: true,
+    serviceComponents: Em.A([
+      Em.Object.create({
+        isClient: true
+      })
+    ]),
+    configTypes: {},
+    isHiddenOnSelectServicePage: false
+  })
 ]);
 
 var getStacks = function () {
@@ -167,8 +167,6 @@ describe('App.WizardStep8Controller', function () {
     installerStep8Controller = getController();
   });
 
-  App.TestAliases.testAsComputedFilterBy(getController(), 'installedServices', 'content.services', 'isInstalled', true);
-
   App.TestAliases.testAsComputedEqual(getController(), 'isManualKerberos', 'App.router.mainAdminKerberosController.kdc_type', 'none');
 
   App.TestAliases.testAsComputedAlias(getController(), 'clusterName', 'content.cluster.name', 'string');
@@ -2423,11 +2421,16 @@ describe('App.WizardStep8Controller', function () {
        installerStep8Controller.set('content.services', services.filterProperty('isSelected'));
        installerStep8Controller.set('content.hosts', hosts);
        installerStep8Controller.set('content.configGroups', configGroups);
-       installerStep8Controller.set('selectedServices', services.filterProperty('isSelected'));
+       sinon.stub(App.StackService, 'find', function () {
+         return services.filterProperty('isSelected');
+       });
        sinon.spy(installerStep8Controller, 'getConfigurationDetailsForConfigType');
        sinon.spy(installerStep8Controller, 'hostInExistingHostGroup');
        sinon.spy(installerStep8Controller, 'hostInChildHostGroup');
      });
+     afterEach(function () {
+       App.StackService.find.restore();
+     });
      it('should call generateBlueprint', function() {
        installerStep8Controller.generateBlueprint();
        expect(installerStep8Controller.hostInExistingHostGroup.calledAfter(installerStep8Controller.getConfigurationDetailsForConfigType)).to.be.true;

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/views/wizard/step2_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/wizard/step2_view_test.js b/ambari-web/test/views/wizard/step2_view_test.js
index 9393968..caadb48 100644
--- a/ambari-web/test/views/wizard/step2_view_test.js
+++ b/ambari-web/test/views/wizard/step2_view_test.js
@@ -20,14 +20,15 @@ var App = require('app');
 require('views/wizard/step2_view');
 
 var view, controller = Em.Object.create({
-  clusterNameError: ''
+  clusterNameError: '',
+  loadStep: Em.K
 });
 
 function getView() {
   return App.WizardStep2View.create({'controller': controller});
 }
 
-describe('App.WizardStep0View', function () {
+describe('App.WizardStep2View', function () {
 
   beforeEach(function() {
     view = getView();
@@ -162,4 +163,4 @@ describe('App.WizardStep0View', function () {
 
   });
 
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/views/wizard/step6_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/wizard/step6_view_test.js b/ambari-web/test/views/wizard/step6_view_test.js
index 7a4dc9f..4f349e8 100644
--- a/ambari-web/test/views/wizard/step6_view_test.js
+++ b/ambari-web/test/views/wizard/step6_view_test.js
@@ -24,8 +24,10 @@ require('views/wizard/step6_view');
 var view;
 
 function getView() {
+  var controller = App.WizardStep6Controller.create();
+  controller.set('wizardController', App.InstallerController.create());
   return App.WizardStep6View.create({
-    controller: App.WizardStep6Controller.create()
+    controller: controller
   });
 }
 
@@ -115,6 +117,7 @@ describe('App.WizardStep6View', function() {
     beforeEach(function() {
       sinon.stub(view.get('controller'), 'checkCallback', Em.K);
       sinon.stub(view.get('controller'), 'callValidation', Em.K);
+
       e = {
         context: {
           checked: true,


[2/2] ambari git commit: AMBARI-22420 - Enable mpack version selection (Jason Golieb via jonathanhurley)

Posted by jo...@apache.org.
AMBARI-22420 - Enable mpack version selection (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/4087a117
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/4087a117
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/4087a117

Branch: refs/heads/branch-feature-AMBARI-14714-ui
Commit: 4087a11790b76c05898935e9689a377399e13bec
Parents: 0d39eb4
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Mon Nov 13 13:31:05 2017 -0500
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Mon Nov 13 13:31:05 2017 -0500

----------------------------------------------------------------------
 .gitignore                                      |   1 +
 ambari-web/app/app.js                           |   3 +-
 ambari-web/app/controllers/installer.js         | 221 ++++---
 .../app/controllers/main/host/add_controller.js |   2 +-
 .../controllers/main/service/add_controller.js  |   4 +-
 ambari-web/app/controllers/wizard.js            |   8 +-
 .../wizard/configureDownload_controller.js      |   9 +-
 .../wizard/downloadProducts_controller.js       |  45 +-
 .../wizard/selectMpacks_controller.js           | 167 +++--
 .../app/controllers/wizard/step2_controller.js  |  20 +-
 .../app/controllers/wizard/step3_controller.js  |   9 +
 .../app/controllers/wizard/step5_controller.js  |  10 +
 .../app/controllers/wizard/step6_controller.js  |  14 +
 .../app/controllers/wizard/step7_controller.js  |   2 +-
 .../app/controllers/wizard/step8_controller.js  |  13 +-
 ambari-web/app/messages.js                      |   6 +-
 .../mixins/wizard/assign_master_components.js   |  30 +-
 ambari-web/app/routes/installer.js              | 154 +++--
 .../common/assign_master_components.hbs         |   4 +
 .../app/templates/wizard/selectMpacks.hbs       |   6 +-
 .../app/templates/wizard/selectMpacks/mpack.hbs |  14 +-
 .../selectMpacks/selectedMpackVersion.hbs       |   2 +-
 ambari-web/app/templates/wizard/step2.hbs       |   3 +
 ambari-web/app/templates/wizard/step3.hbs       |   5 +-
 ambari-web/app/templates/wizard/step6.hbs       |   3 +
 ambari-web/app/views.js                         |   2 -
 .../app/views/wizard/selectMpacks/mpack_view.js |  31 -
 .../selectMpacks/selectedMpackVersion_view.js   |  27 -
 .../app/views/wizard/selectMpacks_view.js       |  53 +-
 ambari-web/app/views/wizard/step2_view.js       |   1 +
 ambari-web/app/views/wizard/step6_view.js       |   1 +
 ambari-web/test/controllers/installer_test.js   | 110 +++-
 .../main/host/add_controller_test.js            |   2 +-
 .../main/service/add_controller_test.js         |   2 +-
 .../controllers/wizard/selectMpacks_test.js     | 633 ++++++++++++++++++-
 .../test/controllers/wizard/step5_test.js       |   1 +
 .../test/controllers/wizard/step6_test.js       |  13 +-
 .../test/controllers/wizard/step8_test.js       | 127 ++--
 ambari-web/test/views/wizard/step2_view_test.js |   7 +-
 ambari-web/test/views/wizard/step6_view_test.js |   5 +-
 40 files changed, 1364 insertions(+), 406 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index a40e61a..50b7ecc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.ignore
 .classpath
 .project
 .settings

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/app.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/app.js b/ambari-web/app/app.js
index 2526baa..8c79b79 100644
--- a/ambari-web/app/app.js
+++ b/ambari-web/app/app.js
@@ -373,7 +373,8 @@ module.exports = Em.Application.create({
 
     addableMasterInstallerWizard: function () {
       return App.StackServiceComponent.find().filterProperty('isMasterAddableInstallerWizard').mapProperty('componentName')
-    }.property('App.router.clusterController.isLoaded'),
+    }.property(),
+    //.property('App.router.clusterController.isLoaded'),
 
     multipleMasters: function () {
       return App.StackServiceComponent.find().filterProperty('isMasterWithMultipleInstances').mapProperty('componentName')

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js
index 5dd0941..14ae790 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -82,7 +82,12 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     controllerName: 'installerController',
     mpacks: [],
     mpackVersions: [],
-    mpackServices: []
+    mpackServices: [],
+    // 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
   }),
 
   /**
@@ -98,7 +103,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     'installOptions',
     'allHostNamesPattern',
     'serviceComponents',
-    'clientInfo',
+    'clients',
     'selectedServiceNames',
     'serviceConfigGroups',
     'serviceConfigProperties',
@@ -117,7 +122,8 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     'selectedMpacks',
     'selectedServices',
     'selectedStack',
-    'downloadConfig'
+    'downloadConfig',
+    'stepsSavedState'
   ],
 
   init: function () {
@@ -585,11 +591,16 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
    * Load master component hosts data for using in required step controllers
    * @param inMemory {Boolean}: Load master component hosts from memory
    */
-  loadMasterComponentHosts: function (inMemory) {
-    var props = this.getDBProperties(['masterComponentHosts', 'hosts']);
-    var masterComponentHosts = !!inMemory ? this.get("content.masterComponentHosts") : props.masterComponentHosts,
-      hosts = props.hosts || {},
-      hostNames = Em.keys(hosts);
+  loadMasterComponentHosts: function (lookInMemoryOnly) {
+    var props = this.getDBProperties(['masterComponentHosts', 'hosts']),
+        masterComponentHosts = this.get("content.masterComponentHosts"),
+        hosts = props.hosts || {},
+        hostNames = Em.keys(hosts);
+
+    if (!lookInMemoryOnly && !masterComponentHosts) {
+      masterComponentHosts = props.masterComponentHosts;
+    }
+
     if (Em.isNone(masterComponentHosts)) {
       masterComponentHosts = [];
     } else {
@@ -652,7 +663,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         });
       }, this);
     }, this);
-    this.setDBProperty('clientInfo', clients);
+    this.setDBProperty('clients', clients);
     this.set('content.clients', clients);
   },
 
@@ -1021,6 +1032,7 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
       {
         type: 'sync',
         callback: function () {
+          this.load('stepsSavedState');
           this.load('cluster');
         }
       }
@@ -1035,68 +1047,21 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
     ],
     'configureDownload': [
       {
-        type: 'async',
+        type: 'sync',
         callback: function () {
-          var dfd = $.Deferred();
-
-          this.loadStacks().done(function(stacksLoaded) {
-            App.router.get('clusterController').loadAmbariProperties().always(function() {
-              dfd.resolve(stacksLoaded);
-            });
-          });
-
-          return dfd.promise();
+          this.load('downloadConfig');
         }
       },
-      // {
-      //   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();
-    //     }
-    //   }
-    // ],
+    'selectMpacks': [
+      {
+        type: 'sync',
+        callback: function () {
+          this.load('selectedServices');
+          this.load('selectedMpacks');
+        }
+      }
+    ],
     'step3': [
       {
         type: 'sync',
@@ -1105,19 +1070,17 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
         }
       }
     ],
-    // 'step4': [
-    //   {
-    //     type: 'async',
-    //     callback: function () {
-    //       return this.loadServices();
-    //     }
-    //   }
-    // ],
     'step5': [
       {
+        type: 'async',
+        callback: function () {
+          return this.finishRegisteringMpacks(this.getStepSavedState('step5'));
+        }
+      },
+      {
         type: 'sync',
         callback: function () {
-          this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), 6); //TODO: something needs to be changed here
+          this.setSkipSlavesStep(App.StackService.find().filterProperty('isSelected'), this.getStepIndex('step7'));
           this.loadMasterComponentHosts();
           this.loadConfirmedHosts();
           this.loadComponentsFromConfigs();
@@ -1153,6 +1116,14 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
           return dfd.promise();
         }
       }
+    ],
+    'step8': [
+      {
+        type: 'sync',
+        callback: function () {
+          this.load('selectedStack');
+        }
+      }
     ]
   },
 
@@ -1364,6 +1335,104 @@ App.InstallerController = App.WizardController.extend(App.Persist, {
       }
     }
     sCallback();
+  },
+
+  clearStackServices: function () {
+    const stackServices = App.StackService.find();
+    stackServices.forEach(service => {
+      Em.run.once(this, () => App.MpackServiceMapper.deleteRecord(service));
+    });
+  },
+
+  getStepSavedState: function (stepName) {
+    const stepIndex = this.getStepIndex(stepName);
+    const stepsSaved = this.get('content.stepsSavedState');
+
+    if (!!stepIndex && stepsSaved && stepsSaved[stepIndex]) {
+      return true;
+    }
+
+    return false;
+  },
+
+  setStepUnsaved: function (stepName) {
+    const stepIndex = this.getStepIndex(stepName);
+    const oldState = this.get('content.stepsSavedState') || {};
+    const newState = Em.Object.create(oldState);
+    newState[stepIndex] = false;
+
+    this.set('content.stepsSavedState', newState);
+    this.save('stepsSavedState');
+  },
+
+  /**
+   * 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.
+   */
+  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');
+    }
+  },
+
+  /**
+   * This runs when Step5 loads and completes the mpack registration process that was begun in the Download Mpacks step.
+   * It populates the StackService model from the stack version definitions.
+   * Then, it persists info about the selected services and the selected stack.
+   *
+   * @return {object}  a promise
+   */
+  finishRegisteringMpacks: function (keepStackServices) {
+    return this.getMpackStackVersions().then(data => {
+      data.items.forEach(versionDefinition => App.stackMapper.map(versionDefinition))
+
+      if (!keepStackServices) {
+        this.clearStackServices();
+      }
+
+      //get info about services from specific stack versions and save to StackService model
+      const selectedServices = this.get('content.selectedServices');
+      const servicePromises = selectedServices.map(service => this.loadMpackServiceInfo(service.stackName, service.stackVersion, service.name));
+
+      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'),
+              display_name: clientComponent.get('displayName'),
+              isInstalled: false
+            });
+          });
+        });
+        this.set('content.clients', clients);
+        this.save('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];
+        this.set('content.selectedStack', { name: selectedService.stackName, version: selectedService.stackVersion });
+        this.save('selectedStack');
+      });
+    });
   }
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/main/host/add_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/add_controller.js b/ambari-web/app/controllers/main/host/add_controller.js
index 6b158c1..bd528f2 100644
--- a/ambari-web/app/controllers/main/host/add_controller.js
+++ b/ambari-web/app/controllers/main/host/add_controller.js
@@ -223,7 +223,7 @@ App.AddHostController = App.WizardController.extend({
     var serviceComponents = App.StackServiceComponent.find();
     var services = this.get('content.services').filterProperty('isInstallable').filterProperty('isSelected');
     var clients = this.getClientsToInstall(services, serviceComponents);
-    this.setDBProperty('clientInfo', clients);
+    this.setDBProperty('clients', clients);
     this.set('content.clients', clients);
   },
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/main/service/add_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/add_controller.js b/ambari-web/app/controllers/main/service/add_controller.js
index c11bcf2..0399a48 100644
--- a/ambari-web/app/controllers/main/service/add_controller.js
+++ b/ambari-web/app/controllers/main/service/add_controller.js
@@ -378,7 +378,7 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, {
       }, this);
     }, this);
 
-    this.setDBProperty('clientInfo', clients);
+    this.setDBProperty('clients', clients);
     this.set('content.clients', clients);
   },
 
@@ -386,7 +386,7 @@ App.AddServiceController = App.WizardController.extend(App.AddSecurityConfigs, {
    * Load information about hosts with clients components
    */
   loadClients: function () {
-    var clients = this.getDBProperty('clientInfo');
+    var clients = this.getDBProperty('clients');
     if (clients) {
       this.set('content.clients', clients);
     } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/wizard.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index 540cc63..c5a07ff 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -319,14 +319,14 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
       return false;
     }
 
-    if ((this.get('currentStep') - step) > 1 && !disableNaviWarning) {
+    if ((this.get('currentStep') - step) > 0 && !disableNaviWarning) {
       App.ModalPopup.show({
         header: Em.I18n.t('installer.navigation.warning.header'),
         onPrimary: function () {
           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"
+        body: Em.I18n.t('installer.navigation.warning')
       });
     } else {
       App.router.send('goto' + stepName.capitalize());
@@ -1257,7 +1257,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
    * Load information about hosts with clients components
    */
   loadClients: function () {
-    var clients = this.getDBProperty('clientInfo');
+    var clients = this.getDBProperty('clients');
     this.set('content.clients', clients);
   },
 
@@ -1374,7 +1374,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
   },
 
   /**
-   * Determine if <code>Assign Slaves and Clients</code> step should be skipped
+   * Determine if <code>Assign Slaves and Clients</code> step ("step7") should be skipped
    * @method setSkipSlavesStep
    * @param services
    * @param step

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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 f3a8b1b..0246ad0 100644
--- a/ambari-web/app/controllers/wizard/configureDownload_controller.js
+++ b/ambari-web/app/controllers/wizard/configureDownload_controller.js
@@ -49,8 +49,9 @@ App.WizardConfigureDownloadController = Em.Controller.extend({
   },
 
   loadStep: function () {
-    if (!this.get('content.downloadConfig')) {
-      let downloadConfig = this.get('wizardController').getDBProperty('downloadConfig');
+    let downloadConfig = this.get('content.downloadConfig');
+    //if (!this.get('content.downloadConfig')) {
+      //let downloadConfig = this.get('wizardController').getDBProperty('downloadConfig');
 
       if (!downloadConfig) {
         downloadConfig = {
@@ -60,7 +61,7 @@ App.WizardConfigureDownloadController = Em.Controller.extend({
       }
 
       this.set('content.downloadConfig', downloadConfig);
-    }
+    //}
   },
 
   /**
@@ -90,7 +91,7 @@ App.WizardConfigureDownloadController = Em.Controller.extend({
       return;
     }
 
-    this.get('wizardController').setDBProperty('downloadConfig', this.get('content.downloadConfig'));
+    //this.get('wizardController').setDBProperty('downloadConfig', this.get('content.downloadConfig'));
 
     App.router.send('next');
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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 165debb..b353732 100644
--- a/ambari-web/app/controllers/wizard/downloadProducts_controller.js
+++ b/ambari-web/app/controllers/wizard/downloadProducts_controller.js
@@ -24,7 +24,7 @@ App.WizardDownloadProductsController = Em.Controller.extend({
   mpacks: [],
 
   addMpacks: function () {
-    const selectedMpacks = this.get('content.selectedMpacks') || this.get('wizardController').getDBProperty('selectedMpacks');
+    const selectedMpacks = this.get('content.selectedMpacks');
 
     selectedMpacks.forEach(mpack => {
       this.get('mpacks').pushObject(Em.Object.create({
@@ -95,14 +95,7 @@ App.WizardDownloadProductsController = Em.Controller.extend({
     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;
     }
@@ -121,41 +114,7 @@ App.WizardDownloadProductsController = Em.Controller.extend({
         //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');
-            });
-          });
+          App.router.send('next');
         });
       });
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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
index 9ab5a12..a7c62ff 100644
--- a/ambari-web/app/controllers/wizard/selectMpacks_controller.js
+++ b/ambari-web/app/controllers/wizard/selectMpacks_controller.js
@@ -22,19 +22,15 @@ App.WizardSelectMpacksController = Em.Controller.extend({
 
   name: 'wizardSelectMpacksController',
 
-  //mpacks: Em.computed.alias('content.mpacks'),
-
-  getMpacks: function () {
-    App.ajax.send({
+  loadRegistry: function () {
+    return App.ajax.send({
       name: 'registry.mpacks.versions',
       showLoadingPopup: true,
-      sender: this,
-      success: 'getMpacksSucceeded',
-      error: 'getMpacksFailed'
+      sender: this
     });
   },
 
-  getMpacksSucceeded: function (data) {
+  loadRegistrySucceeded: function (data) {
     const mpacks = data.items.reduce(
       (mpacks, registry) => mpacks.concat(
         registry.mpacks.map(mpack => {
@@ -42,9 +38,10 @@ App.WizardSelectMpacksController = Em.Controller.extend({
             name: mpack.RegistryMpackInfo.mpack_name,
             description: mpack.RegistryMpackInfo.mpack_description,
             logoUrl: mpack.RegistryMpackInfo.mpack_logo_url,
-            versions: mpack.versions ? mpack.versions.map(version => {
+            versions: mpack.versions ? mpack.versions.map((version, index) => {
               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,
                 version: version.RegistryMpackVersionInfo.mpack_version,
                 docUrl: version.RegistryMpackVersionInfo.mpack_dock_url,
@@ -89,19 +86,56 @@ App.WizardSelectMpacksController = Em.Controller.extend({
     this.set('content.mpackServices', mpackServices);
   },
 
-  getMpacksFailed: function () {
+  isSaved: function () {
+    const wizardController = this.get('wizardController');
+    if (wizardController) {
+      return wizardController.getStepSavedState('selectMpacks');
+    }
+    return false;
+  }.property('wizardController.content.stepsSavedState'),
+
+  loadRegistryFailed: function () {
     this.set('content.mpacks', []);
+
+    App.showAlertPopup(
+      Em.I18n.t('common.error'), //header
+      Em.I18n.t('installer.selectMpacks.loadRegistryFailed') //body
+    );
   },
 
-  loadStep: function () {
+  getRegistry: function () {
+    const deferred = $.Deferred();
+
     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();
+      this.loadRegistry().then(registry => {
+        this.loadRegistrySucceeded(registry);
+        deferred.resolve();
+      },
+      () => {
+        this.loadRegistryFailed();
+        deferred.reject();
+      });
+    } else {
+      deferred.resolve();
     }
+
+    return deferred.promise();
+  },
+
+  loadStep: function () {
+    this.getRegistry().then(() => {
+      //add previously selected services
+      const selectedServices = this.get('content.selectedServices');
+      if (selectedServices) {
+        selectedServices.forEach(service => {
+          this.addService(service.id);
+        });
+      }
+    });
   },
 
   isSubmitDisabled: function () {
@@ -109,37 +143,68 @@ App.WizardSelectMpacksController = Em.Controller.extend({
     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;
+  getServiceById: function (serviceId) {
     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);
+    return service;
   },
 
-  removeService: function (event) {
-    const serviceId = event.context;
-    const mpackServices = this.get('content.mpackServices');
-    const byServiceId = service => service.id === serviceId;
+  getMpackVersionById: function (versionId) {
+    const mpackVersions = this.get('content.mpackVersions');
+    const byVersionId = version => version.id === versionId;
+    const version = mpackVersions.find(byVersionId);
+    return version;
+  },
 
-    const service = mpackServices.find(byServiceId);
-    service.set('selected', false);
+  displayMpackVersion: function (versionId) {
+    const version = this.getMpackVersionById(versionId);
+
+    if (version) {
+      version.mpack.versions.forEach(mpackVersion => {
+        if (mpackVersion.get('id') === versionId) {
+          mpackVersion.set('displayed', true);
+        } else {
+          mpackVersion.set('displayed', false);
+        }
+      })
+    }
+  },
+
+  addServiceHandler: function (serviceId) {
+    if (this.addService(serviceId)) {
+      this.get('wizardController').setStepUnsaved('selectMpacks');
+    }
+  },
+
+  addService: function (serviceId) {
+    const service = this.getServiceById(serviceId);
+
+    if (service) {
+      service.set('selected', true);
+      service.set('mpackVersion.selected', true);
+      return true;
+    }
+
+    return false;
+  },
+
+  removeServiceHandler: function (serviceId) {
+    if (this.removeService(serviceId)) {
+      this.get('wizardController').setStepUnsaved('selectMpacks');
+    }
+  },
 
-    service.set('mpackVersion.selected', service.get('mpackVersion.services').some(s => s.get('selected') === true));
+  removeService: function (serviceId) {
+    const service = this.getServiceById(serviceId);
+
+    if (service) {
+      service.set('selected', false);
+      service.set('mpackVersion.selected', service.get('mpackVersion.services').some(s => s.get('selected') === true));
+      return true;
+    }
+
+    return false;
   },
 
   selectedServices: function () {
@@ -157,21 +222,32 @@ App.WizardSelectMpacksController = Em.Controller.extend({
     return versions ? versions.some(v => v.get('selected') === true) : false;
   }.property('content.mpackVersions.@each.selected', 'selectedServices'),
 
+  clearSelection: function () {
+    const mpackServices = this.get('content.mpackServices');
+    if (mpackServices) {
+      mpackServices.setEach('selected', false);
+    }
+
+    const versions = this.get('content.mpackVersions');
+    if (versions) {
+      versions.setEach('selected', false);
+    }
+  },
+
   /**
    * 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')) {
+    if (App.get('router.nextBtnClickInProgress')) {
       return;
     }
 
     if (!this.get('isSubmitDisabled')) {
       const selectedServices = this.get('selectedServices').map(service =>
         ({
+          id: service.id,
           name: service.name,
           mpackName: service.mpackVersion.name,
           mpackVersion: service.mpackVersion.version,
@@ -179,14 +255,12 @@ App.WizardSelectMpacksController = Em.Controller.extend({
           stackVersion: service.mpackVersion.stackVersion
         })
       );
-      self.set('content.selectedServices', selectedServices);
-      self.get('wizardController').setDBProperty('selectedServices', selectedServices);
+      this.set('content.selectedServices', selectedServices);
 
       const selectedServiceNames = selectedServices.map(service => service.name);
-      self.set('content.selectedServiceNames', selectedServiceNames);
-      self.get('wizardController').setDBProperty('selectedServiceNames', selectedServiceNames);
+      this.set('content.selectedServiceNames', selectedServiceNames);
 
-      const selectedMpacks = self.get('selectedMpackVersions').map(mpackVersion =>
+      const selectedMpacks = this.get('selectedMpackVersions').map(mpackVersion =>
         ({
           name: mpackVersion.mpack.name,
           displayName: mpackVersion.mpack.displayName,
@@ -194,8 +268,7 @@ App.WizardSelectMpacksController = Em.Controller.extend({
           version: mpackVersion.version
         })
       );
-      self.set('content.selectedMpacks', selectedMpacks);
-      self.get('wizardController').setDBProperty('selectedMpacks', selectedMpacks);
+      this.set('content.selectedMpacks', selectedMpacks);
 
       App.router.send('next');
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/wizard/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step2_controller.js b/ambari-web/app/controllers/wizard/step2_controller.js
index 05813e2..dffc17e 100644
--- a/ambari-web/app/controllers/wizard/step2_controller.js
+++ b/ambari-web/app/controllers/wizard/step2_controller.js
@@ -115,6 +115,14 @@ App.WizardStep2Controller = Em.Controller.extend({
    */
   hostsError: null,
 
+  isSaved: function () {
+    const wizardController = this.get('wizardController');
+    if (wizardController) {
+      return wizardController.getStepSavedState('step2');
+    }
+    return false;
+  }.property('wizardController.content.stepsSavedState'),
+
   useSSH: function () {
     return !App.get('isHadoopWindowsStack');
   }.property('App.isHadoopWindowsStack'),
@@ -169,6 +177,11 @@ App.WizardStep2Controller = Em.Controller.extend({
    */
   isSubmitDisabled: Em.computed.or('hostsError', 'sshKeyError', 'sshUserError', 'sshPortError', 'agentUserError', 'App.router.btnClickInProgress'),
 
+  loadStep: function () {
+    //save initial hostNames value to check later if changes were made
+    this.set('initialHostNames', this.get('content.installOptions.hostNames'));
+  },
+
   installedHostNames: function () {
     var installedHostsName = [];
     var hosts = this.get('content.hosts');
@@ -313,7 +326,7 @@ App.WizardStep2Controller = Em.Controller.extend({
 
   /**
    * check is there a pattern expression in host name textarea
-   * push hosts that match pattern in hostNamesArr
+   * push hosts that match pattern in hostNameArr
    * @method parseHostNamesAsPatternExpression
    */
   parseHostNamesAsPatternExpression: function () {
@@ -546,6 +559,10 @@ App.WizardStep2Controller = Em.Controller.extend({
    * @method saveHosts
    */
   saveHosts: function () {
+    if (this.get('content.installOptions.hostNames') !== this.get('initialHostNames')) {
+        this.get('wizardController').setStepUnsaved('step2');
+    }
+
     var hosts = this.get('content.hosts');
 
     //add previously installed hosts
@@ -555,6 +572,7 @@ App.WizardStep2Controller = Em.Controller.extend({
       }
     }
 
+    //this.set('content.installOptions.hostNames', this.get('hostNameArr'));
     this.set('content.hosts', $.extend(hosts, this.getHostInfo()));
     this.setAmbariJavaHome();
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/wizard/step3_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step3_controller.js b/ambari-web/app/controllers/wizard/step3_controller.js
index 2c61e02..36bf05a 100644
--- a/ambari-web/app/controllers/wizard/step3_controller.js
+++ b/ambari-web/app/controllers/wizard/step3_controller.js
@@ -75,6 +75,14 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check
     return (this.get('isRegistrationInProgress') || !this.get('isWarningsLoaded')) && !this.get('isBootstrapFailed') || App.get('router.btnClickInProgress');
   }.property('isRegistrationInProgress', 'isWarningsLoaded', 'isBootstrapFailed'),
 
+  isSaved: function () {
+    const wizardController = this.get('wizardController');
+    if (wizardController) {
+      return wizardController.getStepSavedState('step3');
+    }
+    return false;
+  }.property('wizardController.content.stepsSavedState'),
+
   /**
    * Controller is using in Add Host Wizard
    * @return {bool}
@@ -305,6 +313,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, App.Check
       if (!self.hosts.length) {
         self.set('isSubmitDisabled', true);
       }
+      self.get('wizardController').setStepUnsaved('step3');
     }, Em.I18n.t('installer.step3.hosts.remove.popup.body'));
   },
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/wizard/step5_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step5_controller.js b/ambari-web/app/controllers/wizard/step5_controller.js
index 8736652..3ca4165 100644
--- a/ambari-web/app/controllers/wizard/step5_controller.js
+++ b/ambari-web/app/controllers/wizard/step5_controller.js
@@ -22,6 +22,16 @@ App.WizardStep5Controller = Em.Controller.extend(App.BlueprintMixin, App.AssignM
 
   name: "wizardStep5Controller",
 
+  stepName: 'step5',
+
+  isSaved: function () {
+    const wizardController = this.get('wizardController');
+    if (wizardController) {
+      return wizardController.getStepSavedState(this.get('stepName'));
+    }
+    return false;
+  }.property('wizardController.content.stepsSavedState'),
+
   _goNextStepIfValid: function () {
     App.set('router.nextBtnClickInProgress', false);
     if (!this.get('submitDisabled')) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/controllers/wizard/step6_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step6_controller.js b/ambari-web/app/controllers/wizard/step6_controller.js
index 68ec3ed..b1941f0 100644
--- a/ambari-web/app/controllers/wizard/step6_controller.js
+++ b/ambari-web/app/controllers/wizard/step6_controller.js
@@ -95,6 +95,18 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi
    */
   isInstallerWizard: Em.computed.equal('content.controllerName', 'installerController'),
 
+  isSaved: function () {
+    const wizardController = this.get('wizardController');
+    if (wizardController) {
+      return wizardController.getStepSavedState('step6');
+    }
+    return false;
+  }.property('wizardController.content.stepsSavedState'),
+
+  hostsChanged: function () {
+    this.get('wizardController').setStepUnsaved('step6');
+  },
+
   isAllCheckboxesEmpty: function() {
     var hosts = this.get('hosts');
     for (var i = 0; i < hosts.length; i++) {
@@ -267,6 +279,7 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi
       });
     });
     this.checkCallback(component);
+    this.hostsChanged();
   },
 
   /**
@@ -301,6 +314,7 @@ App.WizardStep6Controller = Em.Controller.extend(App.HostComponentValidationMixi
    */
   loadStep: function () {
     this.clearStep();
+
     var parentController = App.router.get(this.get('content.controllerName'));
     if (parentController && parentController.get('content.componentsFromConfigs')) {
       parentController.clearConfigActionComponents();

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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 3def063..4b1eacb 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -477,9 +477,9 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
     if (!this.get('isConfigsLoaded')) {
       return;
     }
+
     console.time('wizard loadStep: ');
     this.clearStep();
-
     var self = this;
     App.config.setPreDefinedServiceConfigs(this.get('addMiscTabToPage'));
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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 0c72295..3ad65f1 100644
--- a/ambari-web/app/controllers/wizard/step8_controller.js
+++ b/ambari-web/app/controllers/wizard/step8_controller.js
@@ -151,14 +151,9 @@ App.WizardStep8Controller = Em.Controller.extend(App.AddSecurityConfigs, App.wiz
   }.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;
+    const selectedStack = this.get('content.selectedStack');
+    const stack = this.get('wizardController').getStack(selectedStack.name, selectedStack.version);
+    return stack;
   },
 
   installedServices: function() {
@@ -651,7 +646,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); //TODO: something
+          wizardController.setSkipSlavesStep(wizardController.getDBProperty('selectedServiceNames'), 3);
         }
       });
     } else {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 82adc80..c9bae25 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -584,7 +584,9 @@ Em.I18n.translations = {
 
   'installer.header':'Cluster Install Wizard',
   'installer.navigation.warning.header':'Navigation Warning',
-
+  'installer.navigation.warning':'If you make changes to a previous step you will lose any changes saved in subsequent steps.',
+  'installer.warning.changes.header':'Warning',
+  'installer.warning.changes':'If you make changes to this step you will lose any changes saved in subsequent steps.',
   'installer.noHostsAssigned':'No host assigned',
   'installer.slaveComponentHosts.selectHosts':'select hosts for this group',
   'installer.slaveComponentHostsPopup.header':'Select which hosts should belong to which {0} group',
@@ -597,7 +599,7 @@ 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.loadRegistryFailed': '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.',

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/mixins/wizard/assign_master_components.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/wizard/assign_master_components.js b/ambari-web/app/mixins/wizard/assign_master_components.js
index 84a56f1..c547b5a 100644
--- a/ambari-web/app/mixins/wizard/assign_master_components.js
+++ b/ambari-web/app/mixins/wizard/assign_master_components.js
@@ -675,7 +675,7 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
    */
   createComponentInstallationObjects: function() {
     var stackMasterComponentsMap = {},
-        masterHosts = this.get('content.masterComponentHosts') || this.get('masterComponentHosts'), //saved to local storage info
+        masterHosts = this.get('content.masterComponentHosts'),
         servicesToAdd = (this.get('content.services')|| []).filterProperty('isSelected').filterProperty('isInstalled', false).mapProperty('serviceName'),
         recommendations = this.get('recommendations'),
         resultComponents = [],
@@ -955,13 +955,23 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
   assignHostToMaster: function (componentName, selectedHost, serviceComponentId) {
     var flag = this.isHostNameValid(componentName, selectedHost);
     var component;
+    const stepName = this.get('stepName');
+
     this.updateIsHostNameValidFlag(componentName, serviceComponentId, flag);
+
     if (serviceComponentId) {
       component = this.get('selectedServicesMasters').filterProperty('component_name', componentName).findProperty("serviceComponentId", serviceComponentId);
-      if (component) component.set("selectedHost", selectedHost);
-    }
-    else {
+      if (component) {
+        component.set("selectedHost", selectedHost);
+        if (stepName) {
+          this.get('wizardController').setStepUnsaved(stepName);
+        }
+      }
+    } else {
       this.get('selectedServicesMasters').findProperty("component_name", componentName).set("selectedHost", selectedHost);
+      if (stepName) {
+        this.get('wizardController').setStepUnsaved(stepName);
+      }
     }
   },
 
@@ -1093,6 +1103,12 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
       });
       this.incrementProperty('rebalanceComponentHostsCounter');
       this.toggleProperty('hostNameCheckTrigger');
+
+      const stepName = this.get('stepName');
+      if (stepName) {
+        this.get('wizardController').setStepUnsaved(stepName);
+      }
+
       return true;
     }
     return false;//if no more zookeepers can be added
@@ -1130,6 +1146,12 @@ App.AssignMasterComponents = Em.Mixin.create(App.HostComponentValidationMixin, A
     });
     this.incrementProperty('rebalanceComponentHostsCounter');
     this.toggleProperty('hostNameCheckTrigger');
+
+    const stepName = this.get('stepName');
+    if (stepName) {
+      this.get('wizardController').setStepUnsaved(stepName);
+    }
+    
     return true;
   },
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/routes/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/installer.js b/ambari-web/app/routes/installer.js
index 5899ff3..9a49c36 100644
--- a/ambari-web/app/routes/installer.js
+++ b/ambari-web/app/routes/installer.js
@@ -106,14 +106,14 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
 
     next: function (router) {
       console.time('step0 next');
-      var installerController = router.get('installerController');
-      installerController.save('cluster');
+      var controller = router.get('installerController');
+      controller.save('cluster');
       App.db.setStacks(undefined);
       App.db.setRepos(undefined);
       App.db.setLocalRepoVDFData(undefined);
       App.Stack.find().clear();
 
-      installerController.set('content.stacks',undefined);
+      controller.set('content.stacks',undefined);
       router.transitionTo('step2');
       console.timeEnd('step0 next');
     }
@@ -188,8 +188,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var controller = router.get('installerController');
       var newStepIndex = controller.getStepIndex('step2');
       router.setNavigationFlow(newStepIndex);
-      controller.clearInstallOptions();
+      //controller.clearInstallOptions();
       controller.setCurrentStep('step2');
+      var wizardStep2Controller = router.get('wizardStep2Controller');
+      wizardStep2Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
         self.scrollTop();
         controller.connectOutlet('wizardStep2', controller.get('content'));
@@ -205,6 +207,7 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         controller.save('installOptions');
         //hosts was saved to content.hosts inside wizardStep2Controller
         controller.save('hosts');
+        controller.setStepSaved('step2');
         router.transitionTo('step3');
       }
       console.timeEnd('step2 next');
@@ -218,9 +221,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var self = this;
       var controller = router.get('installerController');
       controller.setCurrentStep('step3');
+      var wizardStep3Controller = router.get('wizardStep3Controller');
+      wizardStep3Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
-        var wizardStep3Controller = router.get('wizardStep3Controller');
-        wizardStep3Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep3', controller.get('content'));
         self.scrollTop();
         console.timeEnd('step3 connectOutlets');
@@ -235,14 +238,23 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       console.time('step3 next');
       if (!router.get('btnClickInProgress')) {
         App.set('router.nextBtnClickInProgress', true);
-        var installerController = router.get('installerController');
+        var controller = router.get('installerController');
         var wizardStep3Controller = router.get('wizardStep3Controller');
-        installerController.saveConfirmedHosts(wizardStep3Controller);
-        installerController.setDBProperties({
-          bootStatus: true,
-          selectedServiceNames: undefined,
-          installedServiceNames: undefined
-        });
+        controller.saveConfirmedHosts(wizardStep3Controller);
+        if (!wizardStep3Controller.get('isSaved')) {
+          App.router.get('wizardSelectMpacksController').clearSelection();
+          controller.set('content.selectedServices', undefined);
+          controller.set('content.selectedServiceNames', undefined);
+          controller.set('content.selectedMpacks', undefined);
+          controller.setDBProperties({
+            bootStatus: true,
+            selectedServices: undefined,
+            selectedServiceNames: undefined,
+            installedServiceNames: undefined,
+            selectedMpack: undefined
+          });
+        }
+        controller.setStepSaved('step3');
         router.transitionTo('configureDownload');
         console.timeEnd('step3 next');
       }
@@ -268,12 +280,12 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       console.time('configureDownload connectOutlets');
       var self = this;
       var controller = router.get('installerController');
-      var configureDownloadController = router.get('wizardConfigureDownloadController');
       var newStepIndex = controller.getStepIndex('configureDownload');
       router.setNavigationFlow(newStepIndex);
       controller.setCurrentStep('configureDownload');
+      var configureDownloadController = router.get('wizardConfigureDownloadController');
+      configureDownloadController.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
-        configureDownloadController.set('wizardController', controller);
         controller.connectOutlet('wizardConfigureDownload', controller.get('content'));
         self.scrollTop();
         console.timeEnd('configureDownload connectOutlets');
@@ -286,8 +298,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         return;
       }
       App.set('router.nextBtnClickInProgress', true);
-      var installerController = router.get('installerController');
-      installerController.setDBProperty('service', undefined);
+      var controller = router.get('installerController');
+      controller.save('downloadConfig');
+      controller.setDBProperty('service', undefined);
       router.transitionTo('selectMpacks');
       console.timeEnd('configureDownload next');
     }
@@ -301,9 +314,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var self = this;
       var controller = router.get('installerController');
       controller.setCurrentStep('selectMpacks');
+      var wizardSelectMpacksController = router.get('wizardSelectMpacksController');
+      wizardSelectMpacksController.set('wizardController', controller);
       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');
@@ -318,6 +331,22 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       console.time('selectMpacks next');
       if (!router.get('btnClickInProgress')) {
         App.set('router.nextBtnClickInProgress', true);
+        var controller = router.get('installerController');
+        controller.save('selectedServiceNames');
+        controller.save('selectedServices');
+        controller.save('selectedMpacks');
+        var wizardStep6Controller = router.get('wizardStep6Controller');
+        // Clear subsequent settings if user changed service selections
+        if (!wizardStep6Controller.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);
+          wizardStep6Controller.set('isClientsSet', false);
+        }
+        controller.setStepSaved('selectMpacks');
         router.transitionTo('downloadProducts');
         console.timeEnd('selectMpacks next');
       }
@@ -330,12 +359,12 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       console.time('downloadProducts connectOutlets');
       var self = this;
       var controller = router.get('installerController');
-      var configureDownloadController = router.get('wizardDownloadProductsController');
       var newStepIndex = controller.getStepIndex('downloadProducts');
       router.setNavigationFlow(newStepIndex);
       controller.setCurrentStep('downloadProducts');
+      var downloadProductsController = router.get('wizardDownloadProductsController');
+      downloadProductsController.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
-        configureDownloadController.set('wizardController', controller);
         controller.connectOutlet('wizardDownloadProducts', controller.get('content'));
         self.scrollTop();
         console.timeEnd('downloadProducts connectOutlets');
@@ -352,13 +381,6 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         return;
       }
       App.set('router.nextBtnClickInProgress', true);
-      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');
     }
@@ -373,9 +395,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var newStepIndex = controller.getStepIndex('step4');
       router.setNavigationFlow(newStepIndex);
       controller.setCurrentStep('step4');
+      var wizardStep4Controller = router.get('wizardStep4Controller');
+      wizardStep4Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
-        var wizardStep4Controller = router.get('wizardStep4Controller');
-        wizardStep4Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep4', App.StackService.find().filterProperty('isInstallable', true));
         self.scrollTop();
         console.timeEnd('step4 connectOutlets');
@@ -419,9 +441,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         servicesMasters: [],
         isInitialLayout: true
       });
+      wizardStep5Controller.set('wizardController', controller);
       controller.setCurrentStep('step5');
       controller.loadAllPriorSteps().done(function () {
-        wizardStep5Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep5', controller.get('content'));
         self.scrollTop();
         console.timeEnd('step5 connectOutlets');
@@ -436,13 +458,15 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         App.set('router.nextBtnClickInProgress', true);
         var controller = router.get('installerController');
         var wizardStep5Controller = router.get('wizardStep5Controller');
-        var wizardStep6Controller = router.get('wizardStep6Controller');
         controller.saveMasterComponentHosts(wizardStep5Controller);
-        controller.setDBProperties({
-          slaveComponentHosts: undefined,
-          recommendations: wizardStep5Controller.get('content.recommendations')
-        });
-        wizardStep6Controller.set('isClientsSet', false);
+        controller.setDBProperty('recommendations', wizardStep5Controller.get('content.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');
       }
       console.timeEnd('step5 next');
@@ -457,8 +481,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var controller = router.get('installerController');
       var newStepIndex = controller.getStepIndex('step6');
       router.setNavigationFlow(newStepIndex);
-      router.get('wizardStep6Controller').set('hosts', []);
       controller.setCurrentStep('step6');
+      var wizardStep6Controller = router.get('wizardStep6Controller');
+      wizardStep6Controller.set('hosts', []);
+      wizardStep6Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
         controller.connectOutlet('wizardStep6', controller.get('content'));
         self.scrollTop();
@@ -480,13 +506,17 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
             controller.saveSlaveComponentHosts(wizardStep6Controller);
             controller.get('content').set('serviceConfigProperties', null);
             controller.get('content').set('componentsFromConfigs', []);
-            controller.setDBProperties({
-              serviceConfigGroups: null,
-              recommendationsHostGroups: wizardStep6Controller.get('content.recommendationsHostGroups'),
-              recommendationsConfigs: null,
-              componentsFromConfigs: []
-            });
-            controller.clearServiceConfigProperties();
+            // 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();
+            }
+            controller.setStepSaved('step6');
             router.transitionTo('step7');
             console.timeEnd('step6 next');
           }
@@ -511,8 +541,8 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var controller = router.get('installerController');
       router.get('preInstallChecksController').loadStep();
       var wizardStep7Controller = router.get('wizardStep7Controller');
+      wizardStep7Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
-        wizardStep7Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep7', controller.get('content'));
         self.scrollTop();
         console.timeEnd('step7 connectOutlets');
@@ -563,9 +593,9 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       var controller = router.get('installerController');
       var self = this;
       controller.setCurrentStep('step8');
+      var wizardStep8Controller = router.get('wizardStep8Controller');
+      wizardStep8Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
-        var wizardStep8Controller = router.get('wizardStep8Controller');
-        wizardStep8Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep8', controller.get('content'));
         self.scrollTop();
         console.timeEnd('step8 connectOutlets');
@@ -580,13 +610,13 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       console.time('step8 next');
       if (!router.get('btnClickInProgress')) {
         App.set('router.nextBtnClickInProgress', true);
-        var installerController = router.get('installerController');
+        var controller = router.get('installerController');
         var wizardStep8Controller = router.get('wizardStep8Controller');
         // invoke API call to install selected services
-        installerController.installServices(false, function () {
-          installerController.setInfoForStep9();
+        controller.installServices(false, function () {
+          controller.setInfoForStep9();
           // We need to do recovery based on whether we are in Add Host or Installer wizard
-          installerController.saveClusterState('CLUSTER_INSTALLING_3');
+          controller.saveClusterState('CLUSTER_INSTALLING_3');
           wizardStep8Controller.set('servicesInstalled', true);
           router.transitionTo('step9');
           console.timeEnd('step8 next');
@@ -600,15 +630,15 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     connectOutlets: function (router, context) {
       console.time('step9 connectOutlets');
       var self = this;
-      var controller = router.get('installerController'),
-          wizardStep9Controller = router.get('wizardStep9Controller');
+      var controller = router.get('installerController');
+      var wizardStep9Controller = router.get('wizardStep9Controller');
+      wizardStep9Controller.set('wizardController', controller);
       controller.loadAllPriorSteps().done(function () {
         wizardStep9Controller.loadDoServiceChecksFlag().done(function () {
           controller.setCurrentStep('step9');
           if (!App.get('testMode')) {
             controller.setLowerStepsDisable(9);
           }
-          wizardStep9Controller.set('wizardController', controller);
           controller.connectOutlet('wizardStep9', controller.get('content'));
           self.scrollTop();
           console.timeEnd('step9 connectOutlets');
@@ -618,16 +648,16 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     back: Em.Router.transitionTo('step8'),
     retry: function (router) {
       console.time('step9 retry');
-      var installerController = router.get('installerController');
+      var controller = router.get('installerController');
       var wizardStep9Controller = router.get('wizardStep9Controller');
       if (wizardStep9Controller.get('showRetry')) {
         if (wizardStep9Controller.get('content.cluster.status') === 'INSTALL FAILED') {
           var isRetry = true;
-          installerController.installServices(isRetry, function () {
-            installerController.setInfoForStep9();
+          controller.installServices(isRetry, function () {
+            controller.setInfoForStep9();
             wizardStep9Controller.resetHostsForRetry();
             // We need to do recovery based on whether we are in Add Host or Installer wizard
-            installerController.saveClusterState('CLUSTER_INSTALLING_3');
+            controller.saveClusterState('CLUSTER_INSTALLING_3');
             wizardStep9Controller.navigateStep();
           });
         } else {
@@ -649,10 +679,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
       console.time('step9 next');
       if(!router.get('btnClickInProgress')) {
         App.set('router.nextBtnClickInProgress', true);
-        var installerController = router.get('installerController');
+        var controller = router.get('installerController');
         var wizardStep9Controller = router.get('wizardStep9Controller');
-        installerController.saveInstalledHosts(wizardStep9Controller);
-        installerController.saveClusterState('CLUSTER_INSTALLED_4');
+        controller.saveInstalledHosts(wizardStep9Controller);
+        controller.saveClusterState('CLUSTER_INSTALLED_4');
         router.transitionTo('step10');
         console.timeEnd('step9 next');
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/templates/common/assign_master_components.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/assign_master_components.hbs b/ambari-web/app/templates/common/assign_master_components.hbs
index a47e3f6..49eaa10 100644
--- a/ambari-web/app/templates/common/assign_master_components.hbs
+++ b/ambari-web/app/templates/common/assign_master_components.hbs
@@ -22,6 +22,10 @@
   <p class="step-description">
     {{{view.alertMessage}}}
   </p>
+  {{#if isSaved}}
+  <div class="alert alert-warning" role="alert"><strong>{{t installer.warning.changes.header}}</strong> {{t installer.warning.changes}}</div>
+  {{/if}}
+
   <div class="panel panel-default">
     <div class="panel-body">
       {{#each msg in controller.generalErrorMessages}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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
index dd55ff3..19855d3 100644
--- a/ambari-web/app/templates/wizard/selectMpacks.hbs
+++ b/ambari-web/app/templates/wizard/selectMpacks.hbs
@@ -17,14 +17,16 @@
 }}
 <div id="select-mpacks" class="wizard-content col-md-9">
   <h4 class="step-title">{{t installer.selectMpacks.body.header}}</h4>
-
+  {{#if isSaved}}
+  <div class="alert alert-warning" role="alert"><strong>{{t installer.warning.changes.header}}</strong> {{t installer.warning.changes}}</div>
+  {{/if}}
   <div class="display-flex">
+  <!-- Registry -->
   <div class="panel panel-default col-md-8">
     <div class="panel-heading">
       <p>{{t installer.selectMpacks.body.options.header}}</p>
     </div>
     <div class="panel-body">
-      <!-- Registry -->
       <div class="options-list">
         {{#if controller.content.mpacks}}
           {{#each mpack in controller.content.mpacks}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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
index aea598e..9a65baf 100644
--- a/ambari-web/app/templates/wizard/selectMpacks/mpack.hbs
+++ b/ambari-web/app/templates/wizard/selectMpacks/mpack.hbs
@@ -17,14 +17,20 @@
 }}
 <div class="mpack-body">
   <div class="mpack-title" role="tab" id="headingOne">
-    <p>{{mpack.name}} {{view.version}}</p>
+    {{mpack.name}} 
+    <select {{action "changeVersion" on="change" target="view"}}>
+      {{#each ver in mpack.versions}}
+      <option {{bindAttr value="ver.id" selected="ver.displayed"}}>{{ver.version}}</option>
+      {{/each}}
+    </select>
   </div>
+  <div>{{mpack.description}}</div>
   <div>
   {{#each service in view.services}}
-    <button {{bindAttr id="service.name"}} class="capsule" {{action "selectService" service.id target="controller"}}>
-      {{service.name}}&nbsp;<i class="icon icon-plus"></i>
+    <button {{bindAttr id="service.name"}} class="capsule" {{action "addService" service.id target="view"}}>
+      {{service.name}}&nbsp;{{service.version}}&nbsp;<i class="icon icon-plus"></i>
       <span {{bindAttr class="selected:service-remove:service-add"}} {{bindAttr title="service.version"}}></span>
     </button>
   {{/each}}
   </div>
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/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
index cba1177..05e75ff 100644
--- a/ambari-web/app/templates/wizard/selectMpacks/selectedMpackVersion.hbs
+++ b/ambari-web/app/templates/wizard/selectMpacks/selectedMpackVersion.hbs
@@ -22,7 +22,7 @@
   <div>
   {{#each service in mpackVersion.services}}
     {{#if service.selected}}
-      <button {{bindAttr id="service.name"}} class="capsule" {{action "removeService" service.id target="controller"}}>
+      <button {{bindAttr id="service.name"}} class="capsule" {{action "removeService" service.id target="view"}}>
         {{service.name}}&nbsp;<i class="icon">&times;</i>
       <span {{bindAttr class="selected:service-remove:service-add" title="service.version"}}></span>
     </button>

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/templates/wizard/step2.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step2.hbs b/ambari-web/app/templates/wizard/step2.hbs
index b4ee25c..685ff79 100644
--- a/ambari-web/app/templates/wizard/step2.hbs
+++ b/ambari-web/app/templates/wizard/step2.hbs
@@ -19,6 +19,9 @@
 <div id="installOptions" class="wizard-content col-md-9">
   <h4 class="step-header">{{t installer.step2.header}}</h4>
   <p class="step-description">{{t installer.step2.body}}</p>
+  {{#if isSaved}}
+  <div class="alert alert-warning" role="alert"><strong>{{t installer.warning.changes.header}}</strong> {{t installer.warning.changes}}</div>
+  {{/if}}
 
   <div class="panel panel-default">
     <div class="panel-body">

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/templates/wizard/step3.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step3.hbs b/ambari-web/app/templates/wizard/step3.hbs
index b480fd3..2f060fc 100644
--- a/ambari-web/app/templates/wizard/step3.hbs
+++ b/ambari-web/app/templates/wizard/step3.hbs
@@ -19,6 +19,9 @@
 <div id="confirm-hosts" class="wizard-content col-md-9">
   <h4 class="step-header">{{t installer.step3.header}}</h4>
   <p class="step-description" {{QAAttr "step3-description"}}>{{t installer.step3.body}}</p>
+  {{#if isSaved}}
+  <div class="alert alert-warning" role="alert"><strong>{{t installer.warning.changes.header}}</strong> {{t installer.warning.changes}}</div>
+  {{/if}}
 
   <div class="panel panel-default">
     <div class="panel-body">
@@ -160,4 +163,4 @@
       {{t common.next}} &rarr;
     </button>
   </div>
-</div>
\ No newline at end of file
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/templates/wizard/step6.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step6.hbs b/ambari-web/app/templates/wizard/step6.hbs
index 5741f09..8d9e883 100644
--- a/ambari-web/app/templates/wizard/step6.hbs
+++ b/ambari-web/app/templates/wizard/step6.hbs
@@ -19,6 +19,9 @@
 <div id="step6" class="wizard-content col-md-9">
   <h4 class="step-header" {{QAAttr "step-title"}}>{{t installer.step6.header}}</h4>
   <p class="step-description">{{{view.label}}}</p>
+  {{#if isSaved}}
+  <div class="alert alert-warning" role="alert"><strong>{{t installer.warning.changes.header}}</strong> {{t installer.warning.changes}}</div>
+  {{/if}}
 
   <div class="panel panel-default">
     <div class="panel-body">

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 1ef0b62..8810f13 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -380,8 +380,6 @@ require('views/wizard/step9_view');
 require('views/wizard/step9/hostLogPopupBody_view');
 require('views/wizard/step10_view');
 require('views/wizard/selectMpacks_view');
-require('views/wizard/selectMpacks/mpack_view');
-require('views/wizard/selectMpacks/selectedMpackVersion_view');
 require('views/loading');
 
 require('views/experimental');

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/views/wizard/selectMpacks/mpack_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/selectMpacks/mpack_view.js b/ambari-web/app/views/wizard/selectMpacks/mpack_view.js
deleted file mode 100644
index 9c22eb9..0000000
--- a/ambari-web/app/views/wizard/selectMpacks/mpack_view.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * 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.WizardMpackView = Em.View.extend({
-  templateName: require('templates/wizard/selectMpacks/mpack'),
-
-  version: function() {
-    return this.get('mpack.versions')[0].version;
-  }.property(),
-
-  services: function () {
-    return this.get('mpack.versions')[0].services;
-  }.property()
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/views/wizard/selectMpacks/selectedMpackVersion_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/selectMpacks/selectedMpackVersion_view.js b/ambari-web/app/views/wizard/selectMpacks/selectedMpackVersion_view.js
deleted file mode 100644
index ca503d7..0000000
--- a/ambari-web/app/views/wizard/selectMpacks/selectedMpackVersion_view.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * 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.WizardSelectedMpackVersionView = Em.View.extend({
-  templateName: require('templates/wizard/selectMpacks/selectedMpackVersion'),
-
-  mpack: function () {
-    return this.get('mpackVersion.mpack.name');
-  }.property()
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/views/wizard/selectMpacks_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/selectMpacks_view.js b/ambari-web/app/views/wizard/selectMpacks_view.js
index 3dc4ec0..deab12b 100644
--- a/ambari-web/app/views/wizard/selectMpacks_view.js
+++ b/ambari-web/app/views/wizard/selectMpacks_view.js
@@ -16,7 +16,6 @@
  * limitations under the License.
  */
 
-
 var App = require('app');
 
 App.WizardSelectMpacksView = Em.View.extend({
@@ -24,5 +23,57 @@ App.WizardSelectMpacksView = Em.View.extend({
 
   didInsertElement: function () {
     this.get('controller').loadStep();
+  }
+});
+
+/**
+ * View for each mpack in the registry
+ */
+App.WizardMpackView = Em.View.extend({
+  templateName: require('templates/wizard/selectMpacks/mpack'),
+
+  services: function () {
+    return this.get('mpack.versions').filterProperty('displayed')[0].services;
+  }.property('mpack.versions.@each.displayed'),
+
+  /**
+   * Handle mpack version changed
+   * 
+   * @param {any} event 
+   */
+  changeVersion: function (event) {
+    const versionId = event.target.value;
+    this.get('controller').displayMpackVersion(versionId);
   },
+
+  /**
+   * Handle add service button clicked
+   *
+   * @param  {type} event
+   */
+  addService: function (event) {
+    const serviceId = event.context;
+    this.get('controller').addServiceHandler(serviceId);
+  }
+});
+
+/**
+ * View for each selected mpack
+ */
+App.WizardSelectedMpackVersionView = Em.View.extend({
+  templateName: require('templates/wizard/selectMpacks/selectedMpackVersion'),
+
+  mpack: function () {
+    return this.get('mpackVersion.mpack.name');
+  }.property(),
+
+  /**
+   * Handle remove service button clicked.
+   *
+   * @param  {type} event
+   */
+  removeService: function (event) {
+    const serviceId = event.context;
+    this.get('controller').removeServiceHandler(serviceId);
+  }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/views/wizard/step2_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step2_view.js b/ambari-web/app/views/wizard/step2_view.js
index 6bbda30..e498a2d 100644
--- a/ambari-web/app/views/wizard/step2_view.js
+++ b/ambari-web/app/views/wizard/step2_view.js
@@ -60,6 +60,7 @@ App.WizardStep2View = Em.View.extend({
     //todo: move them to conroller
     this.set('controller.hostsError', null);
     this.set('controller.sshKeyError', null);
+    this.get('controller').loadStep();
   },
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/app/views/wizard/step6_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/wizard/step6_view.js b/ambari-web/app/views/wizard/step6_view.js
index ab37245..03a4dc1 100644
--- a/ambari-web/app/views/wizard/step6_view.js
+++ b/ambari-web/app/views/wizard/step6_view.js
@@ -83,6 +83,7 @@ App.WizardStep6View = App.TableView.extend({
     Em.set(checkbox, 'checked', !checkbox.checked);
     this.get('controller').checkCallback(checkbox.component);
     this.get('controller').callValidation();
+    this.get('controller').hostsChanged();
   },
 
   columnCount: function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/installer_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/installer_test.js b/ambari-web/test/controllers/installer_test.js
index f581d91..88289b2 100644
--- a/ambari-web/test/controllers/installer_test.js
+++ b/ambari-web/test/controllers/installer_test.js
@@ -537,11 +537,14 @@ describe('App.InstallerController', function () {
         },
         loadRecommendations: function() {
           loadRecommendations = true;
+        },
+        getStepIndex: function () {
+          return 0;
         }
       };
 
       beforeEach(function () {
-        installerController.loadMap['step5'][0].callback.call(checker);
+        installerController.loadMap['step5'][1].callback.call(checker);
       });
 
       it('confirmed hosts are loaded', function() {
@@ -900,6 +903,7 @@ describe('App.InstallerController', function () {
 
   describe('#loadMasterComponentHosts', function() {
     beforeEach(function () {
+      installerController.set('content.masterComponentHosts', null);
       sinon.stub(installerController, 'getDBProperties', function() {
         return {
           masterComponentHosts: Em.A([
@@ -925,7 +929,7 @@ describe('App.InstallerController', function () {
     afterEach(function () {
       installerController.getDBProperties.restore();
     });
-    it ('Should load hosts', function() {
+    it('Should load hosts', function() {
       installerController.loadMasterComponentHosts();
       expect(installerController.get('content.masterComponentHosts')).to.eql([
         {
@@ -1210,4 +1214,106 @@ describe('App.InstallerController', function () {
 
   });
 
+  describe('#setStepSaved', function() {
+    beforeEach(function() {
+      installerController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
+
+      installerController.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
+      });
+
+      installerController.setStepSaved('step1');
+      var actual = installerController.get('content.stepsSavedState');
+      expect(actual).to.deep.equal(expected);
+      expect(installerController.getStepSavedState('step1')).to.be.true;
+    });
+
+    it('Should do nothing when step is already saved', function() {
+      var expected = Em.Object.create({
+        "1": true,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+      installerController.set('content.stepsSavedState', expected);
+
+      installerController.setStepSaved('step1');
+      var actual = installerController.get('content.stepsSavedState');
+      expect(actual).to.deep.equal(expected);
+      expect(installerController.getStepSavedState('step1')).to.be.true;
+    });
+  });
+
+  describe('#setStepUnsaved', function() {
+    beforeEach(function() {
+      installerController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
+
+      var initial = Em.Object.create({
+        "1": true,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+      installerController.set('content.stepsSavedState', initial);
+    });
+
+    it('Should set step to unsaved', function() {
+      var expected = Em.Object.create({
+        "1": false,
+        "2": true,
+        "3": true,
+        "4": true
+      })
+
+      installerController.setStepUnsaved('step1');
+      var actual = installerController.get('content.stepsSavedState');
+      expect(actual).to.deep.equal(expected);
+      expect(installerController.getStepSavedState('step1')).to.be.false;
+    });
+  });
+
+  describe('#getStepSavedState', function() {
+    beforeEach(function() {
+      installerController.set('steps', [
+        "step0",
+        "step1",
+        "step2",
+        "step3",
+        "step4"
+      ]);
+
+      var initial = Em.Object.create({
+        "1": true,
+        "2": false
+      })
+      installerController.set('content.stepsSavedState', initial);
+    });
+
+    it('Should return false for bad step name', function() {
+      expect(installerController.getStepSavedState('step5')).to.be.false;
+    });
+
+    it('Should return false for step that was never saved', function() {
+      expect(installerController.getStepSavedState('step0')).to.be.false;
+    });
+  });
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/main/host/add_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host/add_controller_test.js b/ambari-web/test/controllers/main/host/add_controller_test.js
index 089d018..15be7ac 100644
--- a/ambari-web/test/controllers/main/host/add_controller_test.js
+++ b/ambari-web/test/controllers/main/host/add_controller_test.js
@@ -671,7 +671,7 @@ describe('App.AddHostController', function () {
       )).to.be.true;
     });
     it('setDBProperty called with valid arguments', function () {
-      expect(controller.setDBProperty.calledWith('clientInfo', ['client'])).to.be.true;
+      expect(controller.setDBProperty.calledWith('clients', ['client'])).to.be.true;
     });
     it('content.clients are valid', function () {
       expect(controller.get('content.clients')).to.be.eql(['client']);

http://git-wip-us.apache.org/repos/asf/ambari/blob/4087a117/ambari-web/test/controllers/main/service/add_controller_test.js
----------------------------------------------------------------------
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 1119176..6315891 100644
--- a/ambari-web/test/controllers/main/service/add_controller_test.js
+++ b/ambari-web/test/controllers/main/service/add_controller_test.js
@@ -437,7 +437,7 @@ describe('App.AddServiceController', function() {
       describe(item.title, function () {
 
         beforeEach(function () {
-          sinon.stub(addServiceController, 'getDBProperty').withArgs('clientInfo').returns(item.clients);
+          sinon.stub(addServiceController, 'getDBProperty').withArgs('clients').returns(item.clients);
           sinon.stub(addServiceController, 'saveClients', Em.K);
           addServiceController.set('content.clients', []);
           addServiceController.loadClients();