You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2014/07/31 12:50:56 UTC

git commit: AMBARI-6691 UI unit tests for Move Wizard controllers. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 68b97f035 -> 0f4f7d7c1


AMBARI-6691 UI unit tests for Move Wizard controllers. (atkach)


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

Branch: refs/heads/trunk
Commit: 0f4f7d7c1def97acd24aedef781fb9a2a0a99a8b
Parents: 68b97f0
Author: atkach <at...@hortonworks.com>
Authored: Thu Jul 31 13:49:33 2014 +0300
Committer: atkach <at...@hortonworks.com>
Committed: Thu Jul 31 13:49:33 2014 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +
 .../main/service/reassign/step2_controller.js   |  43 +-
 .../main/service/reassign/step4_controller.js   | 452 +++++++++----
 .../main/service/reassign_controller.js         |   2 +-
 .../main/admin/highAvailability/progress.hbs    |   2 +-
 .../service/reassign/step2_controller_test.js   | 289 ++++++++
 .../service/reassign/step4_controller_test.js   | 660 +++++++++++++++++++
 .../test/controllers/wizard/step5_test.js       | 125 ----
 8 files changed, 1303 insertions(+), 272 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 7338c5b..b4547d5 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -70,6 +70,8 @@ var files = ['test/init_model_test',
   'test/controllers/main/host/details_test',
   'test/controllers/main/service/add_controller_test',
   'test/controllers/main/service/reassign_controller_test',
+  'test/controllers/main/service/reassign/step2_controller_test',
+  'test/controllers/main/service/reassign/step4_controller_test',
   'test/controllers/main/dashboard_test',
   'test/controllers/main/host_test',
   'test/controllers/main/service/item_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/app/controllers/main/service/reassign/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/reassign/step2_controller.js b/ambari-web/app/controllers/main/service/reassign/step2_controller.js
index ed00207..cc80f07 100644
--- a/ambari-web/app/controllers/main/service/reassign/step2_controller.js
+++ b/ambari-web/app/controllers/main/service/reassign/step2_controller.js
@@ -29,24 +29,29 @@ App.ReassignMasterWizardStep2Controller = App.WizardStep5Controller.extend({
       this.get('multipleComponents').push('NAMENODE');
     }
     this._super();
-    if(this.get('content.reassign.component_name') == "NAMENODE" && !this.get('content.masterComponentHosts').findProperty('component', "SECONDARY_NAMENODE")){
+    if (this.get('content.reassign.component_name') === "NAMENODE" && !this.get('content.masterComponentHosts').findProperty('component', "SECONDARY_NAMENODE")) {
       this.set('showCurrentHost', false);
       this.set('componentToRebalance', 'NAMENODE');
       this.incrementProperty('rebalanceComponentHostsCounter');
-    }else{
+    } else {
       this.set('showCurrentHost', true);
       this.rebalanceSingleComponentHosts(this.get('content.reassign.component_name'));
     }
   },
 
+  /**
+   * load master components
+   * @return {Array}
+   */
   loadComponents: function () {
     var masterComponents = this.get('content.masterComponentHosts');
     this.set('currentHostId', this.get('content').get('reassign').host_id);
     var componentNameToReassign = this.get('content').get('reassign').component_name;
     var result = [];
+
     masterComponents.forEach(function (master) {
       var color = "grey";
-      if(master.component == componentNameToReassign){
+      if (master.component == componentNameToReassign) {
         color = 'green';
       }
       result.push({
@@ -62,25 +67,33 @@ App.ReassignMasterWizardStep2Controller = App.WizardStep5Controller.extend({
     return result;
   },
 
-  rebalanceSingleComponentHosts:function (componentName) {
+  /**
+   * rebalance single component among available hosts
+   * @param componentName
+   * @return {Boolean}
+   */
+  rebalanceSingleComponentHosts: function (componentName) {
     var currentComponents = this.get("selectedServicesMasters").filterProperty("component_name", componentName),
-      availableComponentHosts = [],
-      preparedAvailableHosts = null;
+      availableComponentHosts = [];
+
     this.get("hosts").forEach(function (item) {
       if (this.get('currentHostId') !== item.get('host_name')) {
         availableComponentHosts.pushObject(item);
       }
     }, this);
-    if (availableComponentHosts.length == 0) {
-      return;
+
+    if (availableComponentHosts.length > 0) {
+      currentComponents.forEach(function (item) {
+        var preparedAvailableHosts = availableComponentHosts.slice(0);
+
+        if (item.get('selectedHost') === this.get('currentHostId') && item.get('component_name') === this.get('content.reassign.component_name')) {
+          item.set('selectedHost', preparedAvailableHosts.objectAt(0).host_name);
+        }
+        item.set("availableHosts", preparedAvailableHosts.sortProperty('host_name'));
+      }, this);
+      return true;
     }
-    currentComponents.forEach(function (item) {
-      preparedAvailableHosts = availableComponentHosts.slice(0);
-      if (item.get('selectedHost') == this.get('currentHostId') && item.get('component_name') == this.get('content.reassign.component_name')) {
-        item.set('selectedHost', preparedAvailableHosts.objectAt(0).host_name);
-      }
-      item.set("availableHosts", preparedAvailableHosts.sortProperty('host_name'));
-    }, this);
+    return false;
   },
 
   getIsSubmitDisabled: function () {

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/app/controllers/main/service/reassign/step4_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/reassign/step4_controller.js b/ambari-web/app/controllers/main/service/reassign/step4_controller.js
index 9ee6c4a..da80cbf 100644
--- a/ambari-web/app/controllers/main/service/reassign/step4_controller.js
+++ b/ambari-web/app/controllers/main/service/reassign/step4_controller.js
@@ -31,6 +31,153 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
   hostComponents: [],
   restartYarnMRComponents: false,
 
+  /**
+   * additional configs with template values
+   * Part of value to substitute has following format: "<replace-value>"
+   */
+  additionalConfigsMap: [
+    {
+      componentName: 'RESOURCEMANAGER',
+      configs: {
+        'yarn-site': {
+          'yarn.resourcemanager.address': '<replace-value>:8050',
+          'yarn.resourcemanager.admin.address': '<replace-value>:8141',
+          'yarn.resourcemanager.resource-tracker.address': '<replace-value>:8025',
+          'yarn.resourcemanager.scheduler.address': '<replace-value>:8030',
+          'yarn.resourcemanager.webapp.address': '<replace-value>:8088',
+          'yarn.resourcemanager.hostname': '<replace-value>'
+        }
+      }
+    },
+    {
+      componentName: 'JOBTRACKER',
+      configs: {
+        'mapred-site': {
+          'mapred.job.tracker.http.address': '<replace-value>:50030',
+          'mapred.job.tracker': '<replace-value>:50300'
+        }
+      }
+    },
+    {
+      componentName: 'SECONDARY_NAMENODE',
+      configs: {
+        'hdfs-site': {
+          'dfs.secondary.http.address': '<replace-value>:50090'
+        }
+      },
+      configs_Hadoop2: {
+        'hdfs-site': {
+          'dfs.namenode.secondary.http-address': '<replace-value>:50090'
+        }
+      }
+    },
+    {
+      componentName: 'NAMENODE',
+      configs: {
+        'hdfs-site': {
+          'dfs.http.address': '<replace-value>:50070',
+          'dfs.https.address': '<replace-value>:50470'
+        },
+        'core-site': {
+          'fs.default.name': 'hdfs://<replace-value>:8020'
+        }
+      },
+      configs_Hadoop2: {
+        'hdfs-site': {
+          'dfs.namenode.http-address': '<replace-value>:50070',
+          'dfs.namenode.https-address': '<replace-value>:50470'
+        },
+        'core-site': {
+          'fs.defaultFS': 'hdfs://<replace-value>:8020'
+        }
+      }
+    }
+  ],
+
+  secureConfigsMap: [
+    {
+      componentName: 'NAMENODE',
+      configs: [
+        {
+          site: 'hdfs-site',
+          keytab: 'dfs.namenode.keytab.file',
+          principal: 'dfs.namenode.kerberos.principal'
+        },
+        {
+          site: 'hdfs-site',
+          keytab: 'dfs.web.authentication.kerberos.keytab',
+          principal: 'dfs.web.authentication.kerberos.principal'
+        }
+      ]
+    },
+    {
+      componentName: 'SECONDARY_NAMENODE',
+      configs: [
+        {
+          site: 'hdfs-site',
+          keytab: 'dfs.secondary.namenode.keytab.file',
+          principal: 'dfs.secondary.namenode.kerberos.principal'
+        },
+        {
+          site: 'hdfs-site',
+          keytab: 'dfs.web.authentication.kerberos.keytab',
+          principal: 'dfs.web.authentication.kerberos.principal'
+        }
+      ]
+    },
+    {
+      componentName: 'RESOURCEMANAGER',
+      configs: [
+        {
+          site: 'yarn-site',
+          keytab: 'yarn.resourcemanager.keytab',
+          principal: 'yarn.resourcemanager.principal'
+        },
+        {
+          site: 'yarn-site',
+          keytab: 'yarn.resourcemanager.webapp.spnego-keytab-file',
+          principal: 'yarn.resourcemanager.webapp.spnego-principal'
+        }
+      ]
+    },
+    {
+      componentName: 'JOBTRACKER',
+      configs: [
+        {
+          site: 'mapred-site',
+          keytab: 'mapreduce.jobtracker.keytab.file',
+          principal: 'mapreduce.jobtracker.kerberos.principal'
+        }
+      ]
+    }
+  ],
+
+  /**
+   * set additional configs
+   * configs_Hadoop2 - configs which belongs to Hadoop 2 stack only
+   * @param configs
+   * @param componentName
+   * @param replaceValue
+   * @return {Boolean}
+   */
+  setAdditionalConfigs: function (configs, componentName, replaceValue) {
+    var isHadoop2Stack = App.get('isHadoop2Stack');
+    var component = this.get('additionalConfigsMap').findProperty('componentName', componentName);
+
+    if (Em.isNone(component)) return false;
+    var additionalConfigs = (component.configs_Hadoop2 && isHadoop2Stack) ? component.configs_Hadoop2 : component.configs;
+
+    for (var site in additionalConfigs) {
+      for (var property in additionalConfigs[site]) {
+        configs[site][property] = additionalConfigs[site][property].replace('<replace-value>', replaceValue);
+      }
+    }
+    return true;
+  },
+
+  /**
+   * load step info
+   */
   loadStep: function () {
     if (this.get('content.reassign.component_name') === 'NAMENODE' && App.get('isHaEnabled')) {
       this.set('hostComponents', ['NAMENODE', 'ZKFC']);
@@ -42,15 +189,42 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
     this._super();
   },
 
-  initializeTasks: function () {
-    var commands = this.get('commands');
-    var currentStep = App.router.get('reassignMasterController.currentStep');
+  /**
+   * concat host-component names into string
+   * @return {String}
+   */
+  getHostComponentsNames: function () {
     var hostComponentsNames = '';
-
     this.get('hostComponents').forEach(function (comp, index) {
       hostComponentsNames += index ? '+' : '';
       hostComponentsNames += comp === 'ZKFC' ? comp : App.format.role(comp);
     }, this);
+    return hostComponentsNames;
+  },
+
+  /**
+   * remove unneeded tasks
+   */
+  removeUnneededTasks: function () {
+    if (this.get('content.hasManualSteps')) {
+      if (this.get('content.reassign.component_name') === 'NAMENODE' && App.get('isHaEnabled')) {
+        // Only for reassign NameNode with HA enabled
+        this.get('tasks').splice(7, 2);
+      } else {
+        this.get('tasks').splice(5, 4);
+      }
+    } else {
+      this.get('tasks').splice(5, 2);
+    }
+  },
+
+  /**
+   * initialize tasks
+   */
+  initializeTasks: function () {
+    var commands = this.get('commands');
+    var currentStep = App.router.get('reassignMasterController.currentStep');
+    var hostComponentsNames = this.getHostComponentsNames();
 
     for (var i = 0; i < commands.length; i++) {
       var TaskLabel = i === 3 ? this.get('serviceName') : hostComponentsNames; //For Reconfigure task, show serviceName
@@ -69,63 +243,53 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
         hosts: []
       }));
     }
-
-    if (this.get('content.hasManualSteps')) {
-      if (this.get('content.reassign.component_name') === 'NAMENODE' && App.get('isHaEnabled')) {
-        // Only for reassign NameNode with HA enabled
-        this.get('tasks').splice(7, 2);
-      } else {
-        this.get('tasks').splice(5, 4);
-      }
-    } else {
-      this.get('tasks').splice(5, 2);
-    }
+    this.removeUnneededTasks();
   },
 
   hideRollbackButton: function () {
     var failedTask = this.get('tasks').findProperty('showRollback');
     if (failedTask) {
-      failedTask.set('showRollback', false)
+      failedTask.set('showRollback', false);
     }
   }.observes('tasks.@each.showRollback'),
 
   onComponentsTasksSuccess: function () {
-    this.set('multiTaskCounter', this.get('multiTaskCounter') + 1);
+    this.incrementProperty('multiTaskCounter');
     if (this.get('multiTaskCounter') >= this.get('hostComponents').length) {
       this.onTaskCompleted();
     }
   },
 
-  stopServices: function () {
-    if(this.get('restartYarnMRComponents')) {
+  /**
+   * compute data for call to stop services
+   */
+  getStopServicesData: function () {
+    var data = {
+      "ServiceInfo": {
+        "state": "INSTALLED"
+      }
+    };
+    if (this.get('restartYarnMRComponents')) {
       var list = App.Service.find().mapProperty("serviceName").without("HDFS").join(',');
-      var conf = {
-        name: 'common.services.update',
-        sender: this,
-        data: {
-          "context": "Stop without HDFS",
-          "ServiceInfo": {
-            "state": "INSTALLED"
-          },
-          urlParams: "ServiceInfo/service_name.in("+list+")"},
-        success: 'startPolling',
-        error: 'onTaskError'
-      };
-      App.ajax.send(conf);
+      data.context = "Stop without HDFS";
+      data.urlParams = "ServiceInfo/service_name.in(" + list + ")";
     } else {
-      App.ajax.send({
-        name: 'common.services.update',
-        sender: this,
-        data: {
-          "context": "Stop all services",
-          "ServiceInfo": {
-            "state": "INSTALLED"
-          }
-        },
-        success: 'startPolling',
-        error: 'onTaskError'
-      });
+      data.context = "Stop all services";
     }
+    return data;
+  },
+
+  /**
+   * make server call to stop services
+   */
+  stopServices: function () {
+    App.ajax.send({
+      name: 'common.services.update',
+      sender: this,
+      data: this.getStopServicesData(),
+      success: 'startPolling',
+      error: 'onTaskError'
+    });
   },
 
   createHostComponents: function () {
@@ -182,8 +346,13 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
     });
   },
 
-  onLoadConfigsTags: function (data) {
-    var componentName = this.get('content.reassign.component_name');
+  /**
+   * construct URL parameters for config call
+   * @param componentName
+   * @param data
+   * @return {Array}
+   */
+  getConfigUrlParams: function (componentName, data) {
     var urlParams = [];
     switch (componentName) {
       case 'NAMENODE':
@@ -204,6 +373,12 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
         urlParams.push('(type=yarn-site&tag=' + data.Clusters.desired_configs['yarn-site'].tag + ')');
         break;
     }
+    return urlParams;
+  },
+
+  onLoadConfigsTags: function (data) {
+    var urlParams = this.getConfigUrlParams(this.get('content.reassign.component_name'), data);
+
     App.ajax.send({
       name: 'reassign.load_configs',
       sender: this,
@@ -220,101 +395,34 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
   configsSitesNumber: null,
 
   onLoadConfigs: function (data) {
-    var isHadoop2Stack = App.get('isHadoop2Stack');
-    var securityEnabled = this.get('content.securityEnabled');
     var componentName = this.get('content.reassign.component_name');
     var targetHostName = this.get('content.reassignHosts.target');
-    var sourceHostName = this.get('content.reassignHosts.source');
     var configs = {};
-    var componentDir = '';
     var secureConfigs = [];
     this.set('configsSitesNumber', data.items.length);
     this.set('configsSitesCount', 0);
+
     data.items.forEach(function (item) {
       configs[item.type] = item.properties;
     }, this);
-    switch (componentName) {
-      case 'NAMENODE':
-        if (isHadoop2Stack) {
-          if (App.get('isHaEnabled')) {
-            var nameServices = configs['hdfs-site']['dfs.nameservices'];
-            if (configs['hdfs-site']['dfs.namenode.http-address.' + nameServices + '.nn1'] === sourceHostName + ':50070') {
-              configs['hdfs-site']['dfs.namenode.http-address.' + nameServices + '.nn1'] = targetHostName + ':50070';
-              configs['hdfs-site']['dfs.namenode.https-address.' + nameServices + '.nn1'] = targetHostName + ':50470';
-              configs['hdfs-site']['dfs.namenode.rpc-address.' + nameServices + '.nn1'] = targetHostName + ':8020';
-            } else {
-              configs['hdfs-site']['dfs.namenode.http-address.' + nameServices + '.nn2'] = targetHostName + ':50070';
-              configs['hdfs-site']['dfs.namenode.https-address.' + nameServices + '.nn2'] = targetHostName + ':50470';
-              configs['hdfs-site']['dfs.namenode.rpc-address.' + nameServices + '.nn2'] = targetHostName + ':8020';
-            }
-          } else {
-            configs['hdfs-site']['dfs.namenode.http-address'] = targetHostName + ':50070';
-            configs['hdfs-site']['dfs.namenode.https-address'] = targetHostName + ':50470';
-            configs['core-site']['fs.defaultFS'] = 'hdfs://' + targetHostName + ':8020';
-          }
-          componentDir = configs['hdfs-site']['dfs.namenode.name.dir'];
-        } else {
-          componentDir = configs['hdfs-site']['dfs.name.dir'];
-          configs['hdfs-site']['dfs.http.address'] = targetHostName + ':50070';
-          configs['hdfs-site']['dfs.https.address'] = targetHostName + ':50470';
-          configs['core-site']['fs.default.name'] = 'hdfs://' + targetHostName + ':8020';
-        }
-        if (!App.get('isHaEnabled')) {
-          if (App.Service.find().someProperty('serviceName', 'HBASE')) {
-            configs['hbase-site']['hbase.rootdir'] = configs['hbase-site']['hbase.rootdir'].replace(/\/\/[^\/]*/, '//' + targetHostName + ':8020');
-          }
-        }
-        if (securityEnabled) {
-          secureConfigs.push({keytab: configs['hdfs-site']['dfs.namenode.keytab.file'], principal: configs['hdfs-site']['dfs.namenode.kerberos.principal']});
-          secureConfigs.push({keytab: configs['hdfs-site']['dfs.web.authentication.kerberos.keytab'], principal: configs['hdfs-site']['dfs.web.authentication.kerberos.principal']});
-        }
-        break;
-      case 'SECONDARY_NAMENODE':
-        if (isHadoop2Stack) {
-          componentDir = configs['hdfs-site']['dfs.namenode.checkpoint.dir'];
-          configs['hdfs-site']['dfs.namenode.secondary.http-address'] = targetHostName + ':50090';
-        } else {
-          componentDir = configs['core-site']['fs.checkpoint.dir'];
-          configs['hdfs-site']['dfs.secondary.http.address'] = targetHostName + ':50090';
-        }
-        if (securityEnabled) {
-          secureConfigs.push({keytab: configs['hdfs-site']['dfs.secondary.namenode.keytab.file'], principal: configs['hdfs-site']['dfs.secondary.namenode.kerberos.principal']});
-          secureConfigs.push({keytab: configs['hdfs-site']['dfs.web.authentication.kerberos.keytab'], principal: configs['hdfs-site']['dfs.web.authentication.kerberos.principal']});
-        }
-        break;
-      case 'JOBTRACKER':
-        configs['mapred-site']['mapred.job.tracker.http.address'] = targetHostName + ':50030';
-        configs['mapred-site']['mapred.job.tracker'] = targetHostName + ':50300';
-        if (securityEnabled) {
-          secureConfigs.push({keytab: configs['mapred-site']['mapreduce.jobtracker.keytab.file'], principal: configs['mapred-site']['mapreduce.jobtracker.kerberos.principal']});
-        }
-        break;
-      case 'RESOURCEMANAGER':
-        configs['yarn-site']['yarn.resourcemanager.address'] = targetHostName + ':8050';
-        configs['yarn-site']['yarn.resourcemanager.admin.address'] = targetHostName + ':8141';
-        configs['yarn-site']['yarn.resourcemanager.resource-tracker.address'] = targetHostName + ':8025';
-        configs['yarn-site']['yarn.resourcemanager.scheduler.address'] = targetHostName + ':8030';
-        configs['yarn-site']['yarn.resourcemanager.webapp.address'] = targetHostName + ':8088';
-        configs['yarn-site']['yarn.resourcemanager.hostname'] = targetHostName;
-        if (securityEnabled) {
-          secureConfigs.push({keytab: configs['yarn-site']['yarn.resourcemanager.keytab'], principal: configs['yarn-site']['yarn.resourcemanager.principal']});
-          secureConfigs.push({keytab: configs['yarn-site']['yarn.resourcemanager.webapp.spnego-keytab-file'], principal: configs['yarn-site']['yarn.resourcemanager.webapp.spnego-principal']});
-        }
 
-        break;
-    }
-    if (componentDir || secureConfigs.length) {
-      App.router.get(this.get('content.controllerName')).saveComponentDir(componentDir);
-      App.router.get(this.get('content.controllerName')).saveSecureConfigs(secureConfigs);
-      App.clusterStatus.setClusterStatus({
-        clusterName: this.get('content.cluster.name'),
-        clusterState: this.get('clusterDeployState'),
-        wizardControllerName: this.get('content.controllerName'),
-        localdb: App.db.data
-      });
+    this.setAdditionalConfigs(configs, componentName, targetHostName);
+    this.setSecureConfigs(secureConfigs, configs, componentName);
+
+    if (componentName === 'NAMENODE') {
+      this.setSpecificNamenodeConfigs(configs, targetHostName);
     }
+
+    this.saveClusterStatus(secureConfigs, this.getComponentDir(configs, componentName));
+    this.saveConfigsToServer(configs);
+  },
+
+  /**
+   * make PUT call to save configs to server
+   * @param configs
+   */
+  saveConfigsToServer: function (configs) {
     for (var site in configs) {
-      if (!configs.hasOwnProperty(site)) continue;
       App.ajax.send({
         name: 'reassign.save_configs',
         sender: this,
@@ -328,6 +436,90 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
     }
   },
 
+  /**
+   * set specific configs which applies only to NameNode component
+   * @param configs
+   * @param targetHostName
+   */
+  setSpecificNamenodeConfigs: function (configs, targetHostName) {
+    var sourceHostName = this.get('content.reassignHosts.source');
+
+    if (App.get('isHadoop2Stack') && App.get('isHaEnabled')) {
+      var nameServices = configs['hdfs-site']['dfs.nameservices'];
+      if (configs['hdfs-site']['dfs.namenode.http-address.' + nameServices + '.nn1'] === sourceHostName + ':50070') {
+        configs['hdfs-site']['dfs.namenode.http-address.' + nameServices + '.nn1'] = targetHostName + ':50070';
+        configs['hdfs-site']['dfs.namenode.https-address.' + nameServices + '.nn1'] = targetHostName + ':50470';
+        configs['hdfs-site']['dfs.namenode.rpc-address.' + nameServices + '.nn1'] = targetHostName + ':8020';
+      } else {
+        configs['hdfs-site']['dfs.namenode.http-address.' + nameServices + '.nn2'] = targetHostName + ':50070';
+        configs['hdfs-site']['dfs.namenode.https-address.' + nameServices + '.nn2'] = targetHostName + ':50470';
+        configs['hdfs-site']['dfs.namenode.rpc-address.' + nameServices + '.nn2'] = targetHostName + ':8020';
+      }
+    }
+    if (!App.get('isHaEnabled') && App.Service.find('HBASE').get('isLoaded')) {
+      configs['hbase-site']['hbase.rootdir'] = configs['hbase-site']['hbase.rootdir'].replace(/\/\/[^\/]*/, '//' + targetHostName + ':8020');
+    }
+  },
+
+  /**
+   * set secure configs for component
+   * @param secureConfigs
+   * @param configs
+   * @param componentName
+   * @return {Boolean}
+   */
+  setSecureConfigs: function (secureConfigs, configs, componentName) {
+    var securityEnabled = this.get('content.securityEnabled');
+    var component = this.get('secureConfigsMap').findProperty('componentName', componentName);
+    if (Em.isNone(component) || !securityEnabled) return false;
+
+    component.configs.forEach(function (config) {
+      secureConfigs.push({
+        keytab: configs[config.site][config.keytab],
+        principal: configs[config.site][config.principal]
+      });
+    });
+    return true;
+  },
+
+  /**
+   * derive component directory from configurations
+   * @param configs
+   * @param componentName
+   * @return {String}
+   */
+  getComponentDir: function (configs, componentName) {
+    if (componentName === 'NAMENODE') {
+      return (App.get('isHadoop2Stack')) ? configs['hdfs-site']['dfs.namenode.name.dir'] : configs['hdfs-site']['dfs.name.dir'];
+    }
+    else if (componentName === 'SECONDARY_NAMENODE') {
+      return (App.get('isHadoop2Stack')) ? configs['hdfs-site']['dfs.namenode.checkpoint.dir'] : configs['core-site']['fs.checkpoint.dir'];
+    }
+    return '';
+  },
+
+  /**
+   * save cluster status to server
+   *
+   * @param secureConfigs
+   * @param componentDir
+   * @return {Boolean}
+   */
+  saveClusterStatus: function (secureConfigs, componentDir) {
+    if (componentDir || secureConfigs.length) {
+      App.router.get(this.get('content.controllerName')).saveComponentDir(componentDir);
+      App.router.get(this.get('content.controllerName')).saveSecureConfigs(secureConfigs);
+      App.clusterStatus.setClusterStatus({
+        clusterName: this.get('content.cluster.name'),
+        clusterState: this.get('clusterDeployState'),
+        wizardControllerName: this.get('content.controllerName'),
+        localdb: App.db.data
+      });
+      return true;
+    }
+    return false;
+  },
+
   onSaveConfigs: function () {
     this.set('configsSitesCount', this.get('configsSitesCount') + 1);
     if (this.get('configsSitesCount') === this.get('configsSitesNumber')) {
@@ -346,7 +538,7 @@ App.ReassignMasterWizardStep4Controller = App.HighAvailabilityProgressPageContro
   },
 
   startServices: function () {
-    if(this.get('restartYarnMRComponents')) {
+    if (this.get('restartYarnMRComponents')) {
       var list = App.Service.find().mapProperty("serviceName").without("HDFS").join(',');
       var conf = {
         name: 'common.services.update',

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/app/controllers/main/service/reassign_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/reassign_controller.js b/ambari-web/app/controllers/main/service/reassign_controller.js
index 6f0ff4e..870b281 100644
--- a/ambari-web/app/controllers/main/service/reassign_controller.js
+++ b/ambari-web/app/controllers/main/service/reassign_controller.js
@@ -118,7 +118,7 @@ App.ReassignMasterController = App.WizardController.extend({
         tagName: tag
       }
     ];
-    App.router.get('configurationController').getConfigsByTags(tags).data(function (data) {
+    App.router.get('configurationController').getConfigsByTags(tags).done(function (data) {
       var configs = data.findProperty('tag', tag).properties;
       var result = configs && (configs['security_enabled'] === 'true' || configs['security_enabled'] === true);
       self.saveSecurityEnabled(result);

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/app/templates/main/admin/highAvailability/progress.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/highAvailability/progress.hbs b/ambari-web/app/templates/main/admin/highAvailability/progress.hbs
index 980511a..9eb856a 100644
--- a/ambari-web/app/templates/main/admin/highAvailability/progress.hbs
+++ b/ambari-web/app/templates/main/admin/highAvailability/progress.hbs
@@ -58,6 +58,6 @@
   {{/view}}
   {{/each}}
   <div class="btn-area">
-    <a {{bindAttr class=":btn controller.isSubmitDisabled:disabled :btn-success :pull-right"}} {{action done target="controller"}}>{{view.submitButtonText}}</a>
+    <button class=":btn :btn-success :pull-right" {{bindAttr disabled="controller.isSubmitDisabled"}} {{action done target="controller"}}>{{view.submitButtonText}}</button>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/test/controllers/main/service/reassign/step2_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/reassign/step2_controller_test.js b/ambari-web/test/controllers/main/service/reassign/step2_controller_test.js
new file mode 100644
index 0000000..67c1d35
--- /dev/null
+++ b/ambari-web/test/controllers/main/service/reassign/step2_controller_test.js
@@ -0,0 +1,289 @@
+/**
+ * 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.
+ */
+
+App = require('app');
+
+require('controllers/main/service/reassign/step2_controller');
+require('models/host_component');
+
+describe('App.ReassignMasterWizardStep2Controller', function () {
+
+
+  var controller = App.ReassignMasterWizardStep2Controller.create({
+    content: Em.Object.create({
+      reassign: Em.Object.create({}),
+      services: []
+    }),
+    renderComponents: Em.K,
+    multipleComponents: []
+  });
+  controller.set('_super', Em.K);
+
+  describe('#loadStep', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, 'rebalanceSingleComponentHosts', Em.K);
+    });
+    afterEach(function () {
+      controller.rebalanceSingleComponentHosts.restore();
+    });
+
+    it('SECONDARY_NAMENODE is absent, reassign component is NAMENODE', function () {
+      controller.set('content.reassign.component_name', 'NAMENODE');
+      controller.set('content.masterComponentHosts', []);
+
+      controller.loadStep();
+      expect(controller.get('showCurrentHost')).to.be.false;
+      expect(controller.get('componentToRebalance')).to.equal('NAMENODE');
+      expect(controller.get('rebalanceComponentHostsCounter')).to.equal(1);
+    });
+    it('SECONDARY_NAMENODE is present, reassign component is NAMENODE', function () {
+      controller.set('content.reassign.component_name', 'NAMENODE');
+      controller.set('content.masterComponentHosts', [
+        {
+          component: 'SECONDARY_NAMENODE'
+        }
+      ]);
+
+      controller.loadStep();
+      expect(controller.get('showCurrentHost')).to.be.true;
+      expect(controller.rebalanceSingleComponentHosts.calledWith('NAMENODE'));
+    });
+    it('SECONDARY_NAMENODE is absent, reassign component is not NAMENODE', function () {
+      controller.set('content.reassign.component_name', 'COMP');
+      controller.set('content.masterComponentHosts', []);
+
+      controller.loadStep();
+      expect(controller.get('showCurrentHost')).to.be.true;
+      expect(controller.rebalanceSingleComponentHosts.calledWith('COMP'));
+    });
+    it('if HA is enabled then multipleComponents should contain NAMENODE', function () {
+      sinon.stub(App,'get', function() {
+        return true;
+      });
+
+      controller.loadStep();
+      expect(controller.get('multipleComponents')).to.eql(['NAMENODE']);
+      App.get.restore();
+    });
+  });
+
+  describe('#loadComponents', function () {
+    it('masterComponentHosts is empty', function () {
+      controller.set('content.masterComponentHosts', []);
+      controller.set('content.reassign.host_id', 1);
+
+      expect(controller.loadComponents()).to.be.empty;
+      expect(controller.get('currentHostId')).to.equal(1);
+    });
+    it('masterComponentHosts does not contain reassign component', function () {
+      sinon.stub(App.HostComponent, 'find', function () {
+        return [Em.Object.create({
+          componentName: 'COMP1',
+          serviceName: 'SERVICE'
+        })];
+      });
+      controller.set('content.masterComponentHosts', [{
+        component: 'COMP1',
+        hostName: 'host1'
+      }]);
+      controller.set('content.reassign.host_id', 1);
+      controller.set('content.reassign.component_name', 'COMP2');
+
+      expect(controller.loadComponents()).to.eql([
+        {
+          "component_name": "COMP1",
+          "display_name": "Comp1",
+          "selectedHost": "host1",
+          "isInstalled": true,
+          "serviceId": "SERVICE",
+          "isServiceCoHost": false,
+          "color": "grey"
+        }
+      ]);
+      expect(controller.get('currentHostId')).to.equal(1);
+
+      App.HostComponent.find.restore();
+    });
+    it('masterComponentHosts contains reassign component', function () {
+      sinon.stub(App.HostComponent, 'find', function () {
+        return [Em.Object.create({
+          componentName: 'COMP1',
+          serviceName: 'SERVICE'
+        })];
+      });
+      controller.set('content.masterComponentHosts', [{
+        component: 'COMP1',
+        hostName: 'host1'
+      }]);
+      controller.set('content.reassign.host_id', 1);
+      controller.set('content.reassign.component_name', 'COMP1');
+
+      expect(controller.loadComponents()).to.eql([
+        {
+          "component_name": "COMP1",
+          "display_name": "Comp1",
+          "selectedHost": "host1",
+          "isInstalled": true,
+          "serviceId": "SERVICE",
+          "isServiceCoHost": false,
+          "color": "green"
+        }
+      ]);
+      expect(controller.get('currentHostId')).to.equal(1);
+
+      App.HostComponent.find.restore();
+    });
+  });
+
+  describe('#rebalanceSingleComponentHosts', function () {
+    it('hosts is empty', function () {
+      controller.set('hosts', []);
+
+      expect(controller.rebalanceSingleComponentHosts()).to.be.false;
+    });
+    it('currentHostId matches one available host', function () {
+      controller.set('hosts', [Em.Object.create({
+        host_name: 'host1'
+      })]);
+      controller.set('currentHostId', 'host1');
+
+      expect(controller.rebalanceSingleComponentHosts()).to.be.false;
+    });
+
+    var testCases = [
+      {
+        title: 'selectedHost = currentHostId and component_name = content.reassign.component_name',
+        arguments: {
+          selectedHost: 'host1',
+          reassignComponentName: 'COMP1'
+        },
+        result: 'host3'
+      },
+      {
+        title: 'selectedHost not equal to currentHostId and component_name = content.reassign.component_name',
+        arguments: {
+          selectedHost: 'host2',
+          reassignComponentName: 'COMP1'
+        },
+        result: 'host2'
+      },
+      {
+        title: 'selectedHost = currentHostId and component_name not equal to content.reassign.component_name',
+        arguments: {
+          selectedHost: 'host1',
+          reassignComponentName: 'COMP2'
+        },
+        result: 'host1'
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        controller.set('hosts', [
+          Em.Object.create({
+            host_name: 'host3'
+          }),
+          Em.Object.create({
+            host_name: 'host2'
+          })
+        ]);
+        controller.set('currentHostId', 'host1');
+        controller.set('content.reassign.component_name', test.arguments.reassignComponentName);
+        controller.set('selectedServicesMasters', [Em.Object.create({
+          component_name: 'COMP1',
+          selectedHost: test.arguments.selectedHost
+        })]);
+
+        expect(controller.rebalanceSingleComponentHosts('COMP1')).to.be.true;
+        expect(controller.get('selectedServicesMasters')[0].get('selectedHost')).to.equal(test.result);
+        expect(controller.get('selectedServicesMasters')[0].get('availableHosts').mapProperty('host_name')).to.eql(['host2', 'host3']);
+      });
+    });
+  });
+
+  describe('#getIsSubmitDisabled', function () {
+    var hostComponents = [];
+    var isSubmitDisabled = false;
+
+    beforeEach(function () {
+      sinon.stub(App.HostComponent, 'find', function () {
+        return hostComponents;
+      });
+      sinon.stub(controller, '_super', function() {
+        return isSubmitDisabled;
+      });
+    });
+    afterEach(function () {
+      App.HostComponent.find.restore();
+      controller._super.restore();
+    });
+    it('No host-components, reassigned equal 0', function () {
+      expect(controller.getIsSubmitDisabled()).to.be.true;
+      expect(controller.get('submitDisabled')).to.be.true;
+    });
+    it('Reassign component match existed components, reassigned equal 0', function () {
+      controller.set('content.reassign.component_name', 'COMP1');
+      hostComponents = [Em.Object.create({
+        componentName: 'COMP1',
+        hostName: 'host1'
+      })];
+      controller.set('servicesMasters', [{
+        selectedHost: 'host1'
+      }]);
+
+      expect(controller.getIsSubmitDisabled()).to.be.true;
+      expect(controller.get('submitDisabled')).to.be.true;
+    });
+    it('Reassign component do not match existed components, reassigned equal 1', function () {
+      controller.set('content.reassign.component_name', 'COMP1');
+      hostComponents = [Em.Object.create({
+        componentName: 'COMP1',
+        hostName: 'host1'
+      })];
+      controller.set('servicesMasters', []);
+
+      expect(controller.getIsSubmitDisabled()).to.be.false;
+      expect(controller.get('submitDisabled')).to.be.false;
+    });
+    it('Reassign component do not match existed components, reassigned equal 2', function () {
+      controller.set('content.reassign.component_name', 'COMP1');
+      hostComponents = [
+        Em.Object.create({
+          componentName: 'COMP1',
+          hostName: 'host1'
+        }),
+        Em.Object.create({
+          componentName: 'COMP1',
+          hostName: 'host2'
+        })
+      ];
+      controller.set('servicesMasters', []);
+
+      expect(controller.getIsSubmitDisabled()).to.be.true;
+      expect(controller.get('submitDisabled')).to.be.true;
+    });
+
+    it('submitDisabled is already true', function () {
+      isSubmitDisabled = true;
+
+      expect(controller.getIsSubmitDisabled()).to.be.true;
+      expect(controller.get('submitDisabled')).to.be.true;
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/ambari-web/test/controllers/main/service/reassign/step4_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/reassign/step4_controller_test.js b/ambari-web/test/controllers/main/service/reassign/step4_controller_test.js
new file mode 100644
index 0000000..289c285
--- /dev/null
+++ b/ambari-web/test/controllers/main/service/reassign/step4_controller_test.js
@@ -0,0 +1,660 @@
+/**
+ * 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.
+ */
+
+App = require('app');
+
+require('controllers/main/service/reassign/step4_controller');
+
+describe('App.ReassignMasterWizardStep4Controller', function () {
+
+  var controller = App.ReassignMasterWizardStep4Controller.create({
+    content: Em.Object.create({
+      reassign: Em.Object.create(),
+      reassignHosts: Em.Object.create()
+    })
+  });
+
+  beforeEach(function () {
+    sinon.stub(App.ajax, 'send', Em.K);
+  });
+  afterEach(function () {
+    App.ajax.send.restore();
+  });
+
+  describe('#setAdditionalConfigs()', function () {
+    var isHadoop2Stack = false;
+
+    beforeEach(function () {
+      sinon.stub(App, 'get', function () {
+        return isHadoop2Stack;
+      });
+    });
+    afterEach(function () {
+      App.get.restore();
+    });
+
+
+    it('Component is absent', function () {
+      controller.set('additionalConfigsMap', []);
+      var configs = {};
+
+      expect(controller.setAdditionalConfigs(configs, 'COMP1', '')).to.be.false;
+      expect(configs).to.eql({});
+    });
+    it('Component is present', function () {
+      controller.set('additionalConfigsMap', [
+        {
+          componentName: 'COMP1',
+          configs: {
+            'test-site': {
+              'property1': '<replace-value>:1111'
+            }
+          }
+        }
+      ]);
+      var configs = {
+        'test-site': {}
+      };
+
+      expect(controller.setAdditionalConfigs(configs, 'COMP1', 'host1')).to.be.true;
+      expect(configs).to.eql({
+        'test-site': {
+          'property1': 'host1:1111'
+        }
+      });
+    });
+    it('configs_Hadoop2 is present but isHadoop2Stack = false', function () {
+      isHadoop2Stack = false;
+      controller.set('additionalConfigsMap', [
+        {
+          componentName: 'COMP1',
+          configs: {
+            'test-site': {
+              'property1': '<replace-value>:1111'
+            }
+          },
+          configs_Hadoop2: {
+            'test-site': {
+              'property2': '<replace-value>:2222'
+            }
+          }
+        }
+      ]);
+      var configs = {
+        'test-site': {}
+      };
+
+      expect(controller.setAdditionalConfigs(configs, 'COMP1', 'host1')).to.be.true;
+      expect(configs).to.eql({
+        'test-site': {
+          'property1': 'host1:1111'
+        }
+      });
+    });
+    it('configs_Hadoop2 is present but isHadoop2Stack = true', function () {
+      isHadoop2Stack = true;
+      controller.set('additionalConfigsMap', [
+        {
+          componentName: 'COMP1',
+          configs: {
+            'test-site': {
+              'property1': '<replace-value>:1111'
+            }
+          },
+          configs_Hadoop2: {
+            'test-site': {
+              'property2': '<replace-value>:2222'
+            }
+          }
+        }
+      ]);
+      var configs = {
+        'test-site': {}
+      };
+
+      expect(controller.setAdditionalConfigs(configs, 'COMP1', 'host1')).to.be.true;
+      expect(configs).to.eql({
+        'test-site': {
+          'property2': 'host1:2222'
+        }
+      });
+    });
+  });
+
+  /*  describe('#loadStep()', function () {
+   var isHaEnabled = true;
+
+   beforeEach(function () {
+   controller.set('content.reassign.service_id', 'service1');
+   sinon.stub(controller, 'onTaskStatusChange', Em.K);
+   sinon.stub(App, 'get', function () {
+   return isHaEnabled;
+   });
+   });
+   afterEach(function () {
+   App.get.restore();
+   controller.onTaskStatusChange.restore();
+   });
+
+   it('reassign component is NameNode and HA enabled', function () {
+   isHaEnabled = true;
+   controller.set('content.reassign.component_name', 'NAMENODE');
+
+   controller.loadStep();
+   expect(controller.get('hostComponents')).to.eql(['NAMENODE', 'ZKFC']);
+   expect(controller.get('restartYarnMRComponents')).to.be.false;
+   expect(controller.get('serviceName')).to.eql(['service1']);
+   });
+   it('reassign component is NameNode and HA disabled', function () {
+   isHaEnabled = false;
+   controller.set('content.reassign.component_name', 'NAMENODE');
+
+   controller.loadStep();
+   expect(controller.get('hostComponents')).to.eql(['NAMENODE']);
+   expect(controller.get('restartYarnMRComponents')).to.be.false;
+   expect(controller.get('serviceName')).to.eql(['service1']);
+   });
+   it('reassign component is JOBTRACKER and HA enabled', function () {
+   isHaEnabled = true;
+   controller.set('content.reassign.component_name', 'JOBTRACKER');
+
+   controller.loadStep();
+   expect(controller.get('hostComponents')).to.eql(['JOBTRACKER']);
+   expect(controller.get('restartYarnMRComponents')).to.be.true;
+   expect(controller.get('serviceName')).to.eql(['service1']);
+   });
+   it('reassign component is RESOURCEMANAGER and HA enabled', function () {
+   isHaEnabled = true;
+   controller.set('content.reassign.component_name', 'RESOURCEMANAGER');
+
+   controller.loadStep();
+   expect(controller.get('hostComponents')).to.eql(['RESOURCEMANAGER']);
+   expect(controller.get('restartYarnMRComponents')).to.be.true;
+   expect(controller.get('serviceName')).to.eql(['service1']);
+   });
+   });*/
+
+  describe('#getHostComponentsNames()', function () {
+    it('No host-components', function () {
+      controller.set('hostComponents', []);
+      expect(controller.getHostComponentsNames()).to.be.empty;
+    });
+    it('one host-components', function () {
+      controller.set('hostComponents', ['COMP1']);
+      expect(controller.getHostComponentsNames()).to.equal('Comp1');
+    });
+    it('ZKFC host-components', function () {
+      controller.set('hostComponents', ['COMP1', 'ZKFC']);
+      expect(controller.getHostComponentsNames()).to.equal('Comp1+ZKFC');
+    });
+  });
+
+  describe('#removeUnneededTasks()', function () {
+    var isHaEnabled = false;
+
+    beforeEach(function () {
+      sinon.stub(App, 'get', function () {
+        return isHaEnabled;
+      });
+      controller.set('tasks', [
+        {id: 1},
+        {id: 2},
+        {id: 3},
+        {id: 4},
+        {id: 5},
+        {id: 6},
+        {id: 7},
+        {id: 8},
+        {id: 9}
+      ]);
+    });
+    afterEach(function () {
+      App.get.restore();
+    });
+
+    it('hasManualSteps is false', function () {
+      controller.set('content.hasManualSteps', false);
+
+      controller.removeUnneededTasks();
+      expect(controller.get('tasks').mapProperty('id')).to.eql([1, 2, 3, 4, 5, 8, 9]);
+    });
+    it('reassign component is not NameNode and HA disabled', function () {
+      controller.set('content.hasManualSteps', true);
+      controller.set('content.reassign.component_name', 'COMP1');
+      isHaEnabled = false;
+
+      controller.removeUnneededTasks();
+      expect(controller.get('tasks').mapProperty('id')).to.eql([1, 2, 3, 4, 5]);
+    });
+    it('reassign component is not NameNode and HA enabled', function () {
+      controller.set('content.hasManualSteps', true);
+      controller.set('content.reassign.component_name', 'COMP1');
+      isHaEnabled = true;
+
+      controller.removeUnneededTasks();
+      expect(controller.get('tasks').mapProperty('id')).to.eql([1, 2, 3, 4, 5]);
+    });
+    it('reassign component is NameNode and HA disabled', function () {
+      controller.set('content.hasManualSteps', true);
+      controller.set('content.reassign.component_name', 'NAMENODE');
+      isHaEnabled = false;
+
+      controller.removeUnneededTasks();
+      expect(controller.get('tasks').mapProperty('id')).to.eql([1, 2, 3, 4, 5]);
+    });
+    it('reassign component is NameNode and HA enabled', function () {
+      controller.set('content.hasManualSteps', true);
+      controller.set('content.reassign.component_name', 'NAMENODE');
+      isHaEnabled = true;
+
+      controller.removeUnneededTasks();
+      expect(controller.get('tasks').mapProperty('id')).to.eql([1, 2, 3, 4, 5, 6, 7]);
+    });
+  });
+
+  describe('#initializeTasks()', function () {
+    beforeEach(function () {
+      controller.set('tasks', []);
+      sinon.stub(controller, 'getHostComponentsNames', Em.K);
+      sinon.stub(controller, 'removeUnneededTasks', Em.K);
+    });
+    afterEach(function () {
+      controller.removeUnneededTasks.restore();
+      controller.getHostComponentsNames.restore();
+    });
+    it('No commands', function () {
+      controller.set('commands', []);
+      controller.initializeTasks();
+
+      expect(controller.get('tasks')).to.be.empty;
+    });
+    it('One command', function () {
+      controller.set('commands', ['COMMAND1']);
+      controller.initializeTasks();
+
+      expect(controller.get('tasks')[0].get('id')).to.equal(0);
+      expect(controller.get('tasks')[0].get('command')).to.equal('COMMAND1');
+    });
+  });
+
+  describe('#hideRollbackButton()', function () {
+
+    it('No showRollback command', function () {
+      controller.set('tasks', [Em.Object.create({
+        showRollback: false
+      })]);
+      controller.hideRollbackButton();
+      expect(controller.get('tasks')[0].get('showRollback')).to.be.false;
+    });
+    it('showRollback command is present', function () {
+      controller.set('tasks', [Em.Object.create({
+        showRollback: true
+      })]);
+      controller.hideRollbackButton();
+      expect(controller.get('tasks')[0].get('showRollback')).to.be.false;
+    });
+  });
+
+  describe('#onComponentsTasksSuccess()', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'onTaskCompleted', Em.K);
+    });
+    afterEach(function () {
+      controller.onTaskCompleted.restore();
+    });
+
+    it('No host-components', function () {
+      controller.set('multiTaskCounter', 0);
+      controller.set('hostComponents', []);
+      controller.onComponentsTasksSuccess();
+      expect(controller.get('multiTaskCounter')).to.equal(1);
+      expect(controller.onTaskCompleted.calledOnce).to.be.true;
+    });
+    it('One host-component', function () {
+      controller.set('multiTaskCounter', 0);
+      controller.set('hostComponents', [
+        {}
+      ]);
+      controller.onComponentsTasksSuccess();
+      expect(controller.get('multiTaskCounter')).to.equal(1);
+      expect(controller.onTaskCompleted.calledOnce).to.be.true;
+    });
+    it('two host-components', function () {
+      controller.set('multiTaskCounter', 0);
+      controller.set('hostComponents', [
+        {},
+        {}
+      ]);
+      controller.onComponentsTasksSuccess();
+      expect(controller.get('multiTaskCounter')).to.equal(1);
+      expect(controller.onTaskCompleted.called).to.be.false;
+    });
+  });
+
+  describe('#getStopServicesData()', function () {
+    it('restartYarnMRComponents is true', function () {
+      controller.set('restartYarnMRComponents', true);
+      sinon.stub(App.Service, 'find', function () {
+        return [
+          {
+            serviceName: 'HDFS'
+          },
+          {
+            serviceName: 'SERVICE1'
+          }
+        ];
+      });
+
+      expect(controller.getStopServicesData()).to.eql({
+        "ServiceInfo": {
+          "state": "INSTALLED"
+        },
+        "context": "Stop without HDFS",
+        "urlParams": "ServiceInfo/service_name.in(SERVICE1)"
+      });
+      App.Service.find.restore();
+    });
+    it('restartYarnMRComponents is false', function () {
+      controller.set('restartYarnMRComponents', false);
+      expect(controller.getStopServicesData()).to.eql({
+        "ServiceInfo": {
+          "state": "INSTALLED"
+        },
+        "context": "Stop all services"
+      });
+    });
+  });
+
+  describe('#stopServices()', function () {
+    it('', function () {
+      sinon.stub(controller, 'getStopServicesData', Em.K);
+
+      controller.stopServices();
+      expect(App.ajax.send.calledOnce).to.be.true;
+      expect(controller.getStopServicesData.calledOnce).to.be.true;
+
+      controller.getStopServicesData.restore();
+    });
+  });
+
+  describe('#createHostComponents()', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'createComponent', Em.K);
+    });
+    afterEach(function () {
+      controller.createComponent.restore();
+    });
+
+    it('No host-components', function () {
+      controller.set('hostComponents', []);
+
+      controller.createHostComponents();
+
+      expect(controller.get('multiTaskCounter')).to.equal(0);
+      expect(controller.createComponent.called).to.be.false;
+    });
+    it('One host-component', function () {
+      controller.set('hostComponents', ['COMP1']);
+      controller.set('content.reassignHosts.target', 'host1');
+      controller.set('content.reassign.service_id', 'SERVICE1');
+
+      controller.createHostComponents();
+
+      expect(controller.get('multiTaskCounter')).to.equal(0);
+      expect(controller.createComponent.calledWith('COMP1', 'host1', 'SERVICE1')).to.be.true;
+    });
+  });
+
+  describe('#onCreateComponent()', function () {
+    it('', function () {
+      sinon.stub(controller, 'onComponentsTasksSuccess', Em.K);
+      controller.onCreateComponent();
+      expect(controller.onComponentsTasksSuccess.calledOnce).to.be.true;
+      controller.onComponentsTasksSuccess.restore();
+    });
+  });
+
+  describe('#putHostComponentsInMaintenanceMode()', function () {
+    beforeEach(function(){
+      sinon.stub(controller, 'onComponentsTasksSuccess', Em.K);
+      controller.set('content.reassignHosts.source', 'source');
+    });
+    afterEach(function(){
+      controller.onComponentsTasksSuccess.restore();
+    });
+    it('No host-components', function () {
+      controller.set('hostComponents', []);
+      controller.putHostComponentsInMaintenanceMode();
+      expect(App.ajax.send.called).to.be.false;
+      expect(controller.get('multiTaskCounter')).to.equal(0);
+    });
+    it('One host-components', function () {
+      controller.set('hostComponents', [{}]);
+      controller.putHostComponentsInMaintenanceMode();
+      expect(App.ajax.send.calledOnce).to.be.true;
+      expect(controller.get('multiTaskCounter')).to.equal(0);
+    });
+  });
+
+  describe('#installHostComponents()', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'updateComponent', Em.K);
+    });
+    afterEach(function () {
+      controller.updateComponent.restore();
+    });
+
+    it('No host-components', function () {
+      controller.set('hostComponents', []);
+
+      controller.installHostComponents();
+
+      expect(controller.get('multiTaskCounter')).to.equal(0);
+      expect(controller.updateComponent.called).to.be.false;
+    });
+    it('One host-component', function () {
+      controller.set('hostComponents', ['COMP1']);
+      controller.set('content.reassignHosts.target', 'host1');
+      controller.set('content.reassign.service_id', 'SERVICE1');
+
+      controller.installHostComponents();
+
+      expect(controller.get('multiTaskCounter')).to.equal(0);
+      expect(controller.updateComponent.calledWith('COMP1', 'host1', 'SERVICE1', 'Install', 1)).to.be.true;
+    });
+  });
+
+  describe('#reconfigure()', function () {
+    it('', function () {
+      sinon.stub(controller, 'loadConfigsTags', Em.K);
+      controller.reconfigure();
+      expect(controller.loadConfigsTags.calledOnce).to.be.true;
+      controller.loadConfigsTags.restore();
+    });
+  });
+
+  describe('#loadConfigsTags()', function () {
+    it('', function () {
+      controller.loadConfigsTags();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#getConfigUrlParams()', function () {
+    var testCases  = [
+      {
+        componentName: 'NAMENODE',
+        result: [
+          "(type=hdfs-site&tag=1)",
+          "(type=core-site&tag=2)"
+        ]
+      },
+      {
+        componentName: 'SECONDARY_NAMENODE',
+        result: [
+          "(type=hdfs-site&tag=1)",
+          "(type=core-site&tag=2)"
+        ]
+      },
+      {
+        componentName: 'JOBTRACKER',
+        result: [
+          "(type=mapred-site&tag=4)"
+        ]
+      },
+      {
+        componentName: 'RESOURCEMANAGER',
+        result: [
+          "(type=yarn-site&tag=5)"
+        ]
+      }
+    ];
+
+    var data = {
+      Clusters: {
+        desired_configs: {
+          'hdfs-site': {tag: 1},
+          'core-site': {tag: 2},
+          'hbase-site': {tag: 3},
+          'mapred-site': {tag: 4},
+          'yarn-site': {tag: 5}
+        }
+      }
+    };
+
+    var services = [];
+
+    beforeEach(function () {
+      sinon.stub(App.Service, 'find', function () {
+        return services;
+      })
+    });
+    afterEach(function () {
+      App.Service.find.restore();
+    });
+
+    testCases.forEach(function (test) {
+      it('get config of ' + test.componentName, function () {
+        expect(controller.getConfigUrlParams(test.componentName, data)).to.eql(test.result);
+      })
+    });
+    it('get config of NAMENODE when HBASE installed', function () {
+      services = [
+        {
+          serviceName: 'HBASE'
+        }
+      ];
+      expect(controller.getConfigUrlParams('NAMENODE', data)).to.eql([
+        "(type=hdfs-site&tag=1)",
+        "(type=core-site&tag=2)",
+        "(type=hbase-site&tag=3)"
+      ]);
+    })
+  });
+
+  describe('#onLoadConfigsTags()', function () {
+    it('', function () {
+      sinon.stub(controller, 'getConfigUrlParams', function () {
+        return [];
+      });
+      controller.set('content.reassign.component_name', 'COMP1');
+
+      controller.onLoadConfigsTags({});
+      expect(App.ajax.send.calledOnce).to.be.true;
+      expect(controller.getConfigUrlParams.calledWith('COMP1', {})).to.be.true;
+
+      controller.getConfigUrlParams.restore();
+    });
+  });
+
+  describe('#onLoadConfigs()', function () {
+    beforeEach(function () {
+      sinon.stub(controller, 'setAdditionalConfigs', Em.K);
+      sinon.stub(controller, 'setSecureConfigs', Em.K);
+      sinon.stub(controller, 'setSpecificNamenodeConfigs', Em.K);
+      sinon.stub(controller, 'getComponentDir', Em.K);
+      sinon.stub(controller, 'saveClusterStatus', Em.K);
+      sinon.stub(controller, 'saveConfigsToServer', Em.K);
+      controller.set('content.reassignHosts.target', 'host1');
+    });
+    afterEach(function () {
+      controller.setAdditionalConfigs.restore();
+      controller.setSecureConfigs.restore();
+      controller.setSpecificNamenodeConfigs.restore();
+      controller.getComponentDir.restore();
+      controller.saveClusterStatus.restore();
+      controller.saveConfigsToServer.restore();
+    });
+
+    it('component is not NAMENODE', function () {
+      controller.set('content.reassign.component_name', 'COMP1');
+
+      controller.onLoadConfigs({items: []});
+      expect(controller.get('configsSitesNumber')).to.equal(0);
+      expect(controller.get('configsSitesCount')).to.equal(0);
+      expect(controller.setAdditionalConfigs.calledWith({}, 'COMP1', 'host1')).to.be.true;
+      expect(controller.setSecureConfigs.calledWith([], {}, 'COMP1')).to.be.true;
+      expect(controller.setSpecificNamenodeConfigs.called).to.be.false;
+      expect(controller.getComponentDir.calledWith({}, 'COMP1')).to.be.true;
+      expect(controller.saveClusterStatus.calledWith([])).to.be.true;
+      expect(controller.saveConfigsToServer.calledWith({})).to.be.true;
+    });
+    it('component is NAMENODE, has configs', function () {
+      controller.set('content.reassign.component_name', 'NAMENODE');
+
+      controller.onLoadConfigs({items: [
+        {
+          type: 'hdfs-site',
+          properties: {}
+        }
+      ]});
+      expect(controller.get('configsSitesNumber')).to.equal(1);
+      expect(controller.get('configsSitesCount')).to.equal(0);
+      expect(controller.setAdditionalConfigs.calledWith({'hdfs-site': {}}, 'NAMENODE', 'host1')).to.be.true;
+      expect(controller.setSecureConfigs.calledWith([], {'hdfs-site': {}}, 'NAMENODE')).to.be.true;
+      expect(controller.setSpecificNamenodeConfigs.calledWith({'hdfs-site': {}}, 'host1')).to.be.true;
+      expect(controller.getComponentDir.calledWith({'hdfs-site': {}}, 'NAMENODE')).to.be.true;
+      expect(controller.saveClusterStatus.calledWith([])).to.be.true;
+      expect(controller.saveConfigsToServer.calledWith({'hdfs-site': {}})).to.be.true;
+    });
+  });
+
+  describe('#saveConfigsToServer()', function () {
+    it('configs is empty', function () {
+      controller.saveConfigsToServer({});
+      expect(App.ajax.send.called).to.be.false;
+    });
+    it('configs has one site', function () {
+      controller.saveConfigsToServer({'hdfs-site': {}});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+    it('configs has two sites', function () {
+      controller.saveConfigsToServer({
+        'hdfs-site': {},
+        'core-site': {}
+      });
+      expect(App.ajax.send.calledTwice).to.be.true;
+    });
+  });
+
+ /* describe('#setSpecificNamenodeConfigs()', function () {
+   it('configs is empty', function () {
+   controller.setSpecificNamenodeConfigs();
+   });
+   });*/
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/0f4f7d7c/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 7b188af..ab49097 100644
--- a/ambari-web/test/controllers/wizard/step5_test.js
+++ b/ambari-web/test/controllers/wizard/step5_test.js
@@ -30,49 +30,6 @@ describe('App.WizardStep5Controller', function () {
   controller.set('content', {});
   var cpu = 2, memory = 4;
 
-  describe('#getHostForComponent()', function () {
-    before(function () {
-      modelSetup.setupStackServiceComponent();
-    });
-
-    var componentHostsGenerator = function (componentName, hosts) {
-        it('test hosts input valid', function () {
-          expect(hosts.length).to.eql(5);
-        });
-        return {
-          componentName: componentName,
-          expectedLocation: hosts
-        };
-      },
-      hostsCount = [1, 3, 6, 10, 31],
-      tests = [
-        componentHostsGenerator('NAMENODE', ['host0', 'host0', 'host0', 'host0', 'host0']),
-        componentHostsGenerator('SECONDARY_NAMENODE', ['host0', 'host1', 'host1', 'host1', 'host1']),
-        componentHostsGenerator('HBASE_MASTER', ['host0', 'host0', 'host2', 'host2', 'host3']),
-        componentHostsGenerator('JOBTRACKER', ['host0', 'host1', 'host1', 'host1', 'host2']),
-        componentHostsGenerator('OOZIE_SERVER', ['host0', 'host1', 'host2', 'host2', 'host3']),
-        componentHostsGenerator('HIVE_SERVER', ['host0', 'host1', 'host2', 'host2', 'host4']),
-        componentHostsGenerator('STORM_UI_SERVER', ['host0', 'host0', 'host0', 'host0', 'host0'])
-      ],
-      testMessage = 'should locate `{0}` to `{1}` with {2} node cluster';
-
-    tests.forEach(function (test) {
-      var componentName = test.componentName;
-      hostsCount.forEach(function (count, index) {
-        it(testMessage.format(componentName, test.expectedLocation[index], count), function () {
-          var hosts = Array.apply(null, Array(count)).map(function (_, i) {
-            return 'host' + i;
-          });
-          expect(controller.getHostForComponent(test.componentName, hosts)).to.eql(test.expectedLocation[index]);
-        })
-      });
-    });
-
-    after(function () {
-      modelSetup.cleanStackServiceComponent();
-    });
-  });
-
   controller.set('content', {});
 
   describe('#isReassignWizard', function () {
@@ -218,88 +175,6 @@ describe('App.WizardStep5Controller', function () {
 
   });
 
-  describe('#selectHost', function () {
-    before(function () {
-      modelSetup.setupStackServiceComponent();
-      App.store.load(App.StackServiceComponent, {
-        id: 'KERBEROS_SERVER',
-        component_name: 'KERBEROS_SERVER'
-      });
-    });
-
-    var tests = Em.A([
-      {componentName: 'NAMENODE', hostsCount: 1, e: 'host1'},
-      {componentName: 'NAMENODE', hostsCount: 2, e: 'host1'},
-      {componentName: 'SECONDARY_NAMENODE', hostsCount: 1, e: 'host1'},
-      {componentName: 'SECONDARY_NAMENODE', hostsCount: 2, e: 'host2'},
-      {componentName: 'JOBTRACKER', hostsCount: 1, e: 'host1'},
-      {componentName: 'JOBTRACKER', hostsCount: 3, e: 'host2'},
-      {componentName: 'JOBTRACKER', hostsCount: 6, e: 'host2'},
-      {componentName: 'JOBTRACKER', hostsCount: 31, e: 'host3'},
-      {componentName: 'JOBTRACKER', hostsCount: 32, e: 'host3'},
-      {componentName: 'HISTORYSERVER', hostsCount: 1, e: 'host1'},
-      {componentName: 'HISTORYSERVER', hostsCount: 3, e: 'host2'},
-      {componentName: 'HISTORYSERVER', hostsCount: 6, e: 'host2'},
-      {componentName: 'HISTORYSERVER', hostsCount: 31, e: 'host3'},
-      {componentName: 'HISTORYSERVER', hostsCount: 32, e: 'host3'},
-      {componentName: 'RESOURCEMANAGER', hostsCount: 1, e: 'host1'},
-      {componentName: 'RESOURCEMANAGER', hostsCount: 3, e: 'host2'},
-      {componentName: 'RESOURCEMANAGER', hostsCount: 6, e: 'host2'},
-      {componentName: 'RESOURCEMANAGER', hostsCount: 31, e: 'host3'},
-      {componentName: 'RESOURCEMANAGER', hostsCount: 32, e: 'host3'},
-      {componentName: 'HBASE_MASTER', hostsCount: 1, e: ['host1']},
-      {componentName: 'HBASE_MASTER', hostsCount: 3, e: ['host1']},
-      {componentName: 'HBASE_MASTER', hostsCount: 6, e: ['host3']},
-      {componentName: 'HBASE_MASTER', hostsCount: 31, e: ['host4']},
-      {componentName: 'HBASE_MASTER', hostsCount: 32, e: ['host4']},
-      {componentName: 'OOZIE_SERVER', hostsCount: 1, e: 'host1'},
-      {componentName: 'OOZIE_SERVER', hostsCount: 3, e: 'host2'},
-      {componentName: 'OOZIE_SERVER', hostsCount: 6, e: 'host3'},
-      {componentName: 'OOZIE_SERVER', hostsCount: 31, e: 'host4'},
-      {componentName: 'OOZIE_SERVER', hostsCount: 32, e: 'host4'},
-      {componentName: 'HIVE_SERVER', hostsCount: 1, e: 'host1'},
-      {componentName: 'HIVE_SERVER', hostsCount: 3, e: 'host2'},
-      {componentName: 'HIVE_SERVER', hostsCount: 6, e: 'host3'},
-      {componentName: 'HIVE_SERVER', hostsCount: 31, e: 'host5'},
-      {componentName: 'HIVE_SERVER', hostsCount: 32, e: 'host5'},
-      {componentName: 'HIVE_METASTORE', hostsCount: 1, e: 'host1'},
-      {componentName: 'HIVE_METASTORE', hostsCount: 3, e: 'host2'},
-      {componentName: 'HIVE_METASTORE', hostsCount: 6, e: 'host3'},
-      {componentName: 'HIVE_METASTORE', hostsCount: 31, e: 'host5'},
-      {componentName: 'HIVE_METASTORE', hostsCount: 32, e: 'host5'},
-      {componentName: 'WEBHCAT_SERVER', hostsCount: 1, e: 'host1'},
-      {componentName: 'WEBHCAT_SERVER', hostsCount: 3, e: 'host2'},
-      {componentName: 'WEBHCAT_SERVER', hostsCount: 6, e: 'host3'},
-      {componentName: 'WEBHCAT_SERVER', hostsCount: 31, e: 'host5'},
-      {componentName: 'WEBHCAT_SERVER', hostsCount: 32, e: 'host5'},
-      {componentName: 'APP_TIMELINE_SERVER', hostsCount: 1, e: 'host1'},
-      {componentName: 'APP_TIMELINE_SERVER', hostsCount: 3, e: 'host2'},
-      {componentName: 'APP_TIMELINE_SERVER', hostsCount: 6, e: 'host2'},
-      {componentName: 'APP_TIMELINE_SERVER', hostsCount: 31, e: 'host3'},
-      {componentName: 'APP_TIMELINE_SERVER', hostsCount: 32, e: 'host3'},
-      {componentName: 'FALCON_SERVER', hostsCount: 1, e: 'host1'},
-      {componentName: 'FALCON_SERVER', hostsCount: 3, e: 'host2'},
-      {componentName: 'FALCON_SERVER', hostsCount: 6, e: 'host3'},
-      {componentName: 'FALCON_SERVER', hostsCount: 31, e: 'host4'},
-      {componentName: 'FALCON_SERVER', hostsCount: 32, e: 'host4'}
-    ]);
-
-    tests.forEach(function (test) {
-      it(test.componentName + ' ' + test.hostsCount, function () {
-        controller.reopen({multipleComponents: ['HBASE_MASTER', 'ZOOKEEPER_SERVER']});
-        controller.set('hosts', d3.range(1, test.hostsCount + 1).map(function (i) {
-          return {host_name: 'host' + i.toString()};
-        }));
-        expect(controller.selectHost(test.componentName)).to.eql(test.e);
-      });
-    });
-
-    after(function () {
-      modelSetup.cleanStackServiceComponent();
-    });
-
-  });
-
   describe('#last', function () {
 
     var tests = Em.A([