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/04/28 13:25:49 UTC

[1/2] AMBARI-5587 UI unit tests for host controllers. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk b698c294b -> 5c49faf56


http://git-wip-us.apache.org/repos/asf/ambari/blob/5c49faf5/ambari-web/test/controllers/main/host/details_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js
new file mode 100644
index 0000000..3d6bb2a
--- /dev/null
+++ b/ambari-web/test/controllers/main/host/details_test.js
@@ -0,0 +1,1516 @@
+/**
+ * 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');
+require('controllers/main/host/details');
+require('models/service');
+require('models/host_component');
+var batchUtils = require('utils/batch_scheduled_requests');
+
+
+describe('App.MainHostDetailsController', function () {
+
+  var controller = App.MainHostDetailsController.create({
+    getSecurityStatus: function () {
+      return this.get('mockSecurityStatus');
+    },
+    mockSecurityStatus: false
+  });
+
+  describe('#serviceActiveComponents', function () {
+
+    it('No host-components', function () {
+      controller.set('content.hostComponents', []);
+      expect(controller.get('serviceActiveComponents')).to.be.empty;
+    });
+
+    it('No host-components in active state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        service: {
+          isInPassive: true
+        }
+      })]);
+      expect(controller.get('serviceActiveComponents')).to.be.empty;
+    });
+    it('Host-components in active state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        service: {
+          isInPassive: false
+        }
+      })]);
+      expect(controller.get('serviceActiveComponents')).to.eql([Em.Object.create({
+        service: {
+          isInPassive: false
+        }
+      })]);
+    });
+  });
+
+  describe('#serviceNonClientActiveComponents', function () {
+
+    it('No active host-components', function () {
+      controller.reopen({
+        serviceActiveComponents: []
+      });
+      controller.set('serviceActiveComponents', []);
+      expect(controller.get('serviceNonClientActiveComponents')).to.be.empty;
+    });
+
+    it('Active host-component is client', function () {
+      controller.set('serviceActiveComponents', [Em.Object.create({
+        isClient: true
+      })]);
+      expect(controller.get('serviceNonClientActiveComponents')).to.be.empty;
+    });
+    it('Active host-component is not client', function () {
+      controller.set('serviceActiveComponents', [Em.Object.create({
+        isClient: false
+      })]);
+      expect(controller.get('serviceNonClientActiveComponents')).to.eql( [Em.Object.create({
+        isClient: false
+      })]);
+    });
+  });
+
+  describe('#startComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.startComponent({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.sendStartComponentCommand.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#sendStartComponentCommand()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'comp1'
+      });
+      controller.sendStartComponentCommand(component, '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#getDataToSend()', function () {
+    it('should return correct query info', function () {
+      expect(controller.getDataToSend('STATE1', 'context')).to.eql({
+        RequestInfo: {
+          "context": 'context'
+        },
+        Body: {
+          HostRoles: {
+            state: 'STATE1'
+          }
+        }
+      });
+    });
+  });
+
+  describe('#setComponentToData()', function () {
+    var testCases = [
+      {
+        title: 'dataToSend is null',
+        content: {
+          data: {},
+          dataToSend: null,
+          component: {}
+        },
+        result: {
+          output: false,
+          data: {}
+        }
+      },
+      {
+        title: 'component is null',
+        content: {
+          data: {},
+          dataToSend: {},
+          component: null
+        },
+        result: {
+          output: false,
+          data: {}
+        }
+      },
+      {
+        title: 'data is null',
+        content: {
+          data: null,
+          dataToSend: {},
+          component: {}
+        },
+        result: {
+          output: false,
+          data: null
+        }
+      },
+      {
+        title: 'component is object',
+        content: {
+          data: {},
+          dataToSend: {},
+          component: Em.Object.create({componentName: 'comp1'})
+        },
+        result: {
+          output: true,
+          data: {
+            componentName: 'comp1',
+            data: '{}'
+          }
+        }
+      },
+      {
+        title: 'component is array',
+        content: {
+          data: {},
+          dataToSend: {RequestInfo: {}},
+          component: [Em.Object.create({componentName: 'comp1'})]
+        },
+        result: {
+          output: true,
+          data: {
+            data: '{\"RequestInfo\":{\"query\":\"HostRoles/component_name.in(comp1)\"}}'
+          }
+        }
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.setComponentToData(test.content.data, test.content.dataToSend, test.content.component)).to.equal(test.result.output);
+        expect(test.content.data).to.eql(test.result.data);
+      });
+    });
+  });
+
+  describe('#deleteComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, "show");
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+    });
+
+    it('confirm popup should be displayed', function () {
+      var event = {
+        context: Em.Object.create({})
+      };
+      controller.deleteComponent(event);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#_doDeleteHostComponent()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('delete request should be sent, component is null', function () {
+      controller.set('_deletedHostComponentResult', null);
+      expect(controller._doDeleteHostComponent()).to.equal(null);
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+    it('delete request should be sent, component is correct', function () {
+      controller.set('_deletedHostComponentResult', {});
+      expect(controller._doDeleteHostComponent(Em.Object.create({componentName: 'COMP1'}))).to.eql({});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#mimicWorkStatusChange()', function () {
+
+    var clock;
+    beforeEach(function () {
+      clock = sinon.useFakeTimers();
+    });
+    afterEach(function () {
+      clock.restore()
+    });
+
+    it('change status of object', function () {
+      var entity = Em.Object.create({
+        workStatus: ''
+      });
+      controller.mimicWorkStatusChange(entity, 'STATE1', 'STATE2');
+      expect(entity.get('workStatus')).to.equal('STATE1');
+      clock.tick(App.testModeDelayForActions);
+      expect(entity.get('workStatus')).to.equal('STATE2');
+    });
+    it('change status of objects in array', function () {
+      var entity = [Em.Object.create({
+        workStatus: ''
+      })];
+      controller.mimicWorkStatusChange(entity, 'STATE1', 'STATE2');
+      expect(entity[0].get('workStatus')).to.equal('STATE1');
+      clock.tick(App.testModeDelayForActions);
+      expect(entity[0].get('workStatus')).to.equal('STATE2');
+    });
+  });
+
+  describe('#upgradeComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      App.ajax.send.restore();
+    });
+
+    it('confirm popup should be displayed', function () {
+      var popup = controller.upgradeComponent({context: Em.Object.create()});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#stopComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "sendStopComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStopComponentCommand.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.stopComponent({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.sendStopComponentCommand.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#sendStopComponentCommand()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'comp1'
+      });
+      controller.sendStopComponentCommand(component, '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#restartComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(batchUtils, "restartHostComponents", Em.K);
+    });
+
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      batchUtils.restartHostComponents.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.restartComponent({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(batchUtils.restartHostComponents.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#addComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "addClientComponent", Em.K);
+      sinon.stub(controller, "primary", Em.K);
+    });
+
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.addClientComponent.restore();
+      controller.primary.restore();
+    });
+
+    it('add ZOOKEEPER_SERVER', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER'
+      })};
+      var popup = controller.addComponent(event);
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER'
+      }))).to.be.true;
+    });
+    it('add slave component, securityEnabled = true', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'COMP1'
+      })};
+      controller.set('mockSecurityStatus', true);
+      var popup = controller.addComponent(event);
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({
+        componentName: 'COMP1'
+      }))).to.be.true;
+    });
+    it('add slave component, securityEnabled = false', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'COMP1'
+      })};
+      controller.set('mockSecurityStatus', false);
+      controller.addComponent(event);
+      expect(controller.addClientComponent.calledWith(Em.Object.create({
+        componentName: 'COMP1'
+      }))).to.be.true;
+    });
+    it('add CLIENTS', function () {
+      var event = {context: Em.Object.create({
+        componentName: 'CLIENTS'
+      })};
+      controller.set('mockSecurityStatus', true);
+      controller.addComponent(event);
+      expect(controller.addClientComponent.calledWith(Em.Object.create({
+        componentName: 'CLIENTS'
+      }))).to.be.true;
+    });
+  });
+
+  describe('#formatClientsMessage()', function () {
+    var testCases = [
+      {
+        title: 'subComponentNames is null',
+        client: Em.Object.create({
+          subComponentNames: null,
+          displayName: 'CLIENTS'
+        }),
+        result: 'CLIENTS'
+      },
+      {
+        title: 'subComponentNames is empty',
+        client: Em.Object.create({
+          subComponentNames: [],
+          displayName: 'CLIENTS'
+        }),
+        result: 'CLIENTS'
+      },
+      {
+        title: 'displayName is null',
+        client: Em.Object.create({
+          subComponentNames: ['DATANODE'],
+          displayName: null
+        }),
+        result: ' (DataNode)'
+      },
+      {
+        title: 'displayName is CLIENTS',
+        client: Em.Object.create({
+          subComponentNames: ['DATANODE'],
+          displayName: 'CLIENTS'
+        }),
+        result: 'CLIENTS (DataNode)'
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.formatClientsMessage(test.client)).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#addClientComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, "show");
+      sinon.stub(controller, "primary", Em.K);
+    });
+
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      controller.primary.restore();
+    });
+
+    it('not CLIENT component', function () {
+      var component = Em.Object.create({'componentName': 'Comp1'});
+      var popup = controller.addClientComponent(component);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledWith(Em.Object.create({'componentName': 'Comp1'}))).to.be.true;
+    });
+    it('CLIENT components, with empty subComponentNames', function () {
+      var component = Em.Object.create({
+        componentName: 'CLIENTS',
+        subComponentNames: []
+      });
+      var popup = controller.addClientComponent(component);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledOnce).to.be.false;
+    });
+    it('CLIENT components, with two sub-component', function () {
+      var component = Em.Object.create({
+        componentName: 'CLIENTS',
+        subComponentNames: ['DATANODE', 'TASKTRACKER']
+      });
+      var popup = controller.addClientComponent(component);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.primary.calledTwice).to.be.true;
+    });
+  });
+
+  describe('#primary()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        componentName: 'COMP1',
+        displayName: 'comp1'
+      });
+      controller.primary(component);
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#addNewComponentSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.addNewComponentSuccessCallback({}, {}, {component: Em.Object.create()});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#installNewComponentSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+    afterEach(function () {
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('data.Requests is null', function () {
+      var data = {Requests: null};
+      expect(controller.installNewComponentSuccessCallback(data, {}, {})).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
+    it('data.Requests.id is null', function () {
+      var data = {Requests: {id: null}};
+      expect(controller.installNewComponentSuccessCallback(data, {}, {})).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
+    it('data.Requests.id is correct', function () {
+      var data = {Requests: {id: 1}};
+      expect(controller.installNewComponentSuccessCallback(data, {}, {component: []})).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#refreshComponentConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "sendRefreshComponentConfigsCommand", Em.K);
+    });
+
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendRefreshComponentConfigsCommand.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.refreshComponentConfigs({context: Em.Object.create({'displayName': 'Comp1'})});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.sendRefreshComponentConfigsCommand.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#sendRefreshComponentConfigsCommand()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      var component = Em.Object.create({
+        service: {},
+        componentName: 'COMP1',
+        host: {}
+      });
+      controller.sendRefreshComponentConfigsCommand(component, {});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#loadConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.loadConfigs();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#constructConfigUrlParams()', function () {
+
+    it('URL params should be empty', function () {
+      var data = {};
+      App.Service.find().clear();
+      expect(controller.constructConfigUrlParams(data)).to.eql([]);
+    });
+    it('isHaEnabled = true', function () {
+      var data = {Clusters: {desired_configs: {'core-site': {tag: 1}}}};
+      App.HostComponent.find().clear();
+      App.set('currentStackVersion', 'HDP-2.0.2');
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=core-site&tag=1)']);
+      App.store.load(App.HostComponent, {
+        id: 'SECONDARY_NAMENODE_host1',
+        component_name: 'SECONDARY_NAMENODE'
+      });
+      App.set('currentStackVersion', 'HDP-2.0.1');
+    });
+    it('HBASE is installed', function () {
+      App.store.load(App.Service, {
+        id: 'HBASE',
+        service_name: 'HBASE'
+      });
+      var data = {Clusters: {desired_configs: {'hbase-site': {tag: 1}}}};
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=hbase-site&tag=1)']);
+      App.Service.find().clear();
+    });
+    it('HIVE is installed', function () {
+      App.store.load(App.Service, {
+        id: 'HIVE',
+        service_name: 'HIVE'
+      });
+      var data = {Clusters: {desired_configs: {'webhcat-site': {tag: 1}}}};
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=webhcat-site&tag=1)']);
+      App.Service.find().clear();
+    });
+    it('STORM is installed', function () {
+      App.store.load(App.Service, {
+        id: 'STORM',
+        service_name: 'STORM'
+      });
+      var data = {Clusters: {desired_configs: {'storm-site': {tag: 1}}}};
+      expect(controller.constructConfigUrlParams(data)).to.eql(['(type=storm-site&tag=1)']);
+      App.Service.find().clear();
+    });
+  });
+
+  describe('#loadConfigsSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+      sinon.stub(controller, "constructConfigUrlParams", function () {
+        return this.get('mockUrlParams');
+      });
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+      controller.constructConfigUrlParams.restore();
+    });
+
+    it('url params is empty', function () {
+      controller.set('mockUrlParams', []);
+      expect(controller.loadConfigsSuccessCallback()).to.be.false;
+      expect(App.ajax.send.called).to.be.false;
+    });
+    it('url params are correct', function () {
+      controller.set('mockUrlParams', ['param1']);
+      expect(controller.loadConfigsSuccessCallback()).to.be.true;
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#saveZkConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "getZkServerHosts", Em.K);
+      sinon.stub(controller, "concatZkNames", Em.K);
+      sinon.stub(controller, "setZKConfigs", Em.K);
+      sinon.spy(App.ajax, "send");
+    });
+    afterEach(function () {
+      controller.getZkServerHosts.restore();
+      controller.concatZkNames.restore();
+      controller.setZKConfigs.restore();
+      App.ajax.send.restore();
+    });
+
+    it('data.items is empty', function () {
+      var data = {items: []};
+      controller.saveZkConfigs(data);
+      expect(App.ajax.send.called).to.be.false;
+    });
+    it('data.items has one item', function () {
+      var data = {items: [
+        {
+          type: 'type1',
+          properties: {}
+        }
+      ]};
+      controller.saveZkConfigs(data);
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+    it('data.items has two items', function () {
+      var data = {items: [
+        {
+          type: 'type1',
+          properties: {}
+        },
+        {
+          type: 'type2',
+          properties: {}
+        }
+      ]};
+      controller.saveZkConfigs(data);
+      expect(App.ajax.send.calledTwice).to.be.true;
+    });
+  });
+
+  describe('#setZKConfigs()', function () {
+
+    it('configs is null', function () {
+      expect(controller.setZKConfigs(null)).to.be.false;
+    });
+    it('zks is null', function () {
+      expect(controller.setZKConfigs({}, '', null)).to.be.false;
+    });
+    it('isHaEnabled = true', function () {
+      var configs = {'core-site': {}};
+      App.HostComponent.find().clear();
+      App.set('currentStackVersion', 'HDP-2.0.2');
+      expect(controller.setZKConfigs(configs, 'host1:2181', [])).to.be.true;
+      expect(configs).to.eql({"core-site": {
+        "ha.zookeeper.quorum": "host1:2181"
+      }});
+      App.store.load(App.HostComponent, {
+        id: 'SECONDARY_NAMENODE_host1',
+        component_name: 'SECONDARY_NAMENODE'
+      });
+      App.set('currentStackVersion', 'HDP-2.0.1');
+    });
+    it('hbase-site is present', function () {
+      var configs = {'hbase-site': {}};
+      expect(controller.setZKConfigs(configs, '', ['host1', 'host2'])).to.be.true;
+      expect(configs).to.eql({"hbase-site": {
+        "hbase.zookeeper.quorum": "host1,host2"
+      }});
+    });
+    it('webhcat-site is present', function () {
+      var configs = {'webhcat-site': {}};
+      expect(controller.setZKConfigs(configs, 'host1:2181', [])).to.be.true;
+      expect(configs).to.eql({"webhcat-site": {
+        "templeton.zookeeper.hosts": "host1:2181"
+      }});
+    });
+    it('storm-site is present', function () {
+      var configs = {'storm-site': {}};
+      expect(controller.setZKConfigs(configs, '', ["host1", 'host2'])).to.be.true;
+      expect(configs).to.eql({"storm-site": {
+        "storm.zookeeper.servers": "['host1','host2']"
+      }});
+    });
+  });
+
+  describe('#concatZkNames()', function () {
+    it('No ZooKeeper hosts', function () {
+      expect(controller.concatZkNames([])).to.equal('');
+    });
+    it('One ZooKeeper host', function () {
+      expect(controller.concatZkNames(['host1'])).to.equal('host1:2181');
+    });
+    it('Two ZooKeeper hosts', function () {
+      expect(controller.concatZkNames(['host1', 'host2'])).to.equal('host1:2181,host2:2181');
+    });
+  });
+
+  describe('#getZkServerHosts()', function () {
+    controller.set('content', {});
+
+    it('No ZooKeeper hosts, fromDeleteHost = false', function () {
+      App.HostComponent.find().clear();
+      controller.set('fromDeleteHost', false);
+      expect(controller.getZkServerHosts()).to.be.empty;
+    });
+    it('No ZooKeeper hosts, fromDeleteHost = true', function () {
+      controller.set('fromDeleteHost', true);
+      expect(controller.getZkServerHosts()).to.be.empty;
+      expect(controller.get('fromDeleteHost')).to.be.false;
+    });
+    it('One ZooKeeper host, fromDeleteHost = false', function () {
+      controller.set('fromDeleteHost', false);
+      App.store.load(App.HostComponent, {
+        id: 'ZOOKEEPER_SERVER_host1',
+        component_name: 'ZOOKEEPER_SERVER',
+        host_id: 'host1'
+      });
+      expect(controller.getZkServerHosts()).to.eql(['host1']);
+    });
+    it('One ZooKeeper host match current host name, fromDeleteHost = true', function () {
+      controller.set('fromDeleteHost', true);
+      controller.set('content.hostName', 'host1');
+      expect(controller.getZkServerHosts()).to.be.empty;
+      expect(controller.get('fromDeleteHost')).to.be.false;
+    });
+    it('One ZooKeeper host does not match current host name, fromDeleteHost = true', function () {
+      controller.set('fromDeleteHost', true);
+      controller.set('content.hostName', 'host2');
+      expect(controller.getZkServerHosts()[0]).to.equal("host1");
+      expect(controller.get('fromDeleteHost')).to.be.false;
+    });
+  });
+
+  describe('#installComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, "show");
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      App.ajax.send.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var event = {context: Em.Object.create()};
+      var popup = controller.installComponent(event);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#decommission()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "runDecommission", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.runDecommission.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.decommission(Em.Object.create({service: {}}));
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.runDecommission.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#runDecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "doDecommission", Em.K);
+      sinon.stub(controller, "warnBeforeDecommission", Em.K);
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+
+    afterEach(function () {
+      controller.doDecommission.restore();
+      controller.warnBeforeDecommission.restore();
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('HDFS service', function () {
+      controller.runDecommission('host1', 'HDFS');
+      expect(controller.doDecommission.calledWith('host1', 'HDFS', "NAMENODE", "DATANODE")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('YARN service', function () {
+      controller.runDecommission('host1', 'YARN');
+      expect(controller.doDecommission.calledWith('host1', 'YARN', "RESOURCEMANAGER", "NODEMANAGER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('MAPREDUCE service', function () {
+      controller.runDecommission('host1', 'MAPREDUCE');
+      expect(controller.doDecommission.calledWith('host1', 'MAPREDUCE', "JOBTRACKER", "TASKTRACKER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('HBASE service', function () {
+      controller.runDecommission('host1', 'HBASE');
+      expect(controller.warnBeforeDecommission.calledWith('host1', 'HBASE', "HBASE_MASTER", "HBASE_REGIONSERVER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#runRecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "doRecommissionAndStart", Em.K);
+      sinon.stub(controller, "doRecommissionAndRestart", Em.K);
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+
+    afterEach(function () {
+      controller.doRecommissionAndStart.restore();
+      controller.doRecommissionAndRestart.restore();
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('HDFS service', function () {
+      controller.runRecommission('host1', 'HDFS');
+      expect(controller.doRecommissionAndStart.calledWith('host1', 'HDFS', "NAMENODE", "DATANODE")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('YARN service', function () {
+      controller.runRecommission('host1', 'YARN');
+      expect(controller.doRecommissionAndStart.calledWith('host1', 'YARN', "RESOURCEMANAGER", "NODEMANAGER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('MAPREDUCE service', function () {
+      controller.runRecommission('host1', 'MAPREDUCE');
+      expect(controller.doRecommissionAndRestart.calledWith('host1', 'MAPREDUCE', "JOBTRACKER", "TASKTRACKER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('HBASE service', function () {
+      controller.runRecommission('host1', 'HBASE');
+      expect(controller.doRecommissionAndStart.calledWith('host1', 'HBASE', "HBASE_MASTER", "HBASE_REGIONSERVER")).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doDecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doDecommission('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doDecommissionRegionServer()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doDecommissionRegionServer('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  /**
+   * TODO uncomment test when final rules will be implemented into warnBeforeDecommission function
+   */
+ /* describe('#warnBeforeDecommission()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "doDecommissionRegionServer", Em.K);
+      sinon.stub(App.ModalPopup, "show", Em.K);
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      controller.doDecommissionRegionServer.restore();
+    });
+
+    it('Component in passive state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'HBASE_REGIONSERVER',
+        passiveState: 'ON'
+      })]);
+      controller.warnBeforeDecommission('host1', 'HBASE', 'HBASE_REGIONSERVER', 'SLAVE');
+      expect(App.ModalPopup.show.called).to.be.false;
+      expect(controller.doDecommissionRegionServer.calledWith('host1', 'HBASE', 'HBASE_REGIONSERVER', 'SLAVE')).to.be.true;
+    });
+    it('Component is not in passive state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'HBASE_REGIONSERVER',
+        passiveState: 'OFF'
+      })]);
+      controller.warnBeforeDecommission('host1', 'HBASE', 'HBASE_REGIONSERVER', 'SLAVE');
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      expect(controller.doDecommissionRegionServer.called).to.be.false;
+    });
+  });*/
+
+  describe('#doRecommissionAndStart()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doRecommissionAndStart('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#decommissionSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "showBackgroundOperationsPopup", Em.K);
+    });
+    afterEach(function () {
+      controller.showBackgroundOperationsPopup.restore();
+    });
+
+    it('data is null', function () {
+      expect(controller.decommissionSuccessCallback(null)).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
+    it('data has Requests', function () {
+      var data = {Requests: []};
+      expect(controller.decommissionSuccessCallback(data)).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+    it('data has resources', function () {
+      var data = {resources: [{RequestSchedule: {}}]};
+      expect(controller.decommissionSuccessCallback(data)).to.be.true;
+      expect(controller.showBackgroundOperationsPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doRecommissionAndRestart()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.doRecommissionAndRestart('', '', '', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doAction()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "validateAndDeleteHost", Em.K);
+      sinon.stub(controller, "doStartAllComponents", Em.K);
+      sinon.stub(controller, "doStopAllComponents", Em.K);
+      sinon.stub(controller, "doRestartAllComponents", Em.K);
+      sinon.stub(controller, "onOffPassiveModeForHost", Em.K);
+    });
+
+    afterEach(function () {
+      controller.validateAndDeleteHost.restore();
+      controller.doStartAllComponents.restore();
+      controller.doStopAllComponents.restore();
+      controller.doRestartAllComponents.restore();
+      controller.onOffPassiveModeForHost.restore();
+    });
+
+    it('"deleteHost" action', function () {
+      var option = {context: {action: "deleteHost"}};
+      controller.doAction(option);
+      expect(controller.validateAndDeleteHost.calledOnce).to.be.true;
+    });
+    it('"startAllComponents" action, isNotHeartBeating = false', function () {
+      var option = {context: {action: "startAllComponents"}};
+      controller.set('content.isNotHeartBeating', false);
+      controller.doAction(option);
+      expect(controller.doStartAllComponents.calledOnce).to.be.true;
+    });
+    it('"startAllComponents" action, isNotHeartBeating = true', function () {
+      var option = {context: {action: "startAllComponents"}};
+      controller.set('content.isNotHeartBeating', true);
+      controller.doAction(option);
+      expect(controller.doStartAllComponents.called).to.be.false;
+    });
+    it('"stopAllComponents" action, isNotHeartBeating = false', function () {
+      var option = {context: {action: "stopAllComponents"}};
+      controller.set('content.isNotHeartBeating', false);
+      controller.doAction(option);
+      expect(controller.doStopAllComponents.calledOnce).to.be.true;
+    });
+    it('"stopAllComponents" action, isNotHeartBeating = true', function () {
+      var option = {context: {action: "stopAllComponents"}};
+      controller.set('content.isNotHeartBeating', true);
+      controller.doAction(option);
+      expect(controller.doStopAllComponents.called).to.be.false;
+    });
+    it('"restartAllComponents" action, isNotHeartBeating = false', function () {
+      var option = {context: {action: "restartAllComponents"}};
+      controller.set('content.isNotHeartBeating', false);
+      controller.doAction(option);
+      expect(controller.doRestartAllComponents.calledOnce).to.be.true;
+    });
+    it('"restartAllComponents" action, isNotHeartBeating = true', function () {
+      var option = {context: {action: "restartAllComponents"}};
+      controller.set('content.isNotHeartBeating', true);
+      controller.doAction(option);
+      expect(controller.doRestartAllComponents.called).to.be.false;
+    });
+    it('"onOffPassiveModeForHost" action', function () {
+      var option = {context: {action: "onOffPassiveModeForHost"}};
+      controller.doAction(option);
+      expect(controller.onOffPassiveModeForHost.calledWith({action: "onOffPassiveModeForHost"})).to.be.true;
+    });
+  });
+
+  describe('#onOffPassiveModeForHost()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(controller, "hostPassiveModeRequest", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.hostPassiveModeRequest.restore();
+    });
+
+    it('popup should be displayed, active = true', function () {
+      var popup = controller.onOffPassiveModeForHost({active: true});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.hostPassiveModeRequest.calledWith('ON')).to.be.true;
+    });
+    it('popup should be displayed, active = false', function () {
+      var popup = controller.onOffPassiveModeForHost({active: false});
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(controller.hostPassiveModeRequest.calledWith('OFF')).to.be.true;
+    });
+  });
+
+  describe('#hostPassiveModeRequest()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('Query should be sent', function () {
+      controller.hostPassiveModeRequest('', '');
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#doStartAllComponents()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('serviceNonClientActiveComponents is null', function () {
+      controller.reopen({
+        serviceNonClientActiveComponents: []
+      });
+      controller.set('serviceNonClientActiveComponents', null);
+      controller.doStartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is empty', function () {
+      controller.set('serviceNonClientActiveComponents', []);
+      controller.doStartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is correct', function () {
+      controller.set('serviceNonClientActiveComponents', [{}]);
+      var popup = controller.doStartAllComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      /*popup.onPrimary();
+      expect(controller.sendStartComponentCommand.calledWith([{}])).to.be.true;*/
+    });
+  });
+
+  describe('#doStopAllComponents()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('serviceNonClientActiveComponents is null', function () {
+      controller.set('serviceNonClientActiveComponents', null);
+      controller.doStopAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is empty', function () {
+      controller.set('serviceNonClientActiveComponents', []);
+      controller.doStopAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceNonClientActiveComponents is correct', function () {
+      controller.set('serviceNonClientActiveComponents', [{}]);
+      var popup = controller.doStopAllComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      /*popup.onPrimary();
+      expect(controller.sendStopComponentCommand.calledWith([{}])).to.be.true;*/
+    });
+  });
+
+  describe('#doRestartAllComponents()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "sendStartComponentCommand", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.sendStartComponentCommand.restore();
+    });
+
+    it('serviceActiveComponents is null', function () {
+      controller.set('serviceActiveComponents', null);
+      controller.doRestartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceActiveComponents is empty', function () {
+      controller.set('serviceActiveComponents', []);
+      controller.doRestartAllComponents();
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('serviceActiveComponents is correct', function () {
+      controller.set('serviceActiveComponents', [{}]);
+      var popup = controller.doRestartAllComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      /*popup.onPrimary();
+       expect(controller.restartHostComponents.calledWith([{}])).to.be.true;*/
+    });
+  });
+
+  describe('#getHostComponentsInfo()', function () {
+
+    var result = {
+      zkServerInstalled: false,
+      lastComponents: [],
+      masterComponents: [],
+      runningComponents: [],
+      nonDeletableComponents: [],
+      unknownComponents: []
+    };
+
+    it('content.hostComponents is null', function () {
+      controller.set('content.hostComponents', null);
+      expect(controller.getHostComponentsInfo()).to.eql(result);
+    });
+    it('content.hostComponents is empty', function () {
+      controller.set('content.hostComponents', []);
+      expect(controller.getHostComponentsInfo()).to.eql(result);
+    });
+    it('content.hostComponents has ZOOKEEPER_SERVER', function () {
+      App.HostComponent.find().clear();
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'ZOOKEEPER_SERVER',
+        workStatus: 'INIT',
+        isDeletable: true
+      })]);
+      expect(controller.getHostComponentsInfo().zkServerInstalled).to.be.true;
+    });
+    it('content.hostComponents has last component', function () {
+      App.store.load(App.HostComponent, {
+        id: 'TASKTRACKER_host1',
+        component_name: 'TASKTRACKER'
+      });
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        displayName: 'TaskTracker',
+        workStatus: 'INIT',
+        isDeletable: true
+      })]);
+      expect(controller.getHostComponentsInfo().lastComponents).to.eql(['TaskTracker']);
+      App.HostComponent.find().clear();
+    });
+    it('content.hostComponents has master non-deletable component', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'INIT',
+        isDeletable: false,
+        isMaster: true,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().masterComponents).to.eql(['ZK1']);
+      expect(controller.getHostComponentsInfo().nonDeletableComponents).to.eql(['ZK1']);
+    });
+    it('content.hostComponents has running component', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'STARTED',
+        isDeletable: true,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().runningComponents).to.eql(['ZK1']);
+    });
+    it('content.hostComponents has non-deletable component', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'INIT',
+        isDeletable: false,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().nonDeletableComponents).to.eql(['ZK1']);
+    });
+    it('content.hostComponents has component with UNKNOWN state', function () {
+      controller.set('content.hostComponents', [Em.Object.create({
+        componentName: 'TASKTRACKER',
+        workStatus: 'UNKNOWN',
+        isDeletable: false,
+        displayName: 'ZK1'
+      })]);
+      expect(controller.getHostComponentsInfo().unknownComponents).to.eql(['ZK1']);
+    });
+  });
+
+  describe('#validateAndDeleteHost()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App, "showConfirmationPopup", Em.K);
+      sinon.stub(controller, "getHostComponentsInfo", function(){
+        return this.get('mockHostComponentsInfo');
+      });
+      sinon.stub(controller, "raiseDeleteComponentsError", Em.K);
+      sinon.stub(controller, "_doDeleteHost", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      controller.getHostComponentsInfo.restore();
+      controller.raiseDeleteComponentsError.restore();
+      controller._doDeleteHost.restore();
+    });
+
+    it('App.supports.deleteHost = false', function () {
+      App.supports.deleteHost = false;
+      expect(controller.validateAndDeleteHost()).to.be.false;
+      App.supports.deleteHost = true;
+    });
+    it('masterComponents exist', function () {
+      controller.set('mockHostComponentsInfo', {masterComponents: [{}]});
+      controller.validateAndDeleteHost();
+      expect(controller.raiseDeleteComponentsError.calledWith([{}], 'masterList')).to.be.true;
+    });
+    it('nonDeletableComponents exist', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [{}]
+      });
+      controller.validateAndDeleteHost();
+      expect(controller.raiseDeleteComponentsError.calledWith([{}], 'nonDeletableList')).to.be.true;
+    });
+    it('runningComponents exist', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [],
+        runningComponents: [{}]
+      });
+      controller.validateAndDeleteHost();
+      expect(controller.raiseDeleteComponentsError.calledWith([{}], 'runningList')).to.be.true;
+    });
+    it('zkServerInstalled = true', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [],
+        runningComponents: [],
+        unknownComponents: [],
+        lastComponents: [],
+        zkServerInstalled: true
+      });
+      var popup = controller.validateAndDeleteHost();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+     /* popup.onPrimary();
+      expect(controller._doDeleteHost.calledWith([], [])).to.be.true;*/
+    });
+    it('zkServerInstalled = false', function () {
+      controller.set('mockHostComponentsInfo', {
+        masterComponents: [],
+        nonDeletableComponents: [],
+        runningComponents: [],
+        unknownComponents: [],
+        lastComponents: [],
+        zkServerInstalled: false
+      });
+      controller.validateAndDeleteHost();
+      expect(controller._doDeleteHost.calledWith([], [])).to.be.true;
+    });
+  });
+
+  describe('#raiseDeleteComponentsError()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ModalPopup, "show", Em.K);
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+    });
+
+    it('Popup should be displayed', function () {
+      controller.raiseDeleteComponentsError([], '');
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#_doDeleteHost()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ModalPopup, "show", Em.K);
+    });
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+    });
+
+    it('Popup should be displayed', function () {
+      controller._doDeleteHost([], []);
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#restartAllStaleConfigComponents()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(batchUtils, "restartHostComponents", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      batchUtils.restartHostComponents.restore();
+    });
+
+    it('popup should be displayed', function () {
+      controller.set('content.componentsWithStaleConfigs', [{}]);
+      var popup = controller.restartAllStaleConfigComponents();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(batchUtils.restartHostComponents.calledWith([{}])).to.be.true;
+    });
+  });
+
+  describe('#moveComponent()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+    });
+
+    it('popup should be displayed', function () {
+      var popup = controller.moveComponent();
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#refreshConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App, "showConfirmationPopup");
+      sinon.stub(batchUtils, "restartHostComponents", Em.K);
+    });
+    afterEach(function () {
+      App.showConfirmationPopup.restore();
+      batchUtils.restartHostComponents.restore();
+    });
+
+    it('No components', function () {
+      var event = {context: []};
+      controller.refreshConfigs(event);
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('No components with stale configs', function () {
+      var event = {context: [Em.Object.create({
+        staleConfigs: false
+      })]};
+      controller.refreshConfigs(event);
+      expect(App.showConfirmationPopup.called).to.be.false;
+    });
+    it('Components with stale configs', function () {
+      var event = {context: [Em.Object.create({
+        staleConfigs: true
+      })]};
+      var popup = controller.refreshConfigs(event);
+      expect(App.showConfirmationPopup.calledOnce).to.be.true;
+      popup.onPrimary();
+      expect(batchUtils.restartHostComponents.calledWith([Em.Object.create({
+        staleConfigs: true
+      })])).to.be.true;
+    });
+  });
+});
\ No newline at end of file


[2/2] git commit: AMBARI-5587 UI unit tests for host controllers. (atkach)

Posted by at...@apache.org.
AMBARI-5587 UI unit tests for host controllers. (atkach)


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

Branch: refs/heads/trunk
Commit: 5c49faf56bb0fb28a9aa038e3e97f40c3dc34dcc
Parents: b698c29
Author: atkach <at...@hortonworks.com>
Authored: Mon Apr 28 14:23:40 2014 +0300
Committer: atkach <at...@hortonworks.com>
Committed: Mon Apr 28 14:23:40 2014 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |    2 +
 .../controllers/main/host/configs_service.js    |  113 +-
 ambari-web/app/controllers/main/host/details.js | 1066 ++++++------
 ambari-web/app/messages.js                      |    1 +
 .../main/host/configs_service_test.js           |  223 +++
 .../test/controllers/main/host/details_test.js  | 1516 ++++++++++++++++++
 6 files changed, 2331 insertions(+), 590 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/5c49faf5/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 1ba5922..363a05f 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -48,6 +48,8 @@ require('test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceus
 require('test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test');
 require('test/controllers/main/charts/heatmap_metrics/heatmap_metric_yarn_ResourceUsed_test');
 require('test/controllers/main/host/add_controller_test');
+require('test/controllers/main/host/configs_service_test');
+require('test/controllers/main/host/details_test');
 require('test/controllers/main/service/add_controller_test');
 require('test/controllers/main/service/reassign_controller_test');
 require('test/controllers/main/dashboard_test');

http://git-wip-us.apache.org/repos/asf/ambari/blob/5c49faf5/ambari-web/app/controllers/main/host/configs_service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/configs_service.js b/ambari-web/app/controllers/main/host/configs_service.js
index 9bbda04..adf62e0 100644
--- a/ambari-web/app/controllers/main/host/configs_service.js
+++ b/ambari-web/app/controllers/main/host/configs_service.js
@@ -5,9 +5,9 @@
  * 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
@@ -21,6 +21,9 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
   name: 'mainHostServiceConfigsController',
   host: null,
   isHostsConfigsPage: true,
+  typeTagToHostMap: null,
+  configKeyToConfigMap: null,
+
   /**
    * On load function
    */
@@ -36,48 +39,68 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
    */
   renderServiceConfigs: function (serviceConfigs) {
     var newServiceConfigs = jQuery.extend({}, serviceConfigs);
-    var hostHostComponentNames = [];
+    newServiceConfigs.configCategories = this.filterServiceConfigs(serviceConfigs.configCategories);
+    this._super(newServiceConfigs);
+  },
+  /**
+   * filter config categories by host-component of host
+   * @param configCategories
+   * @return {Array}
+   */
+  filterServiceConfigs: function (configCategories) {
     var hostComponents = this.get('host.hostComponents');
-    if (hostComponents) {
-      hostComponents.forEach(function (hc) {
-        var name = hc.get('componentName');
-        if (!hostHostComponentNames.contains(name)) {
-          hostHostComponentNames.push(name);
-        }
-      });
-    }
-    newServiceConfigs.configCategories = serviceConfigs.configCategories.filter(function (category) {
+    var hostHostComponentNames = (hostComponents) ? hostComponents.mapProperty('componentName') : [];
+
+    return configCategories.filter(function (category) {
       var hcNames = category.get('hostComponentNames');
-      if (hcNames != null && hcNames.length > 0) {
-        var show = false;
-        hcNames.forEach(function (name) {
-          if (hostHostComponentNames.contains(name)) {
-            show = true;
+      if (hcNames && hcNames.length > 0) {
+        for (var i = 0, l = hcNames.length; i < l; i++) {
+          if (hostHostComponentNames.contains(hcNames[i])) {
+            return true;
           }
-        });
-        return show;
+        }
+        return false;
       }
       return true;
     });
-    this._super(newServiceConfigs);
   },
 
-  typeTagToHostMap: null,
-
-  configKeyToConfigMap: null,
-
   /**
    * This method will *not load* the overridden properties. However it will
    * replace the value shown for properties which this host has override for.
+   * @param serviceConfigs
+   * @param loadedGroupToOverrideSiteToTagMap
+   * @param configGroups
    */
   loadServiceConfigHostsOverrides: function (serviceConfigs, loadedGroupToOverrideSiteToTagMap, configGroups) {
-    var thisHostName = this.get('host.hostName');
     var configKeyToConfigMap = {};
     serviceConfigs.forEach(function (item) {
       configKeyToConfigMap[item.name] = item;
     });
+    this.set('configKeyToConfigMap', configKeyToConfigMap);
+    var urlParams = this.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups);
+    if (urlParams.length > 0) {
+      App.ajax.send({
+        name: 'host.service_config_hosts_overrides',
+        sender: this,
+        data: {
+          urlParams: urlParams.join('|')
+        },
+        success: 'loadServiceConfigHostsOverridesSuccessCallback',
+        error: 'loadServiceConfigHostsOverridesErrorCallback'
+      });
+    }
+  },
+  /**
+   * construct URL parameters, based on groups
+   * @param loadedGroupToOverrideSiteToTagMap
+   * @param configGroups
+   * @return {Array}
+   */
+  constructUrlParams: function (loadedGroupToOverrideSiteToTagMap, configGroups) {
     var typeTagToGroupMap = {};
     var urlParams = [];
+    var thisHostName = this.get('host.hostName');
     for (var group in loadedGroupToOverrideSiteToTagMap) {
       var groupObj = configGroups.findProperty('name', group);
       if (groupObj.get('hosts').contains(thisHostName)) {
@@ -90,53 +113,29 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
       }
     }
     this.set('typeTagToGroupMap', typeTagToGroupMap);
-    this.set('configKeyToConfigMap', configKeyToConfigMap);
-    if (urlParams.length > 0) {
-      App.ajax.send({
-        name: 'host.service_config_hosts_overrides',
-        sender: this,
-        data: {
-          urlParams: urlParams.join('|')
-        },
-        success: 'loadServiceConfigHostsOverridesSuccessCallback',
-        error: 'loadServiceConfigHostsOverridesErrorCallback'
-      });
-    }
+    return urlParams;
   },
+  /**
+   * handle configs, override their values
+   * @param data
+   */
   loadServiceConfigHostsOverridesSuccessCallback: function (data) {
     var typeTagToGroupMap = this.get('typeTagToGroupMap');
     var configKeyToConfigMap = this.get('configKeyToConfigMap');
     data.items.forEach(function (config) {
       var group = typeTagToGroupMap[config.type + "///" + config.tag];
       var properties = config.properties;
-      for ( var prop in properties) {
+      for (var prop in properties) {
         var serviceConfig = configKeyToConfigMap[prop];
         var hostOverrideValue = properties[prop];
-        if (serviceConfig && serviceConfig.displayType === 'int') {
-          if (/\d+m$/.test(hostOverrideValue)) {
-            hostOverrideValue = hostOverrideValue.slice(0, hostOverrideValue.length - 1);
-          }
-        } else if (serviceConfig && serviceConfig.displayType === 'checkbox') {
-          switch (hostOverrideValue) {
-            case 'true':
-              hostOverrideValue = true;
-              break;
-            case 'false':
-              hostOverrideValue = false;
-              break;
-          }
-        }
         if (serviceConfig) {
-          // Value of this property is different for this host.
-          console.log("loadServiceConfigHostsOverrides(" + group + "): [" + group + "] OVERRODE(" + serviceConfig.name + "): " + serviceConfig.value + " -> " + hostOverrideValue);
           serviceConfig.value = hostOverrideValue;
-          serviceConfig.defaultValue = hostOverrideValue;
           serviceConfig.isOriginalSCP = false;
           serviceConfig.group = group;
+          App.config.handleSpecialProperties(serviceConfig);
         }
       }
     });
-    console.log("loadServiceConfigHostsOverrides(" + this.get('host.hostName') + "): Finished loading.");
   },
   loadServiceConfigHostsOverridesErrorCallback: function (request, ajaxOptions, error) {
     console.log("TRACE: error code status is: " + request.status);
@@ -146,7 +145,7 @@ App.MainHostServiceConfigsController = App.MainServiceInfoConfigsController.exte
    */
   switchHostGroup: function () {
     var self = this;
-    App.config.launchSwitchConfigGroupOfHostDialog(this.get('selectedConfigGroup'), this.get('configGroups'), this.get('host.hostName'), function(newGroup){
+    App.config.launchSwitchConfigGroupOfHostDialog(this.get('selectedConfigGroup'), this.get('configGroups'), this.get('host.hostName'), function (newGroup) {
       self.set('selectedConfigGroup', newGroup);
     })
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/5c49faf5/ambari-web/app/controllers/main/host/details.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js
index 4dea285..73fda3e 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -54,25 +54,25 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} event
    * @method routeToService
    */
-  routeToService: function(event){
+  routeToService: function (event) {
     var service = event.context;
-    App.router.transitionTo('main.services.service.summary',service);
+    App.router.transitionTo('main.services.service.summary', service);
   },
 
   /**
    * List of active (not in passive state) host components
    * @type {Ember.Enumerable}
    */
-  serviceActiveComponents: function() {
-    return this.get('content.hostComponents').filterProperty('service.isInPassive',false);
+  serviceActiveComponents: function () {
+    return this.get('content.hostComponents').filterProperty('service.isInPassive', false);
   }.property('content.hostComponents'),
 
   /**
    * List of active host components which aren't clients
    * @type {Ember.Enumerable}
    */
-  serviceNonClientActiveComponents: function() {
-    return this.get('serviceActiveComponents').filterProperty('isClient',false);
+  serviceNonClientActiveComponents: function () {
+    return this.get('serviceActiveComponents').filterProperty('isClient', false);
   }.property('serviceActiveComponents'),
 
   /**
@@ -96,60 +96,70 @@ App.MainHostDetailsController = Em.Controller.extend({
    */
   startComponent: function (event) {
     var self = this;
-    App.showConfirmationPopup(function() {
+    return App.showConfirmationPopup(function () {
       var component = event.context;
       var context = Em.I18n.t('requestInfo.startHostComponent') + " " + component.get('displayName');
       self.sendStartComponentCommand(component, context);
     });
   },
-  
+
   /**
-   * PUTs a command to server to start a component. If no 
+   * PUTs a command to server to start a component. If no
    * specific component is provided, all components are started.
    * @param {object} component  When <code>null</code> all startable components are started.
    * @param {string} context  Context under which this command is beign sent.
    * @method sendStartComponentCommand
    */
-  sendStartComponentCommand: function(component, context) {
-
-    var dataToSend = {
-      RequestInfo : {
-        "context" : context
+  sendStartComponentCommand: function (component, context) {
+    var dataToSend = this.getDataToSend('STARTED', context);
+    var data = {
+      hostName: this.get('content.hostName'),
+      component: component
+    };
+    this.setComponentToData(data, dataToSend, component);
+    App.ajax.send({
+      name: (Array.isArray(component)) ? 'host.host_components.stop' : 'host.host_component.stop',
+      sender: this,
+      data: data,
+      success: 'startComponentSuccessCallback',
+      error: 'ajaxErrorCallback'
+    });
+  },
+  /**
+   * construct meta data for query
+   * @param state
+   * @param context
+   * @return {Object}
+   */
+  getDataToSend: function (state, context) {
+    return {
+      RequestInfo: {
+        "context": context
       },
-      Body:{
+      Body: {
         HostRoles: {
-          state: 'STARTED'
+          state: state
         }
       }
-    };
-    if (Em.isArray(component)) {
-      dataToSend.RequestInfo.query = "HostRoles/component_name.in(" + component.mapProperty('componentName').join(',') + ")";
-      App.ajax.send({
-        name: 'host.host_components.stop',
-        sender: this,
-        data: {
-          data: JSON.stringify(dataToSend),
-          hostName: this.get('content.hostName'),
-          component: component
-        },
-        success: 'stopComponentSuccessCallback',
-        error: 'ajaxErrorCallback'
-      });
     }
-    else {
-      App.ajax.send({
-        name: 'host.host_component.stop',
-        sender: this,
-        data: {
-          data: JSON.stringify(dataToSend),
-          hostName: this.get('content.hostName'),
-          componentName: component.get('componentName').toUpperCase(),
-          component: component
-        },
-        success: 'startComponentSuccessCallback',
-        error: 'ajaxErrorCallback'
-      });
+  },
+  /**
+   * set additional data to query info depending on component
+   * @param data
+   * @param dataToSend
+   * @param component
+   * @return {Boolean}
+   */
+  setComponentToData: function (data, dataToSend, component) {
+    if (!dataToSend || !component || !data) return false;
+
+    if (Array.isArray(component)) {
+      dataToSend.RequestInfo.query = "HostRoles/component_name.in(" + component.mapProperty('componentName').join(',') + ")";
+    } else {
+      data.componentName = component.get('componentName');
     }
+    data.data = JSON.stringify(dataToSend);
+    return true;
   },
 
   /**
@@ -159,32 +169,51 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method startComponentSuccessCallback
    */
-  startComponentSuccessCallback: function(data, opt, params) {
+  startComponentSuccessCallback: function (data, opt, params) {
     console.log('Send request for STARTING successfully');
 
     if (App.testMode) {
-      if(Em.isArray(params.component)){
-        var allComponents = this.get('content.hostComponents');
-        allComponents.forEach(function(c){
-          c.set('workStatus', App.HostComponentStatus.stopping);
-          setTimeout(function(){
-            c.set('workStatus', App.HostComponentStatus.stopped);
-          },App.testModeDelayForActions);
-        });
-      } else {
-        params.component.set('workStatus', App.HostComponentStatus.starting);
-        setTimeout(function(){
-          params.component.set('workStatus', App.HostComponentStatus.started);
-        },App.testModeDelayForActions);
-      }
+      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.starting, App.HostComponentStatus.started);
     } else {
       App.router.get('clusterController').loadUpdatedStatusDelayed(500);
     }
-    // load data (if we need to show this background operations popup) from persist
+    this.showBackgroundOperationsPopup();
+  },
+
+  /**
+   * mimic status transition in test mode
+   * @param entity
+   * @param transitionalState
+   * @param finalState
+   */
+  mimicWorkStatusChange: function(entity, transitionalState, finalState) {
+    if (Em.isArray(entity)) {
+      entity.forEach(function (item) {
+        item.set('workStatus', transitionalState);
+        setTimeout(function () {
+          item.set('workStatus', finalState);
+        }, App.testModeDelayForActions);
+      });
+    } else {
+      entity.set('workStatus', transitionalState);
+      setTimeout(function () {
+        entity.set('workStatus', finalState);
+      }, App.testModeDelayForActions);
+    }
+  },
+
+  /**
+   * load data (if we need to show this background operations popup) from persist
+   * @param callback
+   */
+  showBackgroundOperationsPopup: function (callback) {
     App.router.get('applicationController').dataLoading().done(function (initValue) {
       if (initValue) {
         App.router.get('backgroundOperationsController').showPopup();
       }
+      if (typeof callback === 'function') {
+        callback();
+      }
     });
   },
 
@@ -209,22 +238,17 @@ App.MainHostDetailsController = Em.Controller.extend({
       disablePrimary: function () {
         return !this.get('isChecked');
       }.property('isChecked'),
-      lastComponent: function() {
-        if (isLastComponent) {
-          this.set('isChecked', false);
-          return true;
-        } else {
-          this.set('isChecked', true);
-          return false;
-        }
+      lastComponent: function () {
+        this.set('isChecked', !isLastComponent);
+        return isLastComponent;
       }.property(),
-      lastComponentError:  Em.View.extend({
+      lastComponentError: Em.View.extend({
         template: Em.Handlebars.compile(Em.I18n.t('hosts.host.deleteComponent.popup.warning').format(displayName))
       }),
       restartNagiosMsg: Em.View.extend({
         template: Em.Handlebars.compile(Em.I18n.t('hosts.host.deleteComponent.popup.msg2').format(displayName))
       }),
-      deleteComponentMsg: function() {
+      deleteComponentMsg: function () {
         return Em.I18n.t('hosts.host.deleteComponent.popup.msg1').format(displayName);
       }.property(),
       onPrimary: function () {
@@ -233,7 +257,6 @@ App.MainHostDetailsController = Em.Controller.extend({
         this.hide();
       }
     });
-
   },
 
   /**
@@ -244,37 +267,24 @@ App.MainHostDetailsController = Em.Controller.extend({
 
   /**
    * Deletes the given host component, or all host components.
-   * 
+   *
    * @param {object|null} component  When <code>null</code> all host components are deleted.
    * @return  <code>null</code> when components get deleted.
-   *          <code>{xhr: XhrObj, url: "http://", method: "DELETE"}</code> 
+   *          <code>{xhr: XhrObj, url: "http://", method: "DELETE"}</code>
    *          when components failed to get deleted.
    * @method _doDeleteHostComponent
    */
-  _doDeleteHostComponent: function(component) {
-    if (component === null) {
-      App.ajax.send({
-        name: 'host.host_components.delete',
-        sender: this,
-        data: {
-          hostName:  this.get('content.hostName')
-        },
-        success: '_doDeleteHostComponentSuccessCallback',
-        error: '_doDeleteHostComponentErrorCallback'
-      });
-    }
-    else {
-      App.ajax.send({
-        name: 'host.host_component.delete',
-        sender: this,
-        data: {
-          componentName: component.get('componentName').toUpperCase(),
-          hostName:  this.get('content.hostName')
-        },
-        success: '_doDeleteHostComponentSuccessCallback',
-        error: '_doDeleteHostComponentErrorCallback'
-      });
-    }
+  _doDeleteHostComponent: function (component) {
+    App.ajax.send({
+      name: (Em.isNone(component)) ? 'host.host_components.delete' : 'host.host_component.delete',
+      sender: this,
+      data: {
+        componentName: (component) ? component.get('componentName') : '',
+        hostName: this.get('content.hostName')
+      },
+      success: '_doDeleteHostComponentSuccessCallback',
+      error: '_doDeleteHostComponentErrorCallback'
+    });
     return this.get('_deletedHostComponentResult');
   },
 
@@ -288,7 +298,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * Success callback for delete host component request
    * @method _doDeleteHostComponentSuccessCallback
    */
-  _doDeleteHostComponentSuccessCallback: function() {
+  _doDeleteHostComponentSuccessCallback: function () {
     this.set('_deletedHostComponentResult', null);
   },
 
@@ -299,7 +309,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} errorThrown
    * @method _doDeleteHostComponentErrorCallback
    */
-  _doDeleteHostComponentErrorCallback: function(xhr, textStatus, errorThrown) {
+  _doDeleteHostComponentErrorCallback: function (xhr, textStatus, errorThrown) {
     console.log('Error deleting host component');
     console.log(textStatus);
     console.log(errorThrown);
@@ -314,21 +324,20 @@ App.MainHostDetailsController = Em.Controller.extend({
   upgradeComponent: function (event) {
     var self = this;
     var component = event.context;
-    App.showConfirmationPopup(function() {
-
+    return App.showConfirmationPopup(function () {
       App.ajax.send({
         name: 'host.host_component.upgrade',
         sender: self,
         data: {
           component: component,
           hostName: self.get('content.hostName'),
-          componentName: component.get('componentName').toUpperCase(),
+          componentName: component.get('componentName'),
           data: JSON.stringify({
-            RequestInfo : {
-              "context" : Em.I18n.t('requestInfo.upgradeHostComponent') + " " + component.get('displayName')
+            RequestInfo: {
+              "context": Em.I18n.t('requestInfo.upgradeHostComponent') + " " + component.get('displayName')
             },
-            Body:{
-              HostRoles:{
+            Body: {
+              HostRoles: {
                 stack_id: 'HDP-1.2.2',
                 state: 'INSTALLED'
               }
@@ -348,24 +357,15 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method upgradeComponentSuccessCallback
    */
-  upgradeComponentSuccessCallback: function(data, opt, params) {
+  upgradeComponentSuccessCallback: function (data, opt, params) {
     console.log('Send request for UPGRADE successfully');
 
     if (App.testMode) {
-      params.component.set('workStatus', App.HostComponentStatus.starting);
-      setTimeout(function(){
-        params.component.set('workStatus', App.HostComponentStatus.started);
-      },App.testModeDelayForActions);
+      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.starting, App.HostComponentStatus.started);
     } else {
       App.router.get('clusterController').loadUpdatedStatusDelayed(500);
     }
-
-    // load data (if we need to show this background operations popup) from persist
-    App.router.get('applicationController').dataLoading().done(function (initValue) {
-      if (initValue) {
-        App.router.get('backgroundOperationsController').showPopup();
-      }
-    });
+    this.showBackgroundOperationsPopup();
   },
 
   /**
@@ -375,58 +375,34 @@ App.MainHostDetailsController = Em.Controller.extend({
    */
   stopComponent: function (event) {
     var self = this;
-    App.showConfirmationPopup(function() {
+    return App.showConfirmationPopup(function () {
       var component = event.context;
-      var context = Em.I18n.t('requestInfo.stopHostComponent')+ " " + component.get('displayName');
+      var context = Em.I18n.t('requestInfo.stopHostComponent') + " " + component.get('displayName');
       self.sendStopComponentCommand(component, context);
     });
   },
-  
+
   /**
-   * PUTs a command to server to stop a component. If no 
+   * PUTs a command to server to stop a component. If no
    * specific component is provided, all components are stopped.
    * @param {object} component  When <code>null</code> all components are stopped.
    * @param {object} context Context under which this command is beign sent.
    * @method sendStopComponentCommand
    */
-  sendStopComponentCommand: function(component, context) {
-    var dataToSend = {
-      RequestInfo : {
-        "context" : context
-      },
-      Body:{
-        HostRoles: {
-          state: 'INSTALLED'
-        }
-      }
+  sendStopComponentCommand: function (component, context) {
+    var dataToSend = this.getDataToSend('INSTALLED', context);
+    var data = {
+      hostName: this.get('content.hostName'),
+      component: component
     };
-    if (Em.isArray(component)) {
-      dataToSend.RequestInfo.query = "HostRoles/component_name.in(" + component.mapProperty('componentName').join(',') + ")";
-      App.ajax.send({
-        name: 'host.host_components.stop',
-        sender: this,
-        data: {
-          data: JSON.stringify(dataToSend),
-          hostName: this.get('content.hostName'),
-          component: component
-        },
-        success: 'stopComponentSuccessCallback'
-      });
-    }
-    else {
-      App.ajax.send({
-        name: 'host.host_component.stop',
-        sender: this,
-        data: {
-          data: JSON.stringify(dataToSend),
-          hostName: this.get('content.hostName'),
-          componentName: component.get('componentName').toUpperCase(),
-          component: component
-        },
-        success: 'stopComponentSuccessCallback',
-        error: 'ajaxErrorCallback'
-      });
-    }
+    this.setComponentToData(data, dataToSend, component);
+    App.ajax.send({
+      name: (Array.isArray(component)) ? 'host.host_components.stop' : 'host.host_component.stop',
+      sender: this,
+      data: data,
+      success: 'stopComponentSuccessCallback',
+      error: 'ajaxErrorCallback'
+    });
   },
 
   /**
@@ -436,34 +412,15 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method stopComponentSuccessCallback
    */
-  stopComponentSuccessCallback: function(data, opt, params) {
+  stopComponentSuccessCallback: function (data, opt, params) {
     console.log('Send request for STOPPING successfully');
 
     if (App.testMode) {
-      if(Em.isArray(params.component)) {
-        params.component.forEach(function(c){
-          c.set('workStatus', App.HostComponentStatus.stopping);
-          setTimeout(function(){
-            c.set('workStatus', App.HostComponentStatus.stopped);
-          },App.testModeDelayForActions);
-        });
-      }
-      else {
-        params.component.set('workStatus', App.HostComponentStatus.stopping);
-        setTimeout(function() {
-          params.component.set('workStatus', App.HostComponentStatus.stopped);
-        },App.testModeDelayForActions);
-      }
-    }
-    else {
+      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.stopping, App.HostComponentStatus.stopped);
+    } else {
       App.router.get('clusterController').loadUpdatedStatusDelayed(500);
     }
-    // load data (if we need to show this background operations popup) from persist
-    App.router.get('applicationController').dataLoading().done(function (initValue) {
-      if (initValue) {
-        App.router.get('backgroundOperationsController').showPopup();
-      }
-    });
+    this.showBackgroundOperationsPopup();
   },
 
   /**
@@ -471,13 +428,20 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} event
    * @method restartComponent
    */
-  restartComponent: function(event) {
-    var self = this;
+  restartComponent: function (event) {
     var component = event.context;
-    App.showConfirmationPopup(function(){
+    return App.showConfirmationPopup(function () {
       batchUtils.restartHostComponents([component], Em.I18n.t('rollingrestart.context.selectedComponentOnSelectedHost').format(component.get('displayName')));
     });
   },
+  /**
+   * get current status of security settings,
+   * if true security is enabled otherwise disabled
+   * @return {Boolean}
+   */
+  getSecurityStatus: function () {
+    return App.router.get('mainAdminSecurityController').getUpdatedSecurityStatus();
+  },
 
   /**
    * Send command to server to install selected host component
@@ -487,69 +451,84 @@ App.MainHostDetailsController = Em.Controller.extend({
   addComponent: function (event) {
     var self = this;
     var component = event.context;
-    var componentName = component.get('componentName').toUpperCase().toString();
-    var subComponentNames = component.get('subComponentNames');
-    var displayName = component.get('displayName');
+    var componentName = component.get('componentName');
 
-    var securityEnabled = App.router.get('mainAdminSecurityController').getUpdatedSecurityStatus();
+    var securityEnabled = this.getSecurityStatus();
 
     if (componentName === 'ZOOKEEPER_SERVER') {
-      App.showConfirmationPopup(function() {
+      return App.showConfirmationPopup(function () {
         self.primary(component);
       }, Em.I18n.t('hosts.host.addComponent.addZooKeeper'));
     }
     else {
-      if (securityEnabled && component.get('componentName') !== 'CLIENTS') {
-        App.showConfirmationPopup(function() {
+      if (securityEnabled && componentName !== 'CLIENTS') {
+        return App.showConfirmationPopup(function () {
           self.primary(component);
-        }, Em.I18n.t('hosts.host.addComponent.securityNote').format(componentName,self.get('content.hostName')));
+        }, Em.I18n.t('hosts.host.addComponent.securityNote').format(componentName, self.get('content.hostName')));
       }
       else {
-        var dn = displayName;
-        if (subComponentNames !== null && subComponentNames.length > 0) {
-          var dns = [];
-          subComponentNames.forEach(function(scn){
-            dns.push(App.format.role(scn));
+        return this.addClientComponent(component);
+      }
+    }
+  },
+  /**
+   * Send command to server to install client on selected host
+   * @param component
+   */
+  addClientComponent: function (component) {
+    var self = this;
+    var message = this.formatClientsMessage(component);
+    return App.ModalPopup.show({
+      primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
+      header: Em.I18n.t('popup.confirmation.commonHeader'),
+
+      addComponentMsg: function () {
+        return Em.I18n.t('hosts.host.addComponent.msg').format(message);
+      }.property(),
+
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/host/details/addComponentPopup')
+      }),
+
+      restartNagiosMsg: Em.View.extend({
+        template: Em.Handlebars.compile(Em.I18n.t('hosts.host.addComponent.note').format(message))
+      }),
+
+      onPrimary: function () {
+        this.hide();
+        if (component.get('componentName') === 'CLIENTS') {
+          // Clients component has many sub-components which
+          // need to be installed.
+          var scs = component.get('subComponentNames');
+          scs.forEach(function (sc) {
+            var c = Em.Object.create({
+              displayName: App.format.role(sc),
+              componentName: sc
+            });
+            self.primary(c);
           });
-          dn += " ("+dns.join(", ")+")";
+        } else {
+          self.primary(component);
         }
-        App.ModalPopup.show({
-          primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
-          header: Em.I18n.t('popup.confirmation.commonHeader'),
-
-          addComponentMsg: function() {
-            return Em.I18n.t('hosts.host.addComponent.msg').format(dn);
-          }.property(),
-
-          bodyClass: Em.View.extend({
-            templateName: require('templates/main/host/details/addComponentPopup')
-          }),
-
-          restartNagiosMsg: Em.View.extend({
-            template: Em.Handlebars.compile(Em.I18n.t('hosts.host.addComponent.note').format(dn))
-          }),
-
-          onPrimary: function () {
-            this.hide();
-            if (component.get('componentName') === 'CLIENTS') {
-              // Clients component has many sub-components which
-              // need to be installed.
-              var scs = component.get('subComponentNames');
-              scs.forEach(function (sc, index) {
-                var c = Em.Object.create({
-                  displayName: App.format.role(sc),
-                  componentName: sc
-                });
-                self.primary(c);
-              });
-            }
-            else {
-              self.primary(component);
-            }
-          }
-        });
       }
+    });
+  },
+
+  /**
+   * format message for operation of adding clients
+   * @param client
+   */
+  formatClientsMessage: function (client) {
+    var displayName = Em.isNone(client.get('displayName')) ? '' : client.get('displayName');
+    var subComponentNames = client.get('subComponentNames');
+    if (subComponentNames && subComponentNames.length > 0) {
+      var dns = [];
+      subComponentNames.forEach(function (scn) {
+        dns.push(App.format.role(scn));
+      });
+      displayName += " (" + dns.join(", ") + ")";
     }
+    return displayName;
   },
 
   /**
@@ -557,9 +536,9 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {App.HostComponent} component
    * @method primary
    */
-  primary: function(component) {
+  primary: function (component) {
     var self = this;
-    var componentName = component.get('componentName').toUpperCase();
+    var componentName = component.get('componentName');
     var displayName = component.get('displayName');
     App.ajax.send({
       name: 'host.host_component.add_new_component',
@@ -594,7 +573,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method addNewComponentSuccessCallback
    */
-  addNewComponentSuccessCallback: function(data, opt, params) {
+  addNewComponentSuccessCallback: function (data, opt, params) {
     console.log('Send request for ADDING NEW COMPONENT successfully');
     App.ajax.send({
       name: 'host.host_component.install_new_component',
@@ -626,34 +605,27 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method installNewComponentSuccessCallback
    */
-  installNewComponentSuccessCallback: function(data, opt, params) {
+  installNewComponentSuccessCallback: function (data, opt, params) {
     if (!data.Requests || !data.Requests.id) {
-      return;
+      return false;
     }
     var self = this;
     console.log('Send request for INSTALLING NEW COMPONENT successfully');
 
     if (App.testMode) {
-      params.component.set('workStatus', App.HostComponentStatus.installing);
-      setTimeout(function () {
-        params.component.set('workStatus', App.HostComponentStatus.stopped);
-      }, App.testModeDelayForActions);
-    }
-    else {
+      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.installing, App.HostComponentStatus.stopped);
+    } else {
       App.router.get('clusterController').loadUpdatedStatusDelayed(500);
     }
 
-    // load data (if we need to show this background operations popup) from persist
-    App.router.get('applicationController').dataLoading().done(function (initValue) {
-      if (initValue) {
-        App.router.get('backgroundOperationsController').showPopup();
-      }
+    this.showBackgroundOperationsPopup(function () {
       if (params.componentName === 'ZOOKEEPER_SERVER') {
         self.set('zkRequestId', data.Requests.id);
         self.addObserver('App.router.backgroundOperationsController.serviceTimestamp', self, self.checkZkConfigs);
         self.checkZkConfigs();
       }
     });
+    return true;
   },
 
   /**
@@ -663,7 +635,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    */
   refreshComponentConfigs: function (event) {
     var self = this;
-    App.showConfirmationPopup(function() {
+    return App.showConfirmationPopup(function () {
       var component = event.context;
       var context = Em.I18n.t('requestInfo.refreshComponentConfigs').format(component.get('displayName'));
       self.sendRefreshComponentConfigsCommand(component, context);
@@ -677,7 +649,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method sendRefreshComponentConfigsCommand
    */
   sendRefreshComponentConfigsCommand: function (component, context) {
-    resource_filters = [
+    var resource_filters = [
       {
         service_name: component.get('service.serviceName'),
         component_name: component.get('componentName'),
@@ -699,24 +671,18 @@ App.MainHostDetailsController = Em.Controller.extend({
    * Success callback for refresh host component configs request
    * @method refreshComponentConfigsSuccessCallback
    */
-  refreshComponentConfigsSuccessCallback: function() {
+  refreshComponentConfigsSuccessCallback: function () {
     console.log('Send request for refresh configs successfully');
-    // load data (if we need to show this background operations popup) from persist
-    App.router.get('applicationController').dataLoading().done(function (showPopup) {
-      if (showPopup) {
-        App.router.get('backgroundOperationsController').showPopup();
-      }
-    });
+    this.showBackgroundOperationsPopup();
   },
 
   /**
    * Load tags
    * @method checkZkConfigs
    */
-  checkZkConfigs: function() {
+  checkZkConfigs: function () {
     var bg = App.router.get('backgroundOperationsController.services').findProperty('id', this.get('zkRequestId'));
-    if (!bg) return;
-    if (!bg.get('isRunning')) {
+    if (bg && !bg.get('isRunning')) {
       this.loadConfigs();
     }
   },
@@ -725,7 +691,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * Load configs
    * @method loadConfigs
    */
-  loadConfigs: function() {
+  loadConfigs: function () {
     this.removeObserver('App.router.backgroundOperationsController.serviceTimestamp', this, this.checkZkConfigs);
     App.ajax.send({
       name: 'config.tags',
@@ -739,63 +705,60 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} data
    * @method adConfigsSuccessCallback
    */
-  loadConfigsSuccessCallback: function(data) {
+  loadConfigsSuccessCallback: function (data) {
+    var urlParams = this.constructConfigUrlParams(data);
+    if (urlParams.length > 0) {
+      App.ajax.send({
+        name: 'reassign.load_configs',
+        sender: this,
+        data: {
+          urlParams: urlParams.join('|')
+        },
+        success: 'saveZkConfigs'
+      });
+      return true;
+    }
+    return false;
+  },
+
+  /**
+   * construct URL params for query, that load configs
+   * @param data {Object}
+   * @return {Array}
+   */
+  constructConfigUrlParams: function (data) {
     var urlParams = [];
+    var services = App.Service.find();
     if (App.get('isHaEnabled')) {
       urlParams.push('(type=core-site&tag=' + data.Clusters.desired_configs['core-site'].tag + ')');
     }
-    if (App.Service.find().someProperty('serviceName', 'HBASE')) {
+    if (services.someProperty('serviceName', 'HBASE')) {
       urlParams.push('(type=hbase-site&tag=' + data.Clusters.desired_configs['hbase-site'].tag + ')');
     }
-    if (App.Service.find().someProperty('serviceName', 'HIVE')) {
+    if (services.someProperty('serviceName', 'HIVE')) {
       urlParams.push('(type=webhcat-site&tag=' + data.Clusters.desired_configs['webhcat-site'].tag + ')');
     }
-    if (App.Service.find().someProperty('serviceName', 'STORM')) {
+    if (services.someProperty('serviceName', 'STORM')) {
       urlParams.push('(type=storm-site&tag=' + data.Clusters.desired_configs['storm-site'].tag + ')');
     }
-    if (!urlParams.length) {
-      return;
-    }
-    App.ajax.send({
-      name: 'reassign.load_configs',
-      sender: this,
-      data: {
-        urlParams: urlParams.join('|')
-      },
-      success: 'setNewZkConfigs'
-    });
+    return urlParams;
   },
 
   /**
-   * Set new values for some configs (based on available ZooKeeper Servers)
+   * save new ZooKeeper configs to server
    * @param {object} data
-   * @method setNewZkConfigs
+   * @method saveZkConfigs
    */
-  setNewZkConfigs: function(data) {
-    var configs = [];
+  saveZkConfigs: function (data) {
+    var configs = {};
     data.items.forEach(function (item) {
       configs[item.type] = item.properties;
     }, this);
 
     var zks = this.getZkServerHosts();
-    var zks_with_port = '';
-    zks.forEach(function(zk) {
-      zks_with_port += zk + ':2181,';
-    });
-    zks_with_port = zks_with_port.slice(0,-1);
+    var zksWithPort = this.concatZkNames(zks);
+    this.setZKConfigs(configs, zksWithPort, zks);
 
-    if (App.get('isHaEnabled')) {
-      configs['core-site']['ha.zookeeper.quorum'] = zks_with_port;
-    }
-    if (configs['hbase-site']) {
-      configs['hbase-site']['hbase.zookeeper.quorum'] = zks.join(',');
-    }
-    if (configs['webhcat-site']) {
-      configs['webhcat-site']['templeton.zookeeper.hosts'] = zks_with_port;
-    }
-    if (configs['storm-site']) {
-      configs['storm-site']['storm.zookeeper.servers'] = JSON.stringify(zks).replace(/"/g, "'");
-    }
     for (var site in configs) {
       if (!configs.hasOwnProperty(site)) continue;
       App.ajax.send({
@@ -808,6 +771,41 @@ App.MainHostDetailsController = Em.Controller.extend({
       });
     }
   },
+  /**
+   * Set new values for some configs (based on available ZooKeeper Servers)
+   * @param configs {object}
+   * @param zksWithPort {string}
+   * @param zks {array}
+   * @return {Boolean}
+   */
+  setZKConfigs: function (configs, zksWithPort, zks) {
+    if(typeof configs !== 'object' || !Array.isArray(zks)) return false;
+    if (App.get('isHaEnabled')) {
+      configs['core-site']['ha.zookeeper.quorum'] = zksWithPort;
+    }
+    if (configs['hbase-site']) {
+      configs['hbase-site']['hbase.zookeeper.quorum'] = zks.join(',');
+    }
+    if (configs['webhcat-site']) {
+      configs['webhcat-site']['templeton.zookeeper.hosts'] = zksWithPort;
+    }
+    if (configs['storm-site']) {
+      configs['storm-site']['storm.zookeeper.servers'] = JSON.stringify(zks).replace(/"/g, "'");
+    }
+    return true;
+  },
+  /**
+   * concatenate URLs to ZOOKEEPER hosts with port "2181",
+   * as value of config divided by comma
+   * @param zks {array}
+   */
+  concatZkNames: function (zks) {
+    var zks_with_port = '';
+    zks.forEach(function (zk) {
+      zks_with_port += zk + ':2181,';
+    });
+    return zks_with_port.slice(0, -1);
+  },
 
   /**
    * Is deleteHost action id fired
@@ -820,7 +818,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @returns {string[]}
    * @method getZkServerHosts
    */
-  getZkServerHosts: function() {
+  getZkServerHosts: function () {
     var zks = App.HostComponent.find().filterProperty('componentName', 'ZOOKEEPER_SERVER').mapProperty('host.hostName');
     if (this.get('fromDeleteHost')) {
       this.set('fromDeleteHost', false);
@@ -837,16 +835,16 @@ App.MainHostDetailsController = Em.Controller.extend({
   installComponent: function (event) {
     var self = this;
     var component = event.context;
-    var componentName = component.get('componentName').toUpperCase();
+    var componentName = component.get('componentName');
     var displayName = component.get('displayName');
 
-    App.ModalPopup.show({
+    return App.ModalPopup.show({
       primary: Em.I18n.t('hosts.host.installComponent.popup.confirm'),
       header: Em.I18n.t('popup.confirmation.commonHeader'),
-      installComponentMessage: function(){
+      installComponentMessage: function () {
         return Em.I18n.t('hosts.host.installComponent.msg').format(displayName);
       }.property(),
-      restartNagiosMsg : Em.View.extend({
+      restartNagiosMsg: Em.View.extend({
         template: Em.Handlebars.compile(Em.I18n.t('hosts.host.addComponent.note').format(displayName))
       }),
       bodyClass: Em.View.extend({
@@ -887,23 +885,14 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method installComponentSuccessCallback
    */
-  installComponentSuccessCallback: function(data, opt, params) {
+  installComponentSuccessCallback: function (data, opt, params) {
     console.log('Send request for REINSTALL COMPONENT successfully');
     if (App.testMode) {
-      params.component.set('workStatus', App.HostComponentStatus.installing);
-      setTimeout(function () {
-        params.component.set('workStatus', App.HostComponentStatus.stopped);
-      }, App.testModeDelayForActions);
+      this.mimicWorkStatusChange(params.component, App.HostComponentStatus.installing, App.HostComponentStatus.stopped);
     } else {
       App.router.get('clusterController').loadUpdatedStatusDelayed(500);
     }
-
-    // load data (if we need to show this background operations popup) from persist
-    App.router.get('applicationController').dataLoading().done(function (initValue) {
-      if (initValue) {
-        App.router.get('backgroundOperationsController').showPopup();
-      }
-    });
+    this.showBackgroundOperationsPopup();
   },
 
   /**
@@ -911,72 +900,68 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {App.HostComponent} component
    * @method decommission
    */
-  decommission: function(component) {
+  decommission: function (component) {
     var self = this;
-    App.showConfirmationPopup(function(){
-      var svcName = component.get('service.serviceName');
-      var hostName = self.get('content.hostName');
-      // HDFS service, decommission DataNode
-      if (svcName === "HDFS") {
-        self.doDecommission(hostName, svcName, "NAMENODE", "DATANODE");
-      }
-      // YARN service, decommission NodeManager
-      if (svcName === "YARN") {
-        self.doDecommission(hostName, svcName, "RESOURCEMANAGER", "NODEMANAGER");
-      }
-      // MAPREDUCE service, decommission TaskTracker
-      if (svcName === "MAPREDUCE") {
-        self.doDecommission(hostName, svcName, "JOBTRACKER", "TASKTRACKER");
-      }
-      // HBASE service, decommission RegionServer
-      if (svcName === "HBASE") {
-        self.warnBeforeDecommission(hostName, svcName, "HBASE_MASTER", "HBASE_REGIONSERVER");
-      }
-
-      // load data (if we need to show this background operations popup) from persist
-      App.router.get('applicationController').dataLoading().done(function (initValue) {
-        if (initValue) {
-          App.router.get('backgroundOperationsController').showPopup();
-        }
-      });
+    return App.showConfirmationPopup(function () {
+      self.runDecommission.call(self, self.get('content.hostName'), component.get('service.serviceName'));
     });
   },
-  
+  /**
+   * identify correct component to run decommission on them by service name,
+   * in result run proper decommission method
+   * @param hostName
+   * @param svcName
+   */
+  runDecommission: function (hostName, svcName) {
+    switch (svcName) {
+      case 'HDFS':
+        this.doDecommission(hostName, svcName, "NAMENODE", "DATANODE");
+        break;
+      case 'YARN':
+        this.doDecommission(hostName, svcName, "RESOURCEMANAGER", "NODEMANAGER");
+        break;
+      case 'MAPREDUCE':
+        this.doDecommission(hostName, svcName, "JOBTRACKER", "TASKTRACKER");
+        break;
+      case 'HBASE':
+        this.warnBeforeDecommission(hostName, svcName, "HBASE_MASTER", "HBASE_REGIONSERVER");
+    }
+    this.showBackgroundOperationsPopup();
+  },
+
   /**
    * Send command to server to run recommission on DATANODE, TASKTRACKER, NODEMANAGER
    * @param {App.HostComponent} component
    * @method recommission
    */
-  recommission: function(component){
+  recommission: function (component) {
     var self = this;
-    App.showConfirmationPopup(function(){
-      var svcName = component.get('service.serviceName');
-      var hostName = self.get('content.hostName');
-      // HDFS service, Recommission datanode
-      if (svcName === "HDFS") {
-        self.doRecommissionAndStart(hostName, svcName, "NAMENODE", "DATANODE");
-      }
-      // YARN service, Recommission nodeManager
-      if (svcName === "YARN") {
-        self.doRecommissionAndStart(hostName, svcName, "RESOURCEMANAGER", "NODEMANAGER");
-      }
-      // MAPREDUCE service, Recommission taskTracker
-      if (svcName === "MAPREDUCE") {
-        self.doRecommissionAndRestart(hostName, svcName, "JOBTRACKER", "TASKTRACKER");
-      }
-      // HBASE service, Recommission RegionServer
-      if (svcName === "HBASE") {
-        self.doRecommissionAndStart(hostName, svcName, "HBASE_MASTER", "HBASE_REGIONSERVER");
-      }
-
-      // load data (if we need to show this background operations popup) from persist
-      App.router.get('applicationController').dataLoading().done(function (initValue) {
-        if (initValue) {
-          App.router.get('backgroundOperationsController').showPopup();
-        }
-      });
+    App.showConfirmationPopup(function () {
+      self.runRecommission.call(self, self.get('content.hostName'), component.get('service.serviceName'));
     });
   },
+  /**
+   * identify correct component to run recommission on them by service name,
+   * in result run proper recommission method
+   * @param hostName
+   * @param svcName
+   */
+  runRecommission: function (hostName, svcName) {
+    switch (svcName) {
+      case 'HDFS':
+        this.doRecommissionAndStart(hostName, svcName, "NAMENODE", "DATANODE");
+        break;
+      case 'YARN':
+        this.doRecommissionAndStart(hostName, svcName, "RESOURCEMANAGER", "NODEMANAGER");
+        break;
+      case 'MAPREDUCE':
+        this.doRecommissionAndRestart(hostName, svcName, "JOBTRACKER", "TASKTRACKER");
+        break;
+      case 'HBASE':
+        this.doRecommissionAndStart(hostName, svcName, "HBASE_MASTER", "HBASE_REGIONSERVER");
+    }
+    this.showBackgroundOperationsPopup();
+  },
 
   /**
    * Performs Decommission (for DN, TT and NM)
@@ -986,7 +971,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {string} slaveType
    * @method doDecommission
    */
-  doDecommission: function(hostName, serviceName, componentName, slaveType){
+  doDecommission: function (hostName, serviceName, componentName, slaveType) {
     var contextNameString = 'hosts.host.' + slaveType.toLowerCase() + '.decommission';
     var context = Em.I18n.t(contextNameString);
     App.ajax.send({
@@ -996,7 +981,7 @@ App.MainHostDetailsController = Em.Controller.extend({
         context: context,
         command: 'DECOMMISSION',
         hostName: hostName,
-        serviceName: serviceName ,
+        serviceName: serviceName,
         componentName: componentName,
         slaveType: slaveType
       },
@@ -1006,18 +991,18 @@ App.MainHostDetailsController = Em.Controller.extend({
   },
 
   /**
-   * Recomends user to put component in MM before decommision (for HBASE only)
+   * Recommend user to put component in MM before decommission (for HBASE only)
    * @method warnBeforeDecommission
    * @param {string[]} hostNames - list of host when run from bulk operations or current host
    * @param {string} serviceName - serviceName
-   * @param {string} componentName - master compoent name
+   * @param {string} componentName - master component name
    * @param {string} slaveType - slave component name
    */
   warnBeforeDecommission: function(hostNames, serviceName, componentName, slaveType) {
     if (App.HostComponent.find().findProperty('componentName', componentName).get('passiveState') == "OFF") {
       App.ModalPopup.show({
         header: Em.I18n.t('common.warning'),
-        message: function(){
+        message: function () {
           return Em.I18n.t('hostPopup.reccomendation.beforeDecommission').format(App.format.components[componentName]);
         }.property(),
         bodyClass: Em.View.extend({
@@ -1038,37 +1023,38 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {string} componentName - master compoent name
    * @param {string} slaveType - slave component name
    */
-  doDecommissionRegionServer: function(hostNames, serviceName, componentName, slaveType){
-
+  doDecommissionRegionServer: function (hostNames, serviceName, componentName, slaveType) {
     App.ajax.send({
       name: 'host.host_component.recommission_and_restart',
       sender: this,
       data: {
         intervalTimeSeconds: 1,
-        tolerateSize : 0,
-        batches:[
+        tolerateSize: 0,
+        batches: [
           {
-            "order_id" : 1,
-            "type" : "POST",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
-                "context" : Em.I18n.t('hosts.host.regionserver.decommission.batch1'),
-                "command" : "DECOMMISSION",
-                "parameters" : {
+            "order_id": 1,
+            "type": "POST",
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
+            "RequestBodyInfo": {
+              "RequestInfo": {
+                "context": Em.I18n.t('hosts.host.regionserver.decommission.batch1'),
+                "command": "DECOMMISSION",
+                "parameters": {
                   "slave_type": slaveType,
                   "excluded_hosts": hostNames
                 }
               },
-              "Requests/resource_filters": [{"service_name" : serviceName, "component_name" : componentName}]
+              "Requests/resource_filters": [
+                {"service_name": serviceName, "component_name": componentName}
+              ]
             }
           },
           {
             "order_id": 2,
             "type": "PUT",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/host_components",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/host_components",
+            "RequestBodyInfo": {
+              "RequestInfo": {
                 context: Em.I18n.t('hosts.host.regionserver.decommission.batch2'),
                 query: 'HostRoles/component_name=' + slaveType + '&HostRoles/host_name.in(' + hostNames + ')&HostRoles/maintenance_state=OFF'
               },
@@ -1080,16 +1066,16 @@ App.MainHostDetailsController = Em.Controller.extend({
             }
           },
           {
-            "order_id" : 3,
-            "type" : "POST",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
-                "context" : Em.I18n.t('hosts.host.regionserver.decommission.batch3'),
-                "command" : "DECOMMISSION",
-                "service_name" : serviceName,
-                "component_name" : componentName,
-                "parameters" : {
+            "order_id": 3,
+            "type": "POST",
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
+            "RequestBodyInfo": {
+              "RequestInfo": {
+                "context": Em.I18n.t('hosts.host.regionserver.decommission.batch3'),
+                "command": "DECOMMISSION",
+                "service_name": serviceName,
+                "component_name": componentName,
+                "parameters": {
                   "slave_type": slaveType,
                   "excluded_hosts": hostNames,
                   "mark_draining_only": "true"
@@ -1112,28 +1098,25 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method decommissionErrorCallback
    */
   decommissionErrorCallback: function (request, ajaxOptions, error) {
-    console.log('ERROR: '+ error);
+    console.log('ERROR: ' + error);
   },
 
   /**
    * Success ajax response for Recommission/Decommission slaves
    * @param {object} data
    * @method decommissionSuccessCallback
+   * @return {Boolean}
    */
-  decommissionSuccessCallback: function(data) {
-    if(data && (data.Requests || data.resources[0].RequestSchedule) ) {
+  decommissionSuccessCallback: function (data) {
+    if (data && (data.Requests || data.resources[0].RequestSchedule)) {
       if (!App.testMode) {
         App.router.get('clusterController').loadUpdatedStatusDelayed(500);
       }
-      // load data (if we need to show this background operations popup) from persist
-      App.router.get('applicationController').dataLoading().done(function (initValue) {
-        if (initValue) {
-          App.router.get('backgroundOperationsController').showPopup();
-        }
-      });
-    }
-    else {
+      this.showBackgroundOperationsPopup();
+      return true;
+    } else {
       console.log('cannot get request id from ', data);
+      return false;
     }
   },
 
@@ -1145,7 +1128,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {string} slaveType
    * @method doRecommissionAndStart
    */
-  doRecommissionAndStart:  function(hostNames, serviceName, componentName, slaveType){
+  doRecommissionAndStart: function (hostNames, serviceName, componentName, slaveType) {
     var contextNameString_1 = 'hosts.host.' + slaveType.toLowerCase() + '.recommission';
     var context_1 = Em.I18n.t(contextNameString_1);
     var contextNameString_2 = 'requestInfo.startHostComponent.' + slaveType.toLowerCase();
@@ -1155,30 +1138,32 @@ App.MainHostDetailsController = Em.Controller.extend({
       sender: this,
       data: {
         intervalTimeSeconds: 1,
-        tolerateSize : 1,
-        batches:[
+        tolerateSize: 1,
+        batches: [
           {
-            "order_id" : 1,
-            "type" : "POST",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
-                "context" : context_1,
-                "command" : "DECOMMISSION",
-                "parameters" : {
+            "order_id": 1,
+            "type": "POST",
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
+            "RequestBodyInfo": {
+              "RequestInfo": {
+                "context": context_1,
+                "command": "DECOMMISSION",
+                "parameters": {
                   "slave_type": slaveType,
                   "included_hosts": hostNames
                 }
               },
-              "Requests/resource_filters": [{"service_name" : serviceName, "component_name" : componentName}]
+              "Requests/resource_filters": [
+                {"service_name": serviceName, "component_name": componentName}
+              ]
             }
           },
           {
             "order_id": 2,
             "type": "PUT",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/host_components",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/host_components",
+            "RequestBodyInfo": {
+              "RequestInfo": {
                 context: startContext,
                 query: 'HostRoles/component_name=' + slaveType + '&HostRoles/host_name.in(' + hostNames + ')&HostRoles/maintenance_state=OFF'
               },
@@ -1204,7 +1189,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {string} slaveType
    * @method doRecommissionAndStart
    */
-  doRecommissionAndRestart:  function(hostNames, serviceName, componentName, slaveType){
+  doRecommissionAndRestart: function (hostNames, serviceName, componentName, slaveType) {
     var contextNameString_1 = 'hosts.host.' + slaveType.toLowerCase() + '.recommission';
     var context_1 = Em.I18n.t(contextNameString_1);
     var contextNameString_2 = 'hosts.host.' + slaveType.toLowerCase() + '.restart';
@@ -1214,35 +1199,37 @@ App.MainHostDetailsController = Em.Controller.extend({
       sender: this,
       data: {
         intervalTimeSeconds: 1,
-        tolerateSize : 1,
-        batches:[
+        tolerateSize: 1,
+        batches: [
           {
-            "order_id" : 1,
-            "type" : "POST",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
-                "context" : context_1,
-                "command" : "DECOMMISSION",
-                "parameters" : {
+            "order_id": 1,
+            "type": "POST",
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
+            "RequestBodyInfo": {
+              "RequestInfo": {
+                "context": context_1,
+                "command": "DECOMMISSION",
+                "parameters": {
                   "slave_type": slaveType,
                   "included_hosts": hostNames
                 }
               },
-              "Requests/resource_filters": [{"service_name" : serviceName, "component_name" : componentName}]
+              "Requests/resource_filters": [
+                {"service_name": serviceName, "component_name": componentName}
+              ]
             }
           },
           {
-            "order_id" : 2,
-            "type" : "POST",
-            "uri" : App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
-            "RequestBodyInfo" : {
-              "RequestInfo" : {
-                "context" : context_2,
-                "command" : "RESTART",
-                "service_name" : serviceName,
-                "component_name" : slaveType,
-                "hosts" : hostNames
+            "order_id": 2,
+            "type": "POST",
+            "uri": App.apiPrefix + "/clusters/" + App.get('clusterName') + "/requests",
+            "RequestBodyInfo": {
+              "RequestInfo": {
+                "context": context_2,
+                "command": "RESTART",
+                "service_name": serviceName,
+                "component_name": slaveType,
+                "hosts": hostNames
               }
             }
           }
@@ -1258,7 +1245,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} option
    * @method doAction
    */
-  doAction: function(option) {
+  doAction: function (option) {
     switch (option.context.action) {
       case "deleteHost":
         this.validateAndDeleteHost();
@@ -1285,14 +1272,14 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} context
    * @method onOffPassiveModeForHost
    */
-  onOffPassiveModeForHost: function(context) {
+  onOffPassiveModeForHost: function (context) {
     var state = context.active ? 'ON' : 'OFF';
     var self = this;
-    var message = context.label + ' for host';
-    App.showConfirmationPopup(function() {
-          self.hostPassiveModeRequest(state, message);
-        },
-        Em.I18n.t('hosts.passiveMode.popup').format(context.active ? 'On' : 'Off',this.get('content.hostName'))
+    var message = Em.I18n.t('hosts.host.details.for.postfix').format(context.label);
+    return App.showConfirmationPopup(function () {
+        self.hostPassiveModeRequest(state, message);
+      },
+      Em.I18n.t('hosts.passiveMode.popup').format(context.active ? 'On' : 'Off', this.get('content.hostName'))
     );
   },
 
@@ -1302,7 +1289,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {string} message
    * @method hostPassiveModeRequest
    */
-  hostPassiveModeRequest: function(state,message) {
+  hostPassiveModeRequest: function (state, message) {
     App.ajax.send({
       name: 'bulk_request.hosts.passive_state',
       sender: this,
@@ -1322,23 +1309,23 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} params
    * @method updateHost
    */
-  updateHost: function(data, opt, params) {
+  updateHost: function (data, opt, params) {
     this.set('content.passiveState', params.passive_state);
-    App.router.get('clusterController').loadUpdatedStatus(function(){
+    App.router.get('clusterController').loadUpdatedStatus(function () {
       batchUtils.infoPassiveState(params.passive_state);
     });
- },
+  },
 
   /**
    * Show confirmation popup for action "start all components"
    * @method doStartAllComponents
    */
-  doStartAllComponents: function() {
+  doStartAllComponents: function () {
     var self = this;
     var components = this.get('serviceNonClientActiveComponents');
-    var componentsLength = components == null ? 0 : components.get('length');
+    var componentsLength = Em.isNone(components) ? 0 : components.get('length');
     if (componentsLength > 0) {
-      App.showConfirmationPopup(function() {
+      return App.showConfirmationPopup(function () {
         self.sendStartComponentCommand(components, Em.I18n.t('hosts.host.maintainance.startAllComponents.context'));
       });
     }
@@ -1348,12 +1335,12 @@ App.MainHostDetailsController = Em.Controller.extend({
    * Show confirmation popup for action "stop all components"
    * @method doStopAllComponents
    */
-  doStopAllComponents: function() {
+  doStopAllComponents: function () {
     var self = this;
     var components = this.get('serviceNonClientActiveComponents');
-    var componentsLength = components == null ? 0 : components.get('length');
+    var componentsLength = Em.isNone(components) ? 0 : components.get('length');
     if (componentsLength > 0) {
-      App.showConfirmationPopup(function() {
+      return App.showConfirmationPopup(function () {
         self.sendStopComponentCommand(components, Em.I18n.t('hosts.host.maintainance.stopAllComponents.context'));
       });
     }
@@ -1363,79 +1350,93 @@ App.MainHostDetailsController = Em.Controller.extend({
    * Show confirmation popup for action "restart all components"
    * @method doRestartAllComponents
    */
-  doRestartAllComponents: function() {
+  doRestartAllComponents: function () {
     var self = this;
     var components = this.get('serviceActiveComponents');
-    var componentsLength = components == null ? 0 : components.get('length');
+    var componentsLength = Em.isNone(components) ? 0 : components.get('length');
     if (componentsLength > 0) {
-      App.showConfirmationPopup(function() {
+      App.showConfirmationPopup(function () {
         batchUtils.restartHostComponents(components, Em.I18n.t('rollingrestart.context.allOnSelectedHost').format(self.get('content.hostName')));
       });
     }
   },
 
   /**
-   * Deletion of hosts not supported for this version
-   * @method validateAndDeleteHost
+   * get info about host-components, exactly:
+   *  - host-components grouped by status, features
+   *  - flag, that indicate whether ZooKeeper Server is installed
+   * @return {Object}
    */
-  validateAndDeleteHost: function () {
-    if (!App.supports.deleteHost) {
-      return;
-    }
-    var stoppedStates = [App.HostComponentStatus.stopped,
-                         App.HostComponentStatus.install_failed,
-                         App.HostComponentStatus.upgrade_failed,
-                         App.HostComponentStatus.init,
-                         App.HostComponentStatus.unknown];
-    var masterComponents = [];
-    var runningComponents = [];
-    var unknownComponents = [];
-    var nonDeletableComponents = [];
-    var lastComponents = [];
+  getHostComponentsInfo: function () {
     var componentsOnHost = this.get('content.hostComponents');
     var allComponents = App.HostComponent.find();
-    var zkServerInstalled = false;
+    var stoppedStates = [App.HostComponentStatus.stopped,
+      App.HostComponentStatus.install_failed,
+      App.HostComponentStatus.upgrade_failed,
+      App.HostComponentStatus.init,
+      App.HostComponentStatus.unknown];
+    var container = {
+      zkServerInstalled: false,
+      lastComponents: [],
+      masterComponents: [],
+      runningComponents: [],
+      nonDeletableComponents: [],
+      unknownComponents: []
+    };
+
     if (componentsOnHost && componentsOnHost.get('length') > 0) {
       componentsOnHost.forEach(function (cInstance) {
         if (cInstance.get('componentName') === 'ZOOKEEPER_SERVER') {
-          zkServerInstalled = true;
+          container.zkServerInstalled = true;
         }
         if (allComponents.filterProperty('componentName', cInstance.get('componentName')).get('length') === 1) {
-          lastComponents.push(cInstance.get('displayName'));
+          container.lastComponents.push(cInstance.get('displayName'));
         }
         var workStatus = cInstance.get('workStatus');
         if (cInstance.get('isMaster') && !cInstance.get('isDeletable')) {
-          masterComponents.push(cInstance.get('displayName'));
+          container.masterComponents.push(cInstance.get('displayName'));
         }
         if (stoppedStates.indexOf(workStatus) < 0) {
-          runningComponents.push(cInstance.get('displayName'));
+          container.runningComponents.push(cInstance.get('displayName'));
         }
         if (!cInstance.get('isDeletable')) {
-          nonDeletableComponents.push(cInstance.get('displayName'));
+          container.nonDeletableComponents.push(cInstance.get('displayName'));
         }
         if (workStatus === App.HostComponentStatus.unknown) {
-          unknownComponents.push(cInstance.get('displayName'));
+          container.unknownComponents.push(cInstance.get('displayName'));
         }
       });
     }
-    if (masterComponents.length > 0) {
-      this.raiseDeleteComponentsError(masterComponents, 'masterList');
+    return container;
+  },
+
+  /**
+   * Deletion of hosts not supported for this version
+   * @method validateAndDeleteHost
+   */
+  validateAndDeleteHost: function () {
+    if (!App.supports.deleteHost) {
+      return false;
+    }
+    var container = this.getHostComponentsInfo();
+
+    if (container.masterComponents.length > 0) {
+      this.raiseDeleteComponentsError(container.masterComponents, 'masterList');
       return;
-    } else if (nonDeletableComponents.length > 0) {
-      this.raiseDeleteComponentsError(nonDeletableComponents, 'nonDeletableList');
+    } else if (container.nonDeletableComponents.length > 0) {
+      this.raiseDeleteComponentsError(container.nonDeletableComponents, 'nonDeletableList');
       return;
-    } else if (runningComponents.length > 0) {
-      this.raiseDeleteComponentsError(runningComponents, 'runningList');
+    } else if (container.runningComponents.length > 0) {
+      this.raiseDeleteComponentsError(container.runningComponents, 'runningList');
       return;
     }
-    if (zkServerInstalled) {
+    if (container.zkServerInstalled) {
       var self = this;
-      App.showConfirmationPopup(function() {
-        self._doDeleteHost(unknownComponents, lastComponents);
+      return App.showConfirmationPopup(function () {
+        self._doDeleteHost(container.unknownComponents, container.lastComponents);
       }, Em.I18n.t('hosts.host.addComponent.deleteHostWithZooKeeper'));
-    }
-    else {
-      this._doDeleteHost(unknownComponents, lastComponents);
+    } else {
+      this._doDeleteHost(container.unknownComponents, container.lastComponents);
     }
   },
 
@@ -1449,19 +1450,19 @@ App.MainHostDetailsController = Em.Controller.extend({
     App.ModalPopup.show({
       header: Em.I18n.t('hosts.cant.do.popup.title'),
       type: type,
-      showBodyEnd: function() {
+      showBodyEnd: function () {
         return this.get('type') === 'runningList' || this.get('type') === 'masterList';
       }.property(),
       components: components,
-      componentsStr: function() {
+      componentsStr: function () {
         return this.get('components').join(", ");
       }.property(),
-      componentsBody: function() {
-        return Em.I18n.t('hosts.cant.do.popup.'+type+'.body').format(this.get('components').length);
+      componentsBody: function () {
+        return Em.I18n.t('hosts.cant.do.popup.' + type + '.body').format(this.get('components').length);
       }.property(),
-      componentsBodyEnd: function() {
+      componentsBodyEnd: function () {
         if (this.get('showBodyEnd')) {
-          return Em.I18n.t('hosts.cant.do.popup.'+type+'.body.end');
+          return Em.I18n.t('hosts.cant.do.popup.' + type + '.body.end');
         }
         return '';
       }.property(),
@@ -1478,30 +1479,30 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {string[]} lastComponents
    * @method _doDeleteHost
    */
-  _doDeleteHost: function(unknownComponents,lastComponents) {
+  _doDeleteHost: function (unknownComponents, lastComponents) {
     var self = this;
     App.ModalPopup.show({
       header: Em.I18n.t('hosts.delete.popup.title'),
-      deletePopupBody: function() {
+      deletePopupBody: function () {
         return Em.I18n.t('hosts.delete.popup.body').format(self.get('content.publicHostName'));
       }.property(),
-      lastComponent: function() {
-         if (lastComponents && lastComponents.length) {
-           this.set('isChecked', false);
-           return true;
-         } else {
-           this.set('isChecked', true);
-           return false;
-         }
+      lastComponent: function () {
+        if (lastComponents && lastComponents.length) {
+          this.set('isChecked', false);
+          return true;
+        } else {
+          this.set('isChecked', true);
+          return false;
+        }
       }.property(),
       disablePrimary: function () {
         return !this.get('isChecked');
       }.property('isChecked'),
       isChecked: false,
-      lastComponentError:  Em.View.extend({
+      lastComponentError: Em.View.extend({
         template: Em.Handlebars.compile(Em.I18n.t('hosts.delete.popup.body.msg4').format(lastComponents))
       }),
-      unknownComponents: function() {
+      unknownComponents: function () {
         if (unknownComponents && unknownComponents.length) {
           return unknownComponents.join(", ");
         }
@@ -1510,11 +1511,11 @@ App.MainHostDetailsController = Em.Controller.extend({
       bodyClass: Em.View.extend({
         templateName: require('templates/main/host/details/doDeleteHostPopup')
       }),
-      onPrimary: function() {
+      onPrimary: function () {
         self.set('fromDeleteHost', true);
         var allComponents = self.get('content.hostComponents');
         var deleteError = null;
-        allComponents.forEach(function(component){
+        allComponents.forEach(function (component) {
           if (!deleteError) {
             deleteError = self._doDeleteHostComponent(component);
           }
@@ -1537,9 +1538,9 @@ App.MainHostDetailsController = Em.Controller.extend({
           App.ajax.defaultErrorHandler(deleteError.xhr, deleteError.url, deleteError.method, deleteError.xhr.status);
         }
       },
-      deleteHostSuccessCallback: function(data) {
+      deleteHostSuccessCallback: function (data) {
         var dialogSelf = this;
-        App.router.get('updateController').updateHost(function(){
+        App.router.get('updateController').updateHost(function () {
           self.loadConfigs();
           dialogSelf.hide();
           App.router.transitionTo('hosts.index');
@@ -1561,9 +1562,9 @@ App.MainHostDetailsController = Em.Controller.extend({
    * Send command to server to restart all host components with stale configs
    * @method restartAllStaleConfigComponents
    */
-  restartAllStaleConfigComponents: function() {
+  restartAllStaleConfigComponents: function () {
     var self = this;
-    App.showConfirmationPopup(function () {
+    return App.showConfirmationPopup(function () {
       var staleComponents = self.get('content.componentsWithStaleConfigs');
       batchUtils.restartHostComponents(staleComponents, Em.I18n.t('rollingrestart.context.allWithStaleConfigsOnSelectedHost').format(self.get('content.hostName')));
     });
@@ -1575,7 +1576,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method moveComponent
    */
   moveComponent: function (event) {
-    App.showConfirmationPopup(function() {
+    App.showConfirmationPopup(function () {
       var component = event.context;
       var reassignMasterController = App.router.get('reassignMasterController');
       reassignMasterController.saveComponentToReassign(component);
@@ -1590,16 +1591,15 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @param {object} event
    * @method refreshConfigs
    */
-  refreshConfigs: function(event) {
+  refreshConfigs: function (event) {
     var self = this;
-    var components = event.context.filter(function(component) {
+    var components = event.context.filter(function (component) {
       return component.get('staleConfigs');
     });
     if (components.get('length') > 0) {
-      App.showConfirmationPopup(function() {
+      return App.showConfirmationPopup(function () {
         batchUtils.restartHostComponents(components, Em.I18n.t('rollingrestart.context.allClientsOnSelectedHost').format(self.get('content.hostName')));
       });
     }
   }
-
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/5c49faf5/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index c958066..6a43257 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1508,6 +1508,7 @@ Em.I18n.translations = {
   'hosts.host.details.stopAllComponents':'Stop All Components',
   'hosts.host.details.restartAllComponents':'Restart All Components',
   'hosts.host.details.refreshConfigs':'Refresh configs',
+  'hosts.host.details.for.postfix':'{0} for host',
 
   'host.host.componentFilter.master':'Master Components',
   'host.host.componentFilter.slave':'Slave Components',

http://git-wip-us.apache.org/repos/asf/ambari/blob/5c49faf5/ambari-web/test/controllers/main/host/configs_service_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host/configs_service_test.js b/ambari-web/test/controllers/main/host/configs_service_test.js
new file mode 100644
index 0000000..f398402
--- /dev/null
+++ b/ambari-web/test/controllers/main/host/configs_service_test.js
@@ -0,0 +1,223 @@
+/**
+ * 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');
+require('controllers/main/host/configs_service');
+
+
+describe('App.MainHostServiceConfigsController', function () {
+
+  var controller = App.MainHostServiceConfigsController.create({
+    host: Em.Object.create()
+  });
+
+
+  describe('#filterServiceConfigs()', function () {
+    var testCases = [
+      {
+        title: 'configCategories is empty',
+        content: {
+          configCategories: [],
+          hostComponents: []
+        },
+        result: []
+      },
+      {
+        title: 'Category hostComponentNames is null',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: null})
+          ],
+          hostComponents: []
+        },
+        result: [
+          Em.Object.create({hostComponentNames: null})
+        ]
+      },
+      {
+        title: 'Components of host are empty',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: ['comp1']})
+          ],
+          hostComponents: []
+        },
+        result: []
+      },
+      {
+        title: 'Host components do not match component of categories',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: ['comp1']})
+          ],
+          hostComponents: [
+            {
+              componentName: 'comp2'
+            }
+          ]
+        },
+        result: []
+      },
+      {
+        title: 'Host components match component of categories',
+        content: {
+          configCategories: [
+            Em.Object.create({hostComponentNames: ['comp1']})
+          ],
+          hostComponents: [
+            {
+              componentName: 'comp1'
+            }
+          ]
+        },
+        result: [
+          Em.Object.create({hostComponentNames: ['comp1']})
+        ]
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        controller.set('host.hostComponents', test.content.hostComponents);
+        expect(controller.filterServiceConfigs(test.content.configCategories)).to.eql(test.result);
+      });
+    });
+  });
+
+  describe('#constructUrlParams()', function () {
+    it('loadedGroupToOverrideSiteToTagMap is empty', function () {
+      var loadedGroupToOverrideSiteToTagMap = {};
+      var configGroups = [];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('Group does not have hosts', function () {
+      var loadedGroupToOverrideSiteToTagMap = {'group1': {}};
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: []
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('Group does not contain current host', function () {
+      controller.set('host.hostName', 'host2');
+      var loadedGroupToOverrideSiteToTagMap = {'group1': {}};
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: ['host1']
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('No type to tags relations in group', function () {
+      var loadedGroupToOverrideSiteToTagMap = {'group1': {}};
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: ['host2']
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql([]);
+      expect(controller.get('typeTagToGroupMap')).to.eql({});
+    });
+    it('Input params are correct', function () {
+      var loadedGroupToOverrideSiteToTagMap = {
+        'group1': {
+          'type1': 'tag1'
+        }
+      };
+      var configGroups = [
+        Em.Object.create({
+          name: 'group1',
+          hosts: ['host2']
+        })
+      ];
+      expect(controller.constructUrlParams(loadedGroupToOverrideSiteToTagMap, configGroups)).to.eql(['(type=type1&tag=tag1)']);
+      expect(controller.get('typeTagToGroupMap')['type1///tag1'].get('name')).to.equal('group1');
+    });
+  });
+
+  describe('#loadServiceConfigHostsOverrides()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "constructUrlParams", function(){
+        return controller.get('testUrlParams');
+      });
+      sinon.spy(App.ajax, "send");
+    });
+    afterEach(function () {
+      controller.constructUrlParams.restore();
+      App.ajax.send.restore();
+    });
+
+    it('configKeyToConfigMap and urlParams are empty', function () {
+      var serviceConfigs = [];
+      controller.set('testUrlParams', []);
+      controller.loadServiceConfigHostsOverrides(serviceConfigs);
+      expect(controller.get('configKeyToConfigMap')).to.eql({});
+      expect(App.ajax.send.called).to.be.false;
+    });
+
+    it('configKeyToConfigMap and urlParams are correct', function () {
+      var serviceConfigs = [{
+        name: 'config1'
+      }];
+      controller.set('testUrlParams', ['params']);
+      controller.loadServiceConfigHostsOverrides(serviceConfigs);
+      expect(controller.get('configKeyToConfigMap')).to.eql({'config1': {
+        name: 'config1'
+      }});
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#loadServiceConfigHostsOverridesSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.config, "handleSpecialProperties");
+    });
+    afterEach(function () {
+      App.config.handleSpecialProperties.restore();
+    });
+    it('data.items is empty', function () {
+      var data = {
+        items: [
+          {
+            type: 'type1',
+            tag: 'tag1',
+            properties: {'prop1': 'value1'}
+          }
+        ]
+      };
+      controller.set('typeTagToGroupMap', {'type1///tag1': {}});
+      controller.set('configKeyToConfigMap', {'prop1': {}});
+      controller.loadServiceConfigHostsOverridesSuccessCallback(data);
+      expect(App.config.handleSpecialProperties.calledWith({
+        value: 'value1',
+        isOriginalSCP: false,
+        group: {}
+      })).to.be.true;
+    });
+  });
+});