You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2015/11/03 13:54:55 UTC

[2/2] ambari git commit: AMBARI-13692. Refactor HostProgressPopup (onechiporenko)

AMBARI-13692. Refactor HostProgressPopup (onechiporenko)


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

Branch: refs/heads/trunk
Commit: 76dd478e5d701ae2c64989d5e07a9caada3ddfb6
Parents: 6321a0d
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Tue Nov 3 14:50:27 2015 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Tue Nov 3 14:50:27 2015 +0200

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |    1 +
 ambari-web/app/utils/host_progress_popup.js     | 1246 +++++++-----------
 ambari-web/app/views.js                         |    1 +
 .../common/host_progress_popup_body_view.js     |  749 +++++++++++
 .../test/utils/host_progress_popup_test.js      |  112 ++
 .../host_progress_popup_body_view_test.js       |   58 +
 6 files changed, 1388 insertions(+), 779 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/76dd478e/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 cb292a5..6617c97 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -266,6 +266,7 @@ var files = [
   'test/views/main/admin/highAvailability/nameNode/step6_view_test',
   'test/views/main/admin/highAvailability/nameNode/step8_view_test',
   'test/views/main/admin/highAvailability/nameNode/wizard_view_test',
+  'test/views/common/host_progress_popup_body_view_test',
   'test/views/common/configs/config_history_flow_test',
   'test/views/common/configs/overriddenProperty_view_test',
   'test/views/common/configs/service_config_view_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/76dd478e/ambari-web/app/utils/host_progress_popup.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/host_progress_popup.js b/ambari-web/app/utils/host_progress_popup.js
index e834bbe..831baeb 100644
--- a/ambari-web/app/utils/host_progress_popup.js
+++ b/ambari-web/app/utils/host_progress_popup.js
@@ -19,37 +19,112 @@
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
 var date = require('utils/date/date');
+var dataUtils = require('utils/data_manipulation');
+
+/**
+ * Host information shown in the operations popup
+ * @typedef {Em.Object} wrappedHost
+ * @property {string} name
+ * @property {string} publicName
+ * @property {string} displayName
+ * @property {number} progress
+ * @property {boolean} isInProgress
+ * @property {string} serviceName
+ * @property {string} status
+ * @property {number} isVisible
+ * @property {string} icon
+ * @property {string} barColor
+ * @property {string} barWidth
+ */
+
+/**
+ * Task information shown in the operations popup
+ * @typedef {Em.Object} wrappedTask
+ * @property {string} id
+ * @property {string} hostName
+ * @property {string} command
+ * @property {string} commandDetail
+ * @property {string} status
+ * @property {string} role
+ * @property {string} stderr
+ * @property {string} stdout
+ * @property {number} request_id
+ * @property {boolean} isVisible
+ * @property {string} startTime
+ * @property {string} duration
+ * @property {string} icon
+ */
+
+/**
+ * Service information shown in the operations popup
+ * @typedef {Em.Object} wrappedService
+ * @property {string} id
+ * @property {string} displayName
+ * @property {string} progress
+ * @property {string} status
+ * @property {boolean} isRunning
+ * @property {string} name
+ * @property {boolean} isVisible
+ * @property {string} startTime
+ * @property {string} duration
+ * @property {string} icon
+ * @property {string} barColor
+ * @property {boolean} isInProgress
+ * @property {string} barWidth
+ * @property {number} sourceRequestScheduleId
+ * @property {string} contextCommand
+ */
 
 /**
  * App.HostPopup is for the popup that shows up upon clicking already-performed or currently-in-progress operations
+ * Allows to abort executing operations
+ *
+ * @type {Em.Object}
+ * @class {HostPopup}
  */
 App.HostPopup = Em.Object.create({
 
   name: 'hostPopup',
 
+  /**
+   * @type {object[]}
+   */
   servicesInfo: [],
+
+  /**
+   * @type {?wrappedHost[]}
+   */
   hosts: null,
+
+  /**
+   * @type {?object[]}
+   */
   inputData: null,
 
   /**
    * @type {string}
    */
-  serviceName: "",
+  serviceName: '',
 
   /**
-   * @type {Number}
+   * @type {?Number}
    */
   currentServiceId: null,
+
+  /**
+   * @type {?Number}
+   */
   previousServiceId: null,
 
   /**
    * @type {string}
    */
-  popupHeaderName: "",
+  popupHeaderName: '',
 
   operationInfo: null,
+
   /**
-   * @type {App.Controller}
+   * @type {?App.Controller}
    */
   dataSourceController: null,
 
@@ -59,15 +134,18 @@ App.HostPopup = Em.Object.create({
   isBackgroundOperations: false,
 
   /**
-   * @type {string}
+   * @type {?string}
    */
   currentHostName: null,
 
   /**
-   * @type {App.ModalPopup}
+   * @type {?App.ModalPopup}
    */
   isPopup: null,
 
+  /**
+   * @type {object}
+   */
   detailedProperties: {
     stdout: 'stdout',
     stderr: 'stderr',
@@ -75,6 +153,35 @@ App.HostPopup = Em.Object.create({
     errorLog: 'error_log'
   },
 
+  /**
+   * @type {object}
+   */
+  barColorMap: {
+    'FAILED': 'progress-danger',
+    'ABORTED': 'progress-warning',
+    'TIMEDOUT': 'progress-warning',
+    'IN_PROGRESS': 'progress-info',
+    'COMPLETED': 'progress-success'
+  },
+
+  /**
+   * map to get css class with styles by service status
+   *
+   * @type {object}
+   */
+  statusesStyleMap: {
+    'FAILED': ['FAILED', 'icon-exclamation-sign', 'progress-danger', false],
+    'ABORTED': ['ABORTED', 'icon-minus', 'progress-warning', false],
+    'TIMEDOUT': ['TIMEDOUT', 'icon-time', 'progress-warning', false],
+    'IN_PROGRESS': ['IN_PROGRESS', 'icon-cogs', 'progress-info', true],
+    'COMPLETED': ['SUCCESS', 'icon-ok', 'progress-success', false]
+  },
+
+  /**
+   * View with "Abort Request"-button
+   *
+   * @type {Em.View}
+   */
   abortIcon: Em.View.extend({
     tagName: 'i',
     classNames: ['abort-icon', 'icon-remove-circle', 'pointer'],
@@ -87,24 +194,36 @@ App.HostPopup = Em.Object.create({
         placement: "top",
         title: Em.I18n.t('hostPopup.bgop.abortRequest.title')
       });
+    },
+    willDestroyElement: function () {
+      $(this.get('element')).tooltip('destroy');
     }
   }),
 
+  /**
+   * View with status icon (and tooltip on it)
+   *
+   * @type {Em.View}
+   */
   statusIcon: Em.View.extend({
     tagName: 'i',
     classNames: ["service-status"],
     classNameBindings: ['servicesInfo.status', 'servicesInfo.icon', 'additionalClass'],
     attributeBindings: ['data-original-title'],
-    'data-original-title': function() {
+    'data-original-title': function () {
       return this.get('servicesInfo.status');
     }.property('servicesInfo.status'),
     didInsertElement: function () {
       App.tooltip($(this.get('element')));
+    },
+    willDestroyElement: function () {
+      $(this.get('element')).tooltip('destroy');
     }
   }),
 
   /**
    * Determines if background operation can be aborted depending on its status
+   *
    * @param status
    * @returns {boolean}
    */
@@ -115,13 +234,15 @@ App.HostPopup = Em.Object.create({
 
   /**
    * Send request to abort operation
+   *
+   * @method abortRequest
    */
   abortRequest: function (serviceInfo) {
     var requestName = serviceInfo.get('name');
     var self = this;
     App.showConfirmationPopup(function () {
       serviceInfo.set('isAbortable', false);
-      App.ajax.send({
+      return App.ajax.send({
         name: 'background_operations.abort_request',
         sender: self,
         data: {
@@ -138,30 +259,36 @@ App.HostPopup = Em.Object.create({
 
   /**
    * Method called on successful sending request to abort operation
+   *
+   * @return {App.ModalPopup}
+   * @method abortRequestSuccessCallback
    */
   abortRequestSuccessCallback: function (response, request, data) {
-    App.ModalPopup.show({
+    return App.ModalPopup.show({
       header: Em.I18n.t('hostPopup.bgop.abortRequest.modal.header'),
-      bodyClass: Em.View.extend({
-        template: Em.Handlebars.compile(Em.I18n.t('hostPopup.bgop.abortRequest.modal.body').format(data.requestName))
-      }),
+      body: Em.I18n.t('hostPopup.bgop.abortRequest.modal.body').format(data.requestName),
       secondary: null
     });
   },
 
   /**
    * Method called on unsuccessful sending request to abort operation
+   *
+   * @method abortRequestErrorCallback
    */
   abortRequestErrorCallback: function (xhr, textStatus, error, opt, data) {
     data.serviceInfo.set('isAbortable', this.isAbortableByStatus(data.serviceInfo.status));
     App.ajax.defaultErrorHandler(xhr, opt.url, 'PUT', xhr.status);
   },
+
   /**
    * Entering point of this component
+   *
    * @param {String} serviceName
    * @param {Object} controller
    * @param {Boolean} isBackgroundOperations
    * @param {Integer} requestId
+   * @method initPopup
    */
   initPopup: function (serviceName, controller, isBackgroundOperations, requestId) {
     if (!isBackgroundOperations) {
@@ -169,12 +296,15 @@ App.HostPopup = Em.Object.create({
       this.set("popupHeaderName", serviceName);
     }
 
-    this.set('currentServiceId', requestId);
-    this.set("serviceName", serviceName);
-    this.set("dataSourceController", controller);
-    this.set("isBackgroundOperations", isBackgroundOperations);
-    this.set("inputData", this.get("dataSourceController.services"));
-    if(isBackgroundOperations){
+    this.setProperties({
+      currentServiceId: requestId,
+      serviceName: serviceName,
+      dataSourceController: controller,
+      isBackgroundOperations: isBackgroundOperations,
+      inputData: this.get("dataSourceController.services")
+    });
+
+    if (isBackgroundOperations) {
       this.onServiceUpdate();
     } else {
       this.onHostUpdate();
@@ -184,172 +314,127 @@ App.HostPopup = Em.Object.create({
 
   /**
    * clear info popup data
+   *
+   * @method clearHostPopup
    */
   clearHostPopup: function () {
-    this.set('servicesInfo', []);
-    this.set('hosts', null);
-    this.set('inputData', null);
-    this.set('serviceName', "");
-    this.set('currentServiceId', null);
-    this.set('previousServiceId', null);
-    this.set('popupHeaderName', "");
-    this.set('dataSourceController', null);
-    this.set('currentHostName', null);
+    this.setProperties({
+      servicesInfo: [],
+      host: null,
+      inputData: null,
+      serviceName: '',
+      currentServiceId: null,
+      previousServiceId: null,
+      popupHeaderName: '',
+      dataSourceController: null,
+      currentHostName: null
+    });
     this.get('isPopup') ? this.get('isPopup').remove() : null;
   },
 
   /**
    * Depending on tasks status
-   * @param {Array} tasks
-   * @return {Array} [Status, Icon type, Progressbar color, is IN_PROGRESS]
+   *
+   * @param {object[]} tasks
+   * @return {*[]} [Status, Icon type, Progressbar color, is IN_PROGRESS]
+   * @method getStatus
    */
-  getStatus: function(tasks){
+  getStatus: function (tasks) {
     var isCompleted = true;
-    var status;
     var tasksLength = tasks.length;
-    var isFailed = false;
-    var isAborted = false;
-    var isTimedout = false;
-    var isInProgress = false;
     for (var i = 0; i < tasksLength; i++) {
-      if (tasks[i].Tasks.status !== 'COMPLETED') {
+      var taskStatus = tasks[i].Tasks.status;
+      if (taskStatus !== 'COMPLETED') {
         isCompleted = false;
       }
-      if(tasks[i].Tasks.status === 'FAILED'){
-        isFailed = true;
+      if (taskStatus === 'FAILED') {
+        return ['FAILED', 'icon-exclamation-sign', 'progress-danger', false];
       }
-      if (tasks[i].Tasks.status === 'ABORTED') {
-        isAborted = true;
+      if (taskStatus === 'ABORTED') {
+        return ['ABORTED', 'icon-minus', 'progress-warning', false];
       }
-      if (tasks[i].Tasks.status === 'TIMEDOUT') {
-        isTimedout = true;
+      if (taskStatus === 'TIMEDOUT') {
+        return ['TIMEDOUT', 'icon-time', 'progress-warning', false];
       }
-      if (tasks[i].Tasks.status === 'IN_PROGRESS') {
-        isInProgress = true;
+      if (taskStatus === 'IN_PROGRESS') {
+        return ['IN_PROGRESS', 'icon-cogs', 'progress-info', true]
       }
     }
-    if (isFailed) {
-      status = ['FAILED', 'icon-exclamation-sign', 'progress-danger', false];
-    } else if (isAborted) {
-      status = ['ABORTED', 'icon-minus', 'progress-warning', false];
-    } else if (isTimedout) {
-      status = ['TIMEDOUT', 'icon-time', 'progress-warning', false];
-    } else if (isInProgress) {
-      status = ['IN_PROGRESS', 'icon-cogs', 'progress-info', true];
-    }
-    if(status){
-      return status;
-    } else if(isCompleted){
+    if (isCompleted) {
       return ['SUCCESS', 'icon-ok', 'progress-success', false];
-    } else {
-      return ['PENDING', 'icon-cog', 'progress-info', true];
     }
+    return ['PENDING', 'icon-cog', 'progress-info', true];
   },
 
   /**
    * Progress of host or service depending on tasks status
-   * @param {Array} tasks
+   * If no tasks, progress is 0
+   *
+   * @param {?object[]} tasks
    * @return {Number} percent of completion
+   * @method getProgress
    */
   getProgress: function (tasks) {
-    if (!tasks || tasks.length === 0) return 0;
-
-    var completedActions = 0;
-    var queuedActions = 0;
-    var inProgressActions = 0;
-
-    tasks.forEach(function (task) {
-      if (['COMPLETED', 'FAILED', 'ABORTED', 'TIMEDOUT'].contains(task.Tasks.status)) {
-        completedActions++;
-      } else if (task.Tasks.status === 'QUEUED') {
-        queuedActions++;
-      } else if (task.Tasks.status === 'IN_PROGRESS') {
-        inProgressActions++;
-      }
-    });
-    return Math.ceil(((queuedActions * 0.09) + (inProgressActions * 0.35) + completedActions ) / tasks.length * 100);
+    if (!tasks || !tasks.length) {
+      return 0;
+    }
+
+    var groupedByStatus = dataUtils.groupPropertyValues(tasks, 'Tasks.status');
+
+    var completedActions = Em.getWithDefault(groupedByStatus, 'COMPLETED.length', 0) +
+      Em.getWithDefault(groupedByStatus, 'FAILED.length', 0) +
+      Em.getWithDefault(groupedByStatus, 'ABORTED.length', 0) +
+      Em.getWithDefault(groupedByStatus, 'TIMEDOUT.length', 0);
+    var queuedActions = Em.getWithDefault(groupedByStatus, 'QUEUED.length', 0);
+    var inProgressActions = Em.getWithDefault(groupedByStatus, 'IN_PROGRESS.length', 0);
+
+    return Math.ceil((queuedActions * 0.09 + inProgressActions * 0.35 + completedActions ) / tasks.length * 100);
   },
 
   /**
    * Count number of operations for select box options
-   * @param {Object[]} obj
-   * @param {Object[]} categories
+   *
+   * @param {?Object[]} obj
+   * @param {progressPopupCategoryObject[]} categories
+   * @method setSelectCount
    */
   setSelectCount: function (obj, categories) {
-    if (!obj) return;
-    var countAll = obj.length;
-    var countPending = 0;
-    var countInProgress = 0;
-    var countFailed = 0;
-    var countCompleted = 0;
-    var countAborted = 0;
-    var countTimedout = 0;
-    obj.forEach(function(item){
-      switch (item.status){
-        case 'pending':
-          countPending++;
-          break;
-        case 'queued':
-          countPending++;
-          break;
-        case 'in_progress':
-          countInProgress++;
-          break;
-        case 'failed':
-          countFailed++;
-          break;
-        case 'success':
-          countCompleted++;
-          break;
-        case 'completed':
-          countCompleted++;
-          break;
-        case 'aborted':
-          countAborted++;
-          break;
-        case 'timedout':
-          countTimedout++;
-          break;
-      }
-    });
-
-    categories.findProperty("value", 'all').set("count", countAll);
-    categories.findProperty("value", 'pending').set("count", countPending);
-    categories.findProperty("value", 'in_progress').set("count", countInProgress);
-    categories.findProperty("value", 'failed').set("count", countFailed);
-    categories.findProperty("value", 'completed').set("count", countCompleted);
-    categories.findProperty("value", 'aborted').set("count", countAborted);
-    categories.findProperty("value", 'timedout').set("count", countTimedout);
+    if (!obj) {
+      return;
+    }
+    var groupedByStatus = dataUtils.groupPropertyValues(obj, 'status');
+
+    categories.findProperty("value", 'all').set("count", obj.length);
+    categories.findProperty("value", 'pending').set("count", Em.getWithDefault(groupedByStatus, 'pending.length', 0) + Em.getWithDefault(groupedByStatus, 'queued.length', 0));
+    categories.findProperty("value", 'in_progress').set("count", Em.getWithDefault(groupedByStatus, 'in_progress.length', 0));
+    categories.findProperty("value", 'failed').set("count", Em.getWithDefault(groupedByStatus, 'failed.length', 0));
+    categories.findProperty("value", 'completed').set("count", Em.getWithDefault(groupedByStatus, 'success.length', 0) + Em.getWithDefault(groupedByStatus, 'completed.length', 0));
+    categories.findProperty("value", 'aborted').set("count", Em.getWithDefault(groupedByStatus, 'aborted.length', 0));
+    categories.findProperty("value", 'timedout').set("count", Em.getWithDefault(groupedByStatus, 'timedout.length', 0));
   },
 
   /**
    * For Background operation popup calculate number of running Operations, and set popup header
+   *
    * @param {bool} isServiceListHidden
+   * @method setBackgroundOperationHeader
    */
   setBackgroundOperationHeader: function (isServiceListHidden) {
     if (this.get('isBackgroundOperations') && !isServiceListHidden) {
-      var numRunning =  App.router.get('backgroundOperationsController.allOperationsCount');
+      var numRunning = App.router.get('backgroundOperationsController.allOperationsCount');
       this.set("popupHeaderName", numRunning + Em.I18n.t('hostPopup.header.postFix').format(numRunning == 1 ? "" : "s"));
     }
   },
 
-  // map to get css class with styles by service status
-  statusesStyleMap: {
-    'FAILED': ['FAILED', 'icon-exclamation-sign', 'progress-danger', false],
-    'ABORTED': ['ABORTED', 'icon-minus', 'progress-warning', false],
-    'TIMEDOUT': ['TIMEDOUT', 'icon-time', 'progress-warning', false],
-    'IN_PROGRESS': ['IN_PROGRESS', 'icon-cogs', 'progress-info', true],
-    'COMPLETED': ['SUCCESS', 'icon-ok', 'progress-success', false]
-  },
-
   /**
    * Create services obj data structure for popup
    * Set data for services
+   *
    * @param {bool} isServiceListHidden
+   * @method onServiceUpdate
    */
   onServiceUpdate: function (isServiceListHidden) {
     if (this.get('isBackgroundOperations') && this.get("inputData")) {
-      var statuses = this.get('statusesStyleMap');
       var servicesInfo = this.get("servicesInfo");
       var currentServices = [];
       this.get("inputData").forEach(function (service, index) {
@@ -360,7 +445,8 @@ App.HostPopup = Em.Object.create({
         updatedService = existedService;
         if (existedService) {
           updatedService = this.updateService(existedService, service);
-        } else {
+        }
+        else {
           updatedService = this.createService(service);
           servicesInfo.insertAt(index, updatedService);
         }
@@ -373,13 +459,16 @@ App.HostPopup = Em.Object.create({
 
   /**
    * Create service object from transmitted data
-   * @param service
+   *
+   * @param {object} service
+   * @return {wrappedService}
+   * @method createService
    */
   createService: function (service) {
     var statuses = this.get('statusesStyleMap');
     var pendingStatus = ['PENDING', 'icon-cog', 'progress-info', true];
     var status = statuses[service.status] || pendingStatus;
-    return Ember.Object.create({
+    return Em.Object.create({
       id: service.id,
       displayName: service.displayName,
       progress: service.progress,
@@ -400,9 +489,11 @@ App.HostPopup = Em.Object.create({
 
   /**
    * Update properties of existed service with new data
-   * @param service
-   * @param newData
-   * @returns {Ember.Object}
+   *
+   * @param {wrappedService} service
+   * @param {object} newData
+   * @returns {wrappedService}
+   * @method updateService
    */
   updateService: function (service, newData) {
     var statuses = this.get('statusesStyleMap');
@@ -426,8 +517,10 @@ App.HostPopup = Em.Object.create({
   /**
    * remove old requests
    * as API returns 10, or  20 , or 30 ...etc latest request, the requests that absent in response should be removed
-   * @param services
-   * @param currentServicesIds
+   *
+   * @param {wrappedService[]} services
+   * @param {number[]} currentServicesIds
+   * @method removeOldServices
    */
   removeOldServices: function (services, currentServicesIds) {
     services.forEach(function (service, index, services) {
@@ -438,15 +531,17 @@ App.HostPopup = Em.Object.create({
   },
 
   /**
-   * create task Ember object
+   * Wrap task as Ember-object
+   *
    * @param {Object} _task
-   * @return {Em.Object}
+   * @return {wrappedTask}
+   * @method createTask
    */
   createTask: function (_task) {
     return Em.Object.create({
       id: _task.Tasks.id,
       hostName: _task.Tasks.host_name,
-      command: ( _task.Tasks.command.toLowerCase() != 'service_check') ? _task.Tasks.command.toLowerCase() : '',
+      command: _task.Tasks.command.toLowerCase() == 'service_check' ? '' : _task.Tasks.command.toLowerCase(),
       commandDetail: App.format.commandDetail(_task.Tasks.command_detail, _task.Tasks.request_inputs),
       status: App.format.taskStatus(_task.Tasks.status),
       role: App.format.role(_task.Tasks.role),
@@ -474,172 +569,243 @@ App.HostPopup = Em.Object.create({
   /**
    * Create hosts and tasks data structure for popup
    * Set data for hosts and tasks
+   *
+   * @method onHostUpdate
    */
   onHostUpdate: function () {
+    var inputData = this.get('inputData');
     var self = this;
-    var inputData = this.get("inputData");
-    if (inputData) {
-      var hostsArr = [];
-      var hostsData;
-      var hostsMap = {};
-
-      if(this.get('isBackgroundOperations') && this.get("currentServiceId")){
-        //hosts popup for Background Operations
-        hostsData = inputData.findProperty("id", this.get("currentServiceId"));
-      } else if (this.get("serviceName")) {
+    if (this.get("inputData")) {
+      var hostsMap = this._getHostsMap();
+      var existedHosts = self.get('hosts');
+
+      if (existedHosts && existedHosts.length && this.get('currentServiceId') === this.get('previousServiceId')) {
+        this._processingExistingHostsWithSameService(hostsMap);
+      }
+      else {
+        var hostsArr = this._hostMapProcessing(hostsMap);
+        hostsArr = hostsArr.sortProperty('name');
+        hostsArr.setEach("serviceName", this.get("serviceName"));
+        self.set("hosts", hostsArr);
+        self.set('previousServiceId', this.get('currentServiceId'));
+      }
+    }
+    var operation = this.get('servicesInfo').findProperty('name', this.get('serviceName'));
+    this.set('operationInfo', !operation || (operation && operation.get('progress') == 100) ? null : operation);
+  },
+
+  /**
+   * Generate hosts map for further processing <code>inputData</code>
+   *
+   * @returns {object}
+   * @private
+   * @method _getHostsMap
+   */
+  _getHostsMap: function () {
+    var hostsData;
+    var hostsMap = {};
+    var inputData = this.get('inputData');
+    if (this.get('isBackgroundOperations') && this.get("currentServiceId")) {
+      //hosts popup for Background Operations
+      hostsData = inputData.findProperty("id", this.get("currentServiceId"));
+    }
+    else {
+      if (this.get("serviceName")) {
         //hosts popup for Wizards
         hostsData = inputData.findProperty("name", this.get("serviceName"));
       }
-      if (hostsData) {
-        if (hostsData.hostsMap) {
-          //hosts data come from Background Operations as object map
-          hostsMap = hostsData.hostsMap;
-        } else if (hostsData.hosts) {
+    }
+
+    if (hostsData) {
+      if (hostsData.hostsMap) {
+        //hosts data come from Background Operations as object map
+        hostsMap = hostsData.hostsMap;
+      }
+      else {
+        if (hostsData.hosts) {
           //hosts data come from Wizard as array
-          hostsData.hosts.forEach(function (_host) {
-            hostsMap[_host.name] = _host;
-          });
+          hostsMap = hostsData.hosts.toMapByProperty('name');
         }
       }
-      var existedHosts = self.get('hosts');
+    }
+    return hostsMap;
+  },
 
-      if (existedHosts && (existedHosts.length > 0) && this.get('currentServiceId') === this.get('previousServiceId')) {
-        existedHosts.forEach(function (host) {
-          var newHostInfo = hostsMap[host.get('name')];
-          //update only hosts with changed tasks or currently opened tasks of host
-          if (newHostInfo && (!this.get('isBackgroundOperations') || newHostInfo.isModified || this.get('currentHostName') === host.get('name'))) {
-            var hostStatus = self.getStatus(newHostInfo.logTasks);
-            var hostProgress = self.getProgress(newHostInfo.logTasks);
-            host.set('status', App.format.taskStatus(hostStatus[0]));
-            host.set('icon', hostStatus[1]);
-            host.set('barColor', hostStatus[2]);
-            host.set('isInProgress', hostStatus[3]);
-            host.set('progress', hostProgress);
-            host.set('barWidth', "width:" + hostProgress + "%;");
-            host.set('logTasks', newHostInfo.logTasks);
-            var existTasks = host.get('tasks');
-            if (existTasks) {
-              newHostInfo.logTasks.forEach(function (_task) {
-                var existTask = existTasks.findProperty('id', _task.Tasks.id);
-                if (existTask) {
-                  var status = _task.Tasks.status;
-                  existTask.set('status', App.format.taskStatus(status));
-                  Em.keys(this.get('detailedProperties')).forEach(function (key) {
-                    var value = _task.Tasks[this.get('detailedProperties')[key]];
-                    if (!Em.isNone(value)) {
-                      existTask.set(key, value);
-                    }
-                  }, this);
-                  existTask.set('startTime', date.startTime(_task.Tasks.start_time));
-                  existTask.set('duration', date.durationSummary(_task.Tasks.start_time, _task.Tasks.end_time));
-                  // Puts some command information to render it 
-                  var isRebalanceHDFSTask = (_task.Tasks.command === 'CUSTOM_COMMAND' && _task.Tasks.custom_command_name === 'REBALANCEHDFS');
-                  existTask.set('isRebalanceHDFSTask', isRebalanceHDFSTask);
-                  if(isRebalanceHDFSTask){
-                    var structuredOut = _task.Tasks.structured_out;
-                    if (!structuredOut || structuredOut === 'null') {
-                      structuredOut = {};
-                    }
-
-                    var barColorMap = {
-                      'FAILED': 'progress-danger',
-                      'ABORTED': 'progress-warning',
-                      'TIMEDOUT': 'progress-warning',
-                      'IN_PROGRESS': 'progress-info',
-                      'COMPLETED': 'progress-success'
-                    };
-
-                    existTask.set('dataMoved', structuredOut['dataMoved'] || '0');
-                    existTask.set('dataLeft', structuredOut['dataLeft'] || '0');
-                    existTask.set('dataBeingMoved', structuredOut['dataBeingMoved'] || '0');
-                    existTask.set('barColor', barColorMap[status]);
-                    existTask.set('isInProgress', status == 'IN_PROGRESS');
-                    existTask.set('isNotComplete', ['QUEUED', 'IN_PROGRESS'].contains(status));
-                    existTask.set('completionProgressStyle', 'width:' + (structuredOut['completePercent'] || 0) * 100 + '%;');
-
-                    existTask.set('command', _task.Tasks.command);
-                    existTask.set('custom_command_name', _task.Tasks.custom_command_name);
-                  }
-                } else {
-                  existTasks.pushObject(this.createTask(_task));
+  /**
+   *
+   * @param {object} hostsMap
+   * @returns {wrappedHost[]}
+   * @private
+   * @method _hostMapProcessing
+   */
+  _hostMapProcessing: function (hostsMap) {
+    var self = this;
+    var hostsArr = [];
+    for (var hostName in hostsMap) {
+      if (!hostsMap.hasOwnProperty(hostName)) {
+        continue;
+      }
+      var _host = hostsMap[hostName];
+      var tasks = _host.logTasks;
+      var hostInfo = Em.Object.create({
+        name: hostName,
+        publicName: _host.publicName,
+        displayName: function () {
+          return this.get('name').length < 43 ? this.get('name') : (this.get('name').substr(0, 40) + '...');
+        }.property('name'),
+        progress: 0,
+        status: App.format.taskStatus("PENDING"),
+        serviceName: _host.serviceName,
+        isVisible: true,
+        icon: "icon-cog",
+        barColor: "progress-info",
+        barWidth: "width:0%;"
+      });
+
+      if (tasks.length) {
+        tasks = tasks.sortProperty('Tasks.id');
+        var hostStatus = self.getStatus(tasks);
+        var hostProgress = self.getProgress(tasks);
+        hostInfo.setProperties({
+          status: App.format.taskStatus(hostStatus[0]),
+          icon: hostStatus[1],
+          barColor: hostStatus[2],
+          isInProgress: hostStatus[3],
+          progress: hostProgress,
+          barWidth: "width:" + hostProgress + "%;"
+        });
+      }
+      hostInfo.set('logTasks', tasks);
+      hostsArr.push(hostInfo);
+    }
+    return hostsArr;
+  },
+
+  /**
+   *
+   * @param {object} hostsMap
+   * @private
+   * @method _processingExistingHostsWithSameService
+   */
+  _processingExistingHostsWithSameService: function (hostsMap) {
+    var self = this;
+    var barColorMap = this.get('barColorMap');
+    var existedHosts = self.get('hosts');
+    var detailedProperties = this.get('detailedProperties');
+    var detailedPropertiesKeys = Em.keys(detailedProperties);
+    existedHosts.forEach(function (host) {
+      var newHostInfo = hostsMap[host.get('name')];
+      //update only hosts with changed tasks or currently opened tasks of host
+      var hostShouldBeUpdated = !this.get('isBackgroundOperations') || newHostInfo.isModified || this.get('currentHostName') === host.get('name');
+      if (newHostInfo && hostShouldBeUpdated) {
+        var hostStatus = self.getStatus(newHostInfo.logTasks);
+        var hostProgress = self.getProgress(newHostInfo.logTasks);
+        host.setProperties({
+          status: App.format.taskStatus(hostStatus[0]),
+          icon: hostStatus[1],
+          barColor: hostStatus[2],
+          isInProgress: hostStatus[3],
+          progress: hostProgress,
+          barWidth: "width:" + hostProgress + "%;",
+          logTasks: newHostInfo.logTasks
+        });
+        var existTasks = host.get('tasks');
+        if (existTasks) {
+          newHostInfo.logTasks.forEach(function (_task) {
+            var existTask = existTasks.findProperty('id', _task.Tasks.id);
+            if (existTask) {
+              var status = _task.Tasks.status;
+              detailedPropertiesKeys.forEach(function (key) {
+                var name = detailedProperties[key];
+                var value = _task.Tasks[name];
+                if (!Em.isNone(value)) {
+                  existTask.set(key, value);
                 }
               }, this);
+              existTask.setProperties({
+                status: App.format.taskStatus(status),
+                startTime: date.startTime(_task.Tasks.start_time),
+                duration: date.durationSummary(_task.Tasks.start_time, _task.Tasks.end_time)
+              });
+              existTask = self._handleRebalanceHDFS(_task, existTask);
             }
-          }
-        }, this);
-      } else {
-        for (var hostName in hostsMap) {
-          var _host = hostsMap[hostName];
-          var tasks = _host.logTasks;
-          var hostInfo = Ember.Object.create({
-            name: hostName,
-            publicName: _host.publicName,
-            displayName: function () {
-              return this.get('name').length < 43 ? this.get('name') : (this.get('name').substr(0, 40) + '...');
-            }.property('name'),
-            progress: 0,
-            status: App.format.taskStatus("PENDING"),
-            serviceName: _host.serviceName,
-            isVisible: true,
-            icon: "icon-cog",
-            barColor: "progress-info",
-            barWidth: "width:0%;"
-          });
-
-          if (tasks.length) {
-            tasks = tasks.sortProperty('Tasks.id');
-            var hostStatus = self.getStatus(tasks);
-            var hostProgress = self.getProgress(tasks);
-            hostInfo.set('status', App.format.taskStatus(hostStatus[0]));
-            hostInfo.set('icon', hostStatus[1]);
-            hostInfo.set('barColor', hostStatus[2]);
-            hostInfo.set('isInProgress', hostStatus[3]);
-            hostInfo.set('progress', hostProgress);
-            hostInfo.set('barWidth', "width:" + hostProgress + "%;");
-          }
-          hostInfo.set('logTasks', tasks);
-          hostsArr.push(hostInfo);
+            else {
+              existTasks.pushObject(this.createTask(_task));
+            }
+          }, this);
         }
-
-        hostsArr = hostsArr.sortProperty('name');
-        hostsArr.setEach("serviceName", this.get("serviceName"));
-        self.set("hosts", hostsArr);
-        self.set('previousServiceId', this.get('currentServiceId'));
       }
-    }
+    }, this);
+  },
 
-    var operation = this.get('servicesInfo').findProperty('name', this.get('serviceName'));
-    if (!operation || (operation && operation.get('progress') == 100)) {
-      this.set('operationInfo', null);
-    } else {
-      this.set('operationInfo', operation);
+  /**
+   * Custom processing for "Rebalance HDFS"-task
+   *
+   * @param {object} task
+   * @param {object} existTask
+   * @returns {object}
+   * @private
+   * @method _handleRebalanceHDFS
+   */
+  _handleRebalanceHDFS: function (task, existTask) {
+    var barColorMap = this.get('barColorMap');
+    var isRebalanceHDFSTask = task.Tasks.command === 'CUSTOM_COMMAND' && task.Tasks.custom_command_name === 'REBALANCEHDFS';
+    existTask.set('isRebalanceHDFSTask', isRebalanceHDFSTask);
+    if (isRebalanceHDFSTask) {
+      var structuredOut = task.Tasks.structured_out || {};
+      var status = task.Tasks.status;
+      existTask.setProperties({
+        dataMoved: structuredOut['dataMoved'] || '0',
+        dataLeft: structuredOut['dataLeft'] || '0',
+        dataBeingMoved: structuredOut['dataBeingMoved'] || '0',
+        barColor: barColorMap[status],
+        isInProgress: status == 'IN_PROGRESS',
+        isNotComplete: ['QUEUED', 'IN_PROGRESS'].contains(status),
+        completionProgressStyle: 'width:' + (structuredOut['completePercent'] || 0) * 100 + '%;',
+        command: task.Tasks.command,
+        custom_command_name: task.Tasks.custom_command_name
+      });
     }
+    return existTask;
   },
 
   /**
    * Show popup
+   *
    * @return {App.ModalPopup} PopupObject For testing purposes
    */
   createPopup: function () {
     var self = this;
     var servicesInfo = this.get("servicesInfo");
     var isBackgroundOperations = this.get('isBackgroundOperations');
-    var categoryObject = Em.Object.extend({
-      value: '',
-      count: 0,
-      labelPath: '',
-      label: function(){
-        return Em.I18n.t(this.get('labelPath')).format(this.get('count'));
-      }.property('count')
-    });
-    self.set('isPopup', App.ModalPopup.show({
 
+    this.set('isPopup', App.ModalPopup.show({
+
+      /**
+       * @type {boolean}
+       */
       isLogWrapHidden: true,
+
+      /**
+       * @type {boolean}
+       */
       isTaskListHidden: true,
+
+      /**
+       * @type {boolean}
+       */
       isHostListHidden: true,
+
+      /**
+       * @type {boolean}
+       */
       isServiceListHidden: false,
 
+      /**
+       * @type {boolean}
+       */
       isHideBodyScroll: true,
+
       /**
        * no need to track is it loaded when popup contain only list of hosts
        * @type {bool}
@@ -652,9 +818,12 @@ App.HostPopup = Em.Object.create({
        */
       isOpen: false,
 
+      /**
+       * @type {object}
+       */
       detailedProperties: self.get('detailedProperties'),
 
-      didInsertElement: function(){
+      didInsertElement: function () {
         this._super();
         this.set('isOpen', true);
       },
@@ -664,10 +833,10 @@ App.HostPopup = Em.Object.create({
        */
       headerClass: Em.View.extend({
         controller: this,
-        template: Ember.Handlebars.compile('{{popupHeaderName}} ' +
-            '{{#unless view.parentView.isHostListHidden}}{{#if controller.operationInfo.isAbortable}}' +
-            '{{view controller.abortIcon servicesInfoBinding="controller.operationInfo"}}' +
-            '{{/if}}{{/unless}}')
+        template: Em.Handlebars.compile('{{popupHeaderName}} ' +
+          '{{#unless view.parentView.isHostListHidden}}{{#if controller.operationInfo.isAbortable}}' +
+          '{{view controller.abortIcon servicesInfoBinding="controller.operationInfo"}}' +
+          '{{/if}}{{/unless}}')
       }),
 
       /**
@@ -677,18 +846,22 @@ App.HostPopup = Em.Object.create({
 
       /**
        * for the checkbox: do not show this dialog again
+       *
        * @type {bool}
        */
       hasFooterCheckbox: true,
 
       /**
        * Auto-display BG-popup
+       *
        * @type {bool}
        */
-      isNotShowBgChecked : null,
+      isNotShowBgChecked: null,
 
       /**
        * Save user pref about auto-display BG-popup
+       *
+       * @method updateNotShowBgChecked
        */
       updateNotShowBgChecked: function () {
         var curVal = !this.get('isNotShowBgChecked');
@@ -698,9 +871,13 @@ App.HostPopup = Em.Object.create({
       }.observes('isNotShowBgChecked'),
 
       autoHeight: false,
+
+      /**
+       * @method closeModelPopup
+       */
       closeModelPopup: function () {
         this.set('isOpen', false);
-        if(isBackgroundOperations){
+        if (isBackgroundOperations) {
           $(this.get('element')).detach();
           App.router.get('backgroundOperationsController').set('levelInfo.name', 'REQUESTS_LIST');
         } else {
@@ -708,513 +885,24 @@ App.HostPopup = Em.Object.create({
           self.set('isPopup', null);
         }
       },
+
       onPrimary: function () {
         this.closeModelPopup();
       },
+
       onClose: function () {
         this.closeModelPopup();
       },
-      secondary: null,
-
-      bodyClass: App.TableView.extend({
-        templateName: require('templates/common/host_progress_popup'),
-        showTextArea: false,
-        isServiceEmptyList: true,
-        isTasksEmptyList: true,
-        controller: this,
-        sourceRequestScheduleId: -1,
-        sourceRequestScheduleRunning: false,
-        sourceRequestScheduleAborted: false,
-        sourceRequestScheduleCommand: null,
-        hosts: self.get('hosts'),
-        services: self.get('servicesInfo'),
-        filterMap: {
-          pending: ["pending", "queued"],
-          in_progress: ["in_progress", "upgrading"],
-          failed: ["failed"],
-          completed: ["completed", "success"],
-          aborted: ["aborted"],
-          timedout: ["timedout"]
-        },
-
-        pagination: true,
-        isPaginate: false,
-        /**
-         * Select box, display names and values
-         */
-        categories: [
-          categoryObject.create({value: 'all', labelPath: 'hostPopup.status.category.all'}),
-          categoryObject.create({value: 'pending', labelPath: 'hostPopup.status.category.pending'}),
-          categoryObject.create({value: 'in_progress', labelPath: 'hostPopup.status.category.inProgress'}),
-          categoryObject.create({value: 'failed', labelPath: 'hostPopup.status.category.failed'}),
-          categoryObject.create({value: 'completed', labelPath: 'hostPopup.status.category.success'}),
-          categoryObject.create({value: 'aborted', labelPath: 'hostPopup.status.category.aborted'}),
-          categoryObject.create({value: 'timedout', labelPath: 'hostPopup.status.category.timedout'})
-        ],
-
-        /**
-         * Selected option is bound to this values
-         */
-        serviceCategory: null,
-        hostCategory: null,
-        taskCategory: null,
-        /**
-         * flag to indicate whether level data has already been loaded
-         * applied only to HOSTS_LIST and TASK_DETAILS levels, whereas async query used to obtain data
-         */
-        isLevelLoaded: true,
-        isHostEmptyList: function() {
-          return !this.get('pageContent.length');
-        }.property('pageContent.length'),
-
-        currentHost: function () {
-          return this.get('hosts') && this.get('hosts').findProperty('name', this.get('controller.currentHostName'));
-        }.property('controller.currentHostName'),
-
-        tasks: function () {
-          var currentHost = this.get('currentHost');
-          if (currentHost) {
-            return currentHost.get('tasks');
-          }
-          return [];
-        }.property('currentHost.tasks', 'currentHost.tasks.@each.status'),
-
-        willDestroyElement: function () {
-          if (this.get('controller.dataSourceController.name') == 'highAvailabilityProgressPopupController') {
-            this.set('controller.dataSourceController.isTaskPolling', false);
-          }
-        },
-
-        /**
-         * Preset values on init
-         */
-        setOnStart: function () {
-          this.set('serviceCategory', this.get('categories').findProperty('value','all'));
-          if (this.get("controller.isBackgroundOperations")) {
-            this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
-            this.updateHostInfo();
-          } else {
-            this.set("parentView.isHostListHidden", false);
-            this.set("parentView.isServiceListHidden", true);
-          }
-        },
-
-        /**
-         * force popup to show list of operations
-         */
-        resetState: function(){
-          if(this.get('parentView.isOpen')){
-            this.set('parentView.isLogWrapHidden', true);
-            this.set('parentView.isTaskListHidden', true);
-            this.set('parentView.isHostListHidden', true);
-            this.set('parentView.isServiceListHidden', false);
-            this.get("controller").setBackgroundOperationHeader(false);
-            this.setOnStart();
-          }
-        }.observes('parentView.isOpen'),
-
-        /**
-         * When popup is opened, and data after polling has changed, update this data in component
-         */
-        updateHostInfo: function () {
-          if(!this.get('parentView.isOpen')) return;
-          this.set('parentView.isLoaded', false);
-          this.get("controller").set("inputData", this.get("controller.dataSourceController.services"));
-          this.get("controller").onServiceUpdate(this.get('parentView.isServiceListHidden'));
-          this.get("controller").onHostUpdate();
-          this.set('parentView.isLoaded', true);
-          this.set("hosts", this.get("controller.hosts"));
-          this.set("services", this.get("controller.servicesInfo"));
-          this.set('isLevelLoaded', true);
-        }.observes("controller.dataSourceController.serviceTimestamp"),
-
-        /**
-         * Depending on service filter, set which services should be shown
-         */
-        visibleServices: function () {
-          if (this.get("services")) {
-            this.set("isServiceEmptyList", true);
-            if (this.get('serviceCategory.value')) {
-              var filter = this.get('serviceCategory.value');
-              var services = this.get('services');
-              this.set("isServiceEmptyList", this.setVisibility(filter, services));
-            }
-          }
-        }.observes('serviceCategory', 'services', 'services.@each.status'),
-
-        /**
-         * Depending on hosts filter, set which hosts should be shown
-         */
-        filter: function() {
-          var _this = this,
-              filter = this.get('hostCategory.value'),
-              hosts = this.get('hosts') || [];
-          if (!filter || !hosts.length) return;
-          if (filter === 'all') {
-            this.set('filteredContent', hosts);
-          } else {
-            this.set('filteredContent', hosts.filter(function(item) {
-              return _this.get('filterMap')[filter].contains(item.status);
-            }));
-          }
-        }.observes('hosts.length', 'hostCategory.value'),
-
-        /**
-         * Reset startIndex property back to 1 when filter type has been changed.
-         */
-        resetIndex: function() {
-          if (this.get('hostCategory.value')) this.set('startIndex', 1)
-        }.observes('hostCategory.value'),
-
-        /**
-         * Depending on tasks filter, set which tasks should be shown
-         */
-        visibleTasks: function () {
-          this.set("isTasksEmptyList", true);
-          if (this.get('taskCategory.value') && this.get('tasks')) {
-            var filter = this.get('taskCategory.value');
-            var tasks = this.get('tasks');
-            this.set("isTasksEmptyList", this.setVisibility(filter, tasks));
-          }
-        }.observes('taskCategory', 'tasks', 'tasks.@each.status'),
-
-        /**
-         * Depending on selected filter type, set object visibility value
-         * @param filter
-         * @param obj
-         * @return {bool} isEmptyList
-         */
-        setVisibility: function (filter, obj) {
-          var isEmptyList = true;
-          if (filter == "all") {
-            obj.setEach("isVisible", true);
-            isEmptyList = !(obj.length > 0);
-          } else {
-            obj.forEach(function(item){
-              item.set('isVisible', this.get('filterMap')[filter].contains(item.status));
-              isEmptyList = (isEmptyList) ? !item.get('isVisible') : false;
-            }, this)
-          }
-          return isEmptyList;
-        },
-
-        /**
-         * Depending on currently viewed tab, call setSelectCount function
-         */
-        updateSelectView: function () {
-          var isPaginate;
-          if (!this.get('parentView.isHostListHidden')) {
-            //since lazy loading used for hosts, we need to get hosts info directly from controller, that always contains entire array of data
-            this.get('controller').setSelectCount(this.get("controller.hosts"), this.get('categories'));
-            isPaginate = true;
-          } else if (!this.get('parentView.isTaskListHidden')) {
-            this.get('controller').setSelectCount(this.get("tasks"), this.get('categories'));
-          } else if (!this.get('parentView.isServiceListHidden')) {
-            this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
-          }
-          this.set('isPaginate', !!isPaginate);
-        }.observes('tasks.@each.status', 'hosts.@each.status', 'parentView.isTaskListHidden', 'parentView.isHostListHidden', 'services.length', 'services.@each.status'),
-
-        /**
-         * control data uploading, depending on which display level is showed
-         * @param levelName
-         */
-        switchLevel: function (levelName) {
-          var dataSourceController = this.get('controller.dataSourceController');
-          if (this.get("controller.isBackgroundOperations")) {
-            var levelInfo = dataSourceController.get('levelInfo');
-            levelInfo.set('taskId', this.get('openedTaskId'));
-            levelInfo.set('requestId', this.get('controller.currentServiceId'));
-            levelInfo.set('name', levelName);
-            if (levelName === 'HOSTS_LIST') {
-              this.set('isLevelLoaded', dataSourceController.requestMostRecent());
-              this.set('hostCategory', this.get('categories').findProperty('value','all'));
-            } else if (levelName === 'TASK_DETAILS') {
-              dataSourceController.requestMostRecent();
-              this.set('isLevelLoaded', false);
-            } else if (levelName === 'REQUESTS_LIST') {
-              this.set('serviceCategory', this.get('categories').findProperty('value','all'));
-              this.get('controller.hosts').clear();
-              dataSourceController.requestMostRecent();
-            } else {
-              this.set('taskCategory', this.get('categories').findProperty('value','all'));
-            }
-          } else if (dataSourceController.get('name') == 'highAvailabilityProgressPopupController') {
-            if (levelName === 'TASK_DETAILS') {
-              this.set('isLevelLoaded', false);
-              dataSourceController.startTaskPolling(this.get('openedTask.request_id'), this.get('openedTask.id'));
-              Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
-                dataSourceController.addObserver('taskInfo.' + this.get('parentView.detailedProperties')[key], this, 'updateTaskInfo');
-              }, this);
-            } else {
-              dataSourceController.stopTaskPolling();
-            }
-          }
-        },
-        updateTaskInfo: function () {
-          var dataSourceController = this.get('controller.dataSourceController');
-          var openedTask = this.get('openedTask');
-          if (openedTask && openedTask.get('id') == dataSourceController.get('taskInfo.id')) {
-            this.set('isLevelLoaded', true);
-            Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
-              openedTask.set(key, dataSourceController.get('taskInfo.' + key));
-            }, this);
-          }
-        },
-        /**
-         * Onclick handler for button <-Tasks
-         */
-        backToTaskList: function () {
-          this.destroyClipBoard();
-          this.set("openedTaskId", 0);
-          this.set("parentView.isLogWrapHidden", true);
-          this.set("parentView.isTaskListHidden", false);
-          this.switchLevel("TASKS_LIST");
-        },
-
-        /**
-         * Onclick handler for button <-Hosts
-         */
-        backToHostList: function () {
-          this.set("parentView.isHostListHidden", false);
-          this.set("parentView.isTaskListHidden", true);
-          this.get("controller").set("popupHeaderName", this.get("controller.serviceName"));
-          this.get("controller").set("operationInfo", this.get('controller.servicesInfo').findProperty('name', this.get('controller.serviceName')));
-          this.switchLevel("HOSTS_LIST");
-        },
-
-        /**
-         * Onclick handler for button <-Services
-         */
-        backToServiceList: function () {
-          this.get("controller").set("serviceName", "");
-          this.set("parentView.isHostListHidden", true);
-          this.set("parentView.isServiceListHidden", false);
-          this.set("parentView.isTaskListHidden", true);
-          this.set("parentView.isLogWrapHidden", true);
-          this.set("hosts", null);
-          this.get("controller").setBackgroundOperationHeader(false);
-          this.switchLevel("REQUESTS_LIST");
-        },
 
-        /**
-         * Onclick handler for Show more ..
-         */
-        requestMoreOperations: function () {
-          var BGOController = App.router.get('backgroundOperationsController');
-          var count = BGOController.get('operationsCount');
-          BGOController.set('operationsCount', (count + 10));
-          BGOController.requestMostRecent();
-        },
-
-        setShowMoreAvailable: function () {
-          if (this.get('parentView.isOpen')) {
-            this.set('isShowMore', App.router.get("backgroundOperationsController.isShowMoreAvailable"));
-          }
-        }.observes('parentView.isOpen', 'App.router.backgroundOperationsController.isShowMoreAvailable'),
-        isShowMore: true,
-
-        /**
-         * Onclick handler for selected Service
-         * @param {Object} event
-         */
-        gotoHosts: function (event) {
-          this.get("controller").set("serviceName", event.context.get("name"));
-          this.get("controller").set("currentServiceId", event.context.get("id"));
-          this.get("controller").set("currentHostName", null);
-          this.get("controller").onHostUpdate();
-          this.switchLevel("HOSTS_LIST");
-          var servicesInfo = this.get("controller.hosts");
-          this.set("controller.popupHeaderName", event.context.get("name"));
-          this.set("controller.operationInfo", event.context);
-
-          //apply lazy loading on cluster with more than 100 nodes
-          if (servicesInfo.length > 100) {
-            this.set('hosts', servicesInfo.slice(0, 50));
-          } else {
-            this.set('hosts', servicesInfo);
-          }
-          this.set("parentView.isServiceListHidden", true);
-          this.set("parentView.isHostListHidden", false);
-          this.set("parentView.isTaskListHidden", true);
-          $(".modal").scrollTop(0);
-          $(".modal-body").scrollTop(0);
-          if (servicesInfo.length > 100) {
-            Em.run.next(this, function(){
-              this.set('hosts', this.get('hosts').concat(servicesInfo.slice(50, servicesInfo.length)));
-            });
-          }
-          // Determine if source request schedule is present
-          this.set('sourceRequestScheduleId', event.context.get("sourceRequestScheduleId"));
-          this.set('sourceRequestScheduleCommand', event.context.get('contextCommand'));
-          this.refreshRequestScheduleInfo();
-        },
-
-        isRequestSchedule : function() {
-          var id = this.get('sourceRequestScheduleId');
-          return id != null && !isNaN(id) && id > -1;
-        }.property('sourceRequestScheduleId'),
-
-        refreshRequestScheduleInfo : function() {
-          var self = this;
-          var id = this.get('sourceRequestScheduleId');
-          batchUtils.getRequestSchedule(id, function(data) {
-            if (data != null && data.RequestSchedule != null &&
-                data.RequestSchedule.status != null) {
-              switch (data.RequestSchedule.status) {
-              case 'DISABLED':
-                self.set('sourceRequestScheduleRunning', false);
-                self.set('sourceRequestScheduleAborted', true);
-                break;
-              case 'COMPLETED':
-                self.set('sourceRequestScheduleRunning', false);
-                self.set('sourceRequestScheduleAborted', false);
-                break;
-              case 'SCHEDULED':
-                self.set('sourceRequestScheduleRunning', true);
-                self.set('sourceRequestScheduleAborted', false);
-                break;
-              }
-            } else {
-              self.set('sourceRequestScheduleRunning', false);
-              self.set('sourceRequestScheduleAborted', false);
-            }
-          }, function(xhr, textStatus, error, opt) {
-            self.set('sourceRequestScheduleRunning', false);
-            self.set('sourceRequestScheduleAborted', false);
-          });
-        }.observes('sourceRequestScheduleId'),
-
-        /**
-         * Attempts to abort the current request schedule
-         */
-        doAbortRequestSchedule: function(event){
-          var self = this;
-          var id = event.context;
-          batchUtils.doAbortRequestSchedule(id, function(){
-            self.refreshRequestScheduleInfo();
-          });
-        },
-
-        requestScheduleAbortLabel : function() {
-          var label = Em.I18n.t("common.abort");
-          var command = this.get('sourceRequestScheduleCommand');
-          if (command != null && "ROLLING-RESTART" == command) {
-            label = Em.I18n.t("hostPopup.bgop.abort.rollingRestart");
-          }
-          return label;
-        }.property('sourceRequestScheduleCommand'),
-
-        /**
-         * Onclick handler for selected Host
-         * @param {Object} event
-         */
-        gotoTasks: function (event) {
-          var tasksInfo = [];
-          event.context.logTasks.forEach(function (_task) {
-            tasksInfo.pushObject(this.get("controller").createTask(_task));
-          }, this);
-          if (tasksInfo.length) {
-            this.get("controller").set("popupHeaderName", event.context.publicName);
-            this.get("controller").set("currentHostName", event.context.publicName);
-          }
-          this.switchLevel("TASKS_LIST");
-          this.set('currentHost.tasks', tasksInfo);
-          this.set("parentView.isHostListHidden", true);
-          this.set("parentView.isTaskListHidden", false);
-          $(".modal").scrollTop(0);
-          $(".modal-body").scrollTop(0);
-        },
-        
-        stopRebalanceHDFS: function () {
-          var hostPopup = this;
-          return App.showConfirmationPopup(function () {
-          App.ajax.send({
-            name : 'cancel.background.operation',
-              sender : hostPopup,
-            data : {
-              requestId : hostPopup.get('controller.currentServiceId')
-            }
-          });
-            hostPopup.backToServiceList();
-          });
-        },
-        /**
-         * Onclick handler for selected Task
-         */
-        openTaskLogInDialog: function () {
-          if ($(".task-detail-log-clipboard").length > 0) {
-            this.destroyClipBoard();
-          }
-          var newWindow = window.open();
-          var newDocument = newWindow.document;
-          newDocument.write($(".task-detail-log-info").html());
-          newDocument.close();
-        },
-
-        openedTaskId: 0,
-
-        /**
-         * Return task detail info of opened task
-         */
-        openedTask: function () {
-          if (!(this.get('openedTaskId') && this.get('tasks'))) {
-            return Ember.Object.create();
-          }
-          return this.get('tasks').findProperty('id', this.get('openedTaskId'));
-        }.property('tasks', 'tasks.@each.stderr', 'tasks.@each.stdout', 'openedTaskId'),
-
-        /**
-         * Onclick event for show task detail info
-         * @param {Object} event
-         */
-        toggleTaskLog: function (event) {
-          var taskInfo = event.context;
-          this.set("parentView.isLogWrapHidden", false);
-          if ($(".task-detail-log-clipboard").length > 0) {
-            this.destroyClipBoard();
-          }
-          this.set("parentView.isHostListHidden", true);
-          this.set("parentView.isTaskListHidden", true);
-          this.set('openedTaskId', taskInfo.id);
-          this.switchLevel("TASK_DETAILS");
-          $(".modal").scrollTop(0);
-          $(".modal-body").scrollTop(0);
-        },
-
-        /**
-         * Onclick event for copy to clipboard button
-         */
-        textTrigger: function () {
-          $(".task-detail-log-clipboard").length > 0 ? this.destroyClipBoard() : this.createClipBoard();
-        },
-
-        /**
-         * Create Clip Board
-         */
-        createClipBoard: function () {
-          var logElement = $(".task-detail-log-maintext");
-          $(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
-          $(".task-detail-log-clipboard")
-            .html("stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
-            .css("display", "block")
-            .width(logElement.width())
-            .height(logElement.height())
-            .select();
-          logElement.css("display", "none")
-        },
-
-        /**
-         * Destroy Clip Board
-         */
-        destroyClipBoard: function () {
-          $(".task-detail-log-clipboard").remove();
-          $(".task-detail-log-maintext").css("display", "block");
-        }
+      secondary: null,
 
+      bodyClass: App.HostProgressPopupBodyView.extend({
+        controller: self
       })
+
     }));
-    return self.get('isPopup');
+
+    return this.get('isPopup');
   }
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/76dd478e/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index d6132e6..3a446bb 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -36,6 +36,7 @@ require('views/common/modal_popups/invalid_KDC_popup');
 require('views/common/modal_popups/dependent_configs_list_popup');
 require('views/common/modal_popups/select_groups_popup');
 require('views/common/editable_list');
+require('views/common/host_progress_popup_body_view');
 require('views/common/rolling_restart_view');
 require('views/common/select_custom_date_view');
 require('views/common/metric');

http://git-wip-us.apache.org/repos/asf/ambari/blob/76dd478e/ambari-web/app/views/common/host_progress_popup_body_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/host_progress_popup_body_view.js b/ambari-web/app/views/common/host_progress_popup_body_view.js
new file mode 100644
index 0000000..86e1fc8
--- /dev/null
+++ b/ambari-web/app/views/common/host_progress_popup_body_view.js
@@ -0,0 +1,749 @@
+/**
+ * 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');
+var batchUtils = require('utils/batch_scheduled_requests');
+var date = require('utils/date/date');
+
+/**
+ * Option for "filter by state" dropdown
+ * @typedef {object} progressPopupCategoryObject
+ * @property {string} value "all|pending|in progress|failed|completed|aborted|timedout"
+ * @property {number} count number of items with <code>state</code> equal to <code>this.value</code>
+ * @property {string} labelPath key in the messages.js
+ * @property {string} label localized label
+ */
+var categoryObject = Em.Object.extend({
+  value: '',
+  count: 0,
+  labelPath: '',
+  label: function () {
+    return Em.I18n.t(this.get('labelPath')).format(this.get('count'));
+  }.property('count', 'labelPath')
+});
+
+/**
+ * @class HostProgressPopupBodyView
+ * @type {Em.View}
+ */
+App.HostProgressPopupBodyView = App.TableView.extend({
+
+  templateName: require('templates/common/host_progress_popup'),
+
+  /**
+   * @type {boolean}
+   */
+  showTextArea: false,
+
+  /**
+   * @type {boolean}
+   */
+  isServiceEmptyList: true,
+
+  /**
+   * @type {boolean}
+   */
+  isTasksEmptyList: true,
+
+  /**
+   * @type {number}
+   */
+  sourceRequestScheduleId: -1,
+
+  /**
+   * @type {boolean}
+   */
+  sourceRequestScheduleRunning: false,
+
+  /**
+   * @type {boolean}
+   */
+  sourceRequestScheduleAborted: false,
+
+  /**
+   * @type {?string}
+   */
+  sourceRequestScheduleCommand: null,
+
+  /**
+   * Alias for <code>controller.hosts</code>
+   *
+   * @type {wrappedHost[]}
+   */
+  hosts: function () {
+    return this.get('controller.hosts')
+  }.property('controller.hosts.[]'),
+
+  /**
+   * Alias for <code>controller.servicesInfo</code>
+   *
+   * @type {wrappedService[]}
+   */
+  services: function () {
+    return this.get('controller.servicesInfo');
+  }.property('controller.servicesInfo.[]'),
+
+  /**
+   * @type {number}
+   */
+  openedTaskId: 0,
+
+  /**
+   * Return task detail info of opened task
+   *
+   * @type {wrappedTask}
+   */
+  openedTask: function () {
+    if (!(this.get('openedTaskId') && this.get('tasks'))) {
+      return Em.Object.create();
+    }
+    return this.get('tasks').findProperty('id', this.get('openedTaskId'));
+  }.property('tasks', 'tasks.@each.stderr', 'tasks.@each.stdout', 'openedTaskId'),
+
+  /**
+   * @type {object}
+   */
+  filterMap: {
+    pending: ["pending", "queued"],
+    in_progress: ["in_progress", "upgrading"],
+    failed: ["failed"],
+    completed: ["completed", "success"],
+    aborted: ["aborted"],
+    timedout: ["timedout"]
+  },
+
+  /**
+   * Determines if "Show More ..."-link should be shown
+   * @type {boolean}
+   */
+  isShowMore: true,
+
+  /**
+   * @type {boolean}
+   */
+  pagination: true,
+
+  /**
+   * @type {boolean}
+   */
+  isPaginate: false,
+
+  /**
+   * Select box, display names and values
+   *
+   * @type {progressPopupCategoryObject[]}
+   */
+  categories: [
+    categoryObject.create({value: 'all', labelPath: 'hostPopup.status.category.all'}),
+    categoryObject.create({value: 'pending', labelPath: 'hostPopup.status.category.pending'}),
+    categoryObject.create({value: 'in_progress', labelPath: 'hostPopup.status.category.inProgress'}),
+    categoryObject.create({value: 'failed', labelPath: 'hostPopup.status.category.failed'}),
+    categoryObject.create({value: 'completed', labelPath: 'hostPopup.status.category.success'}),
+    categoryObject.create({value: 'aborted', labelPath: 'hostPopup.status.category.aborted'}),
+    categoryObject.create({value: 'timedout', labelPath: 'hostPopup.status.category.timedout'})
+  ],
+
+  /**
+   * Selected option is bound to this values
+   * @type {?progressPopupCategoryObject}
+   */
+  serviceCategory: null,
+
+  /**
+   * @type {?progressPopupCategoryObject}
+   */
+  hostCategory: null,
+
+  /**
+   * @type {?progressPopupCategoryObject}
+   */
+  taskCategory: null,
+
+  /**
+   * flag to indicate whether level data has already been loaded
+   * applied only to HOSTS_LIST and TASK_DETAILS levels, whereas async query used to obtain data
+   *
+   * @type {boolean}
+   */
+  isLevelLoaded: true,
+
+  /**
+   * <code>switchLevel</code> for some <code>controller.dataSourceController</code> should be customized
+   * So, this map contains information about customize-methods
+   * Format: key - <code>dataSourceController.name</code>, value - method name in this view
+   * Method is called with same arguments as <code>switchLevel</code> is
+   *
+   * @type {object}
+   */
+  customControllersSwitchLevelMap: {
+    highAvailabilityProgressPopupController: '_switchLevelForHAProgressPopupController'
+  },
+
+  /**
+   * @type {boolean}
+   */
+  isHostEmptyList: function () {
+    return !this.get('pageContent.length');
+  }.property('pageContent.length'),
+
+  /**
+   * @type {wrappedHost}
+   */
+  currentHost: function () {
+    return this.get('hosts') && this.get('hosts').findProperty('name', this.get('controller.currentHostName'));
+  }.property('controller.currentHostName'),
+
+  /**
+   * Tasks for current shown host (<code>currentHost</code>)
+   *
+   * @type {wrappedTask[]}
+   */
+  tasks: function () {
+    var currentHost = this.get('currentHost');
+    return currentHost ? currentHost.get('tasks') : [];
+  }.property('currentHost.tasks', 'currentHost.tasks.@each.status'),
+
+
+  /**
+   * Message about aborting operation
+   * Custom for Rolling Restart
+   *
+   * @type {string}
+   */
+  requestScheduleAbortLabel: function () {
+    return 'ROLLING-RESTART' == this.get('sourceRequestScheduleCommand') ?
+      Em.I18n.t("hostPopup.bgop.abort.rollingRestart"):
+      Em.I18n.t("common.abort");
+  }.property('sourceRequestScheduleCommand'),
+
+
+  willDestroyElement: function () {
+    if (this.get('controller.dataSourceController.name') == 'highAvailabilityProgressPopupController') {
+      this.set('controller.dataSourceController.isTaskPolling', false);
+    }
+  },
+
+  /**
+   * Preset values on init
+   *
+   * @method setOnStart
+   */
+  setOnStart: function () {
+    this.set('serviceCategory', this.get('categories').findProperty('value', 'all'));
+    if (this.get("controller.isBackgroundOperations")) {
+      this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
+      this.updateHostInfo();
+    }
+    else {
+      this.set("parentView.isHostListHidden", false);
+      this.set("parentView.isServiceListHidden", true);
+    }
+  },
+
+  /**
+   * force popup to show list of operations
+   *
+   * @method resetState
+   */
+  resetState: function () {
+    if (this.get('parentView.isOpen')) {
+      this.get('parentView').setProperties({
+        isLogWrapHidden: true,
+        isTaskListHidden: true,
+        isHostListHidden: true,
+        isServiceListHidden: false
+      });
+      this.get("controller").setBackgroundOperationHeader(false);
+      this.setOnStart();
+    }
+  }.observes('parentView.isOpen'),
+
+  /**
+   * When popup is opened, and data after polling has changed, update this data in component
+   *
+   * @method updateHostInfo
+   */
+  updateHostInfo: function () {
+    if (!this.get('parentView.isOpen')) {
+      return;
+    }
+    this.set('parentView.isLoaded', false);
+    this.get("controller").set("inputData", this.get("controller.dataSourceController.services"));
+    this.get("controller").onServiceUpdate(this.get('parentView.isServiceListHidden'));
+    this.get("controller").onHostUpdate();
+    this.set('parentView.isLoaded', true);
+    this.set("hosts", this.get("controller.hosts"));
+    this.set("services", this.get("controller.servicesInfo"));
+    this.set('isLevelLoaded', true);
+  }.observes("controller.dataSourceController.serviceTimestamp"),
+
+  /**
+   * Depending on service filter, set which services should be shown
+   *
+   * @method visibleServices
+   */
+  visibleServices: function () {
+    if (this.get("services")) {
+      this.set("isServiceEmptyList", true);
+      if (this.get('serviceCategory.value')) {
+        var filter = this.get('serviceCategory.value');
+        var services = this.get('services');
+        this.set("isServiceEmptyList", this.setVisibility(filter, services));
+      }
+    }
+  }.observes('serviceCategory', 'services', 'services.@each.status'),
+
+  /**
+   * Depending on hosts filter, set which hosts should be shown
+   *
+   * @method filter
+   */
+  filter: function () {
+    var filter = this.get('hostCategory.value');
+    var hosts = this.get('hosts') || [];
+    var filterMap = this.get('filterMap');
+    if (!filter || !hosts.length) {
+      return;
+    }
+    if (filter === 'all') {
+      this.set('filteredContent', hosts);
+    }
+    else {
+      this.set('filteredContent', hosts.filter(function (item) {
+        return filterMap[filter].contains(item.status);
+      }));
+    }
+  }.observes('hosts.length', 'hostCategory.value'),
+
+  /**
+   * Reset startIndex property back to 1 when filter type has been changed.
+   *
+   * @method resetIndex
+   */
+  resetIndex: function () {
+    if (this.get('hostCategory.value')) {
+      this.set('startIndex', 1);
+    }
+  }.observes('hostCategory.value'),
+
+  /**
+   * Depending on tasks filter, set which tasks should be shown
+   *
+   * @method visibleTasks
+   */
+  visibleTasks: function () {
+    this.set("isTasksEmptyList", true);
+    if (this.get('taskCategory.value') && this.get('tasks')) {
+      var filter = this.get('taskCategory.value');
+      var tasks = this.get('tasks');
+      this.set("isTasksEmptyList", this.setVisibility(filter, tasks));
+    }
+  }.observes('taskCategory', 'tasks', 'tasks.@each.status'),
+
+  /**
+   * Depending on selected filter type, set object visibility value
+   *
+   * @param filter
+   * @param obj
+   * @return {bool} isEmptyList
+   * @method setVisibility
+   */
+  setVisibility: function (filter, obj) {
+    var isEmptyList = true;
+    var filterMap = this.get('filterMap');
+    if (filter == "all") {
+      obj.setEach("isVisible", true);
+      isEmptyList = !obj.length;
+    }
+    else {
+      obj.forEach(function (item) {
+        item.set('isVisible', filterMap[filter].contains(item.status));
+        isEmptyList = (isEmptyList) ? !item.get('isVisible') : false;
+      }, this)
+    }
+    return isEmptyList;
+  },
+
+  /**
+   * Depending on currently viewed tab, call setSelectCount function
+   *
+   * @method updateSelectView
+   */
+  updateSelectView: function () {
+    var isPaginate;
+    if (this.get('parentView.isHostListHidden')) {
+      if (this.get('parentView.isTaskListHidden')) {
+        if (!this.get('parentView.isServiceListHidden')) {
+          this.get('controller').setSelectCount(this.get("services"), this.get('categories'));
+        }
+      }
+      else {
+        this.get('controller').setSelectCount(this.get("tasks"), this.get('categories'));
+      }
+    }
+    else {
+      //since lazy loading used for hosts, we need to get hosts info directly from controller, that always contains entire array of data
+      this.get('controller').setSelectCount(this.get("controller.hosts"), this.get('categories'));
+      isPaginate = true;
+
+    }
+    this.set('isPaginate', !!isPaginate);
+  }.observes('tasks.@each.status', 'hosts.@each.status', 'parentView.isTaskListHidden', 'parentView.isHostListHidden', 'services.length', 'services.@each.status'),
+
+  /**
+   * control data uploading, depending on which display level is showed
+   *
+   * @param {string} levelName
+   * @method switchLevel
+   */
+  switchLevel: function (levelName) {
+    var dataSourceController = this.get('controller.dataSourceController');
+    var args = [].slice.call(arguments);
+    if (this.get("controller.isBackgroundOperations")) {
+      var levelInfo = dataSourceController.get('levelInfo');
+      levelInfo.set('taskId', this.get('openedTaskId'));
+      levelInfo.set('requestId', this.get('controller.currentServiceId'));
+      levelInfo.set('name', levelName);
+      if (levelName === 'HOSTS_LIST') {
+        this.set('isLevelLoaded', dataSourceController.requestMostRecent());
+        this.set('hostCategory', this.get('categories').findProperty('value', 'all'));
+      }
+      else {
+        if (levelName === 'TASK_DETAILS') {
+          dataSourceController.requestMostRecent();
+          this.set('isLevelLoaded', false);
+        }
+        else {
+          if (levelName === 'REQUESTS_LIST') {
+            this.set('serviceCategory', this.get('categories').findProperty('value', 'all'));
+            this.get('controller.hosts').clear();
+            dataSourceController.requestMostRecent();
+          }
+          else {
+            this.set('taskCategory', this.get('categories').findProperty('value', 'all'));
+          }
+        }
+      }
+    }
+    else {
+      var customControllersSwitchLevelMap = this.get('customControllersSwitchLevelMap');
+      Em.tryInvoke(this, customControllersSwitchLevelMap[dataSourceController.get('name')], args);
+    }
+  },
+
+  /**
+   * Switch-level custom method for <code>highAvailabilityProgressPopupController</code>
+   *
+   * @param {string} levelName
+   * @private
+   */
+  _switchLevelForHAProgressPopupController: function (levelName) {
+    var dataSourceController = this.get('controller.dataSourceController');
+    if (levelName === 'TASK_DETAILS') {
+      this.set('isLevelLoaded', false);
+      dataSourceController.startTaskPolling(this.get('openedTask.request_id'), this.get('openedTask.id'));
+      Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
+        dataSourceController.addObserver('taskInfo.' + this.get('parentView.detailedProperties')[key], this, 'updateTaskInfo');
+      }, this);
+    }
+    else {
+      dataSourceController.stopTaskPolling();
+    }
+  },
+
+  /**
+   * @method updateTaskInfo
+   */
+  updateTaskInfo: function () {
+    var dataSourceController = this.get('controller.dataSourceController');
+    var openedTask = this.get('openedTask');
+    if (openedTask && openedTask.get('id') == dataSourceController.get('taskInfo.id')) {
+      this.set('isLevelLoaded', true);
+      Em.keys(this.get('parentView.detailedProperties')).forEach(function (key) {
+        openedTask.set(key, dataSourceController.get('taskInfo.' + key));
+      }, this);
+    }
+  },
+
+  /**
+   * Onclick handler for button <-Tasks
+   *
+   * @method backToTaskList
+   */
+  backToTaskList: function () {
+    this.destroyClipBoard();
+    this.set("openedTaskId", 0);
+    this.set("parentView.isLogWrapHidden", true);
+    this.set("parentView.isTaskListHidden", false);
+    this.switchLevel("TASKS_LIST");
+  },
+
+  /**
+   * Onclick handler for button <-Hosts
+   *
+   * @method backToHostList
+   */
+  backToHostList: function () {
+    this.set("parentView.isHostListHidden", false);
+    this.set("parentView.isTaskListHidden", true);
+    this.get("controller").set("popupHeaderName", this.get("controller.serviceName"));
+    this.get("controller").set("operationInfo", this.get('controller.servicesInfo').findProperty('name', this.get('controller.serviceName')));
+    this.switchLevel("HOSTS_LIST");
+  },
+
+  /**
+   * Onclick handler for button <-Services
+   *
+   * @method backToServiceList
+   */
+  backToServiceList: function () {
+    this.get("controller").set("serviceName", "");
+    this.set("parentView.isHostListHidden", true);
+    this.set("parentView.isServiceListHidden", false);
+    this.set("parentView.isTaskListHidden", true);
+    this.set("parentView.isLogWrapHidden", true);
+    this.set("hosts", null);
+    this.get("controller").setBackgroundOperationHeader(false);
+    this.switchLevel("REQUESTS_LIST");
+  },
+
+  /**
+   * Onclick handler for Show more ..
+   *
+   * @method requestMoreOperations
+   */
+  requestMoreOperations: function () {
+    var BGOController = App.router.get('backgroundOperationsController');
+    var count = BGOController.get('operationsCount');
+    BGOController.set('operationsCount', (count + 10));
+    BGOController.requestMostRecent();
+  },
+
+  /**
+   * @method setShowMoreAvailable
+   */
+  setShowMoreAvailable: function () {
+    if (this.get('parentView.isOpen')) {
+      this.set('isShowMore', App.router.get("backgroundOperationsController.isShowMoreAvailable"));
+    }
+  }.observes('parentView.isOpen', 'App.router.backgroundOperationsController.isShowMoreAvailable'),
+
+  /**
+   * Onclick handler for selected Service
+   *
+   * @param {{context: wrappedService}} event
+   * @method gotoHosts
+   */
+  gotoHosts: function (event) {
+    this.get("controller").set("serviceName", event.context.get("name"));
+    this.get("controller").set("currentServiceId", event.context.get("id"));
+    this.get("controller").set("currentHostName", null);
+    this.get("controller").onHostUpdate();
+    this.switchLevel("HOSTS_LIST");
+    var servicesInfo = this.get("controller.hosts");
+    this.set("controller.popupHeaderName", event.context.get("name"));
+    this.set("controller.operationInfo", event.context);
+
+    //apply lazy loading on cluster with more than 100 nodes
+    this.set('hosts', servicesInfo.length > 100 ? servicesInfo.slice(0, 50) : servicesInfo);
+    this.set("parentView.isServiceListHidden", true);
+    this.set("parentView.isHostListHidden", false);
+    this.set("parentView.isTaskListHidden", true);
+    $(".modal").scrollTop(0);
+    $(".modal-body").scrollTop(0);
+    if (servicesInfo.length > 100) {
+      Em.run.next(this, function () {
+        this.set('hosts', this.get('hosts').concat(servicesInfo.slice(50, servicesInfo.length)));
+      });
+    }
+    // Determine if source request schedule is present
+    this.set('sourceRequestScheduleId', event.context.get("sourceRequestScheduleId"));
+    this.set('sourceRequestScheduleCommand', event.context.get('contextCommand'));
+    this.refreshRequestScheduleInfo();
+  },
+
+  /**
+   * @type {boolean}
+   */
+  isRequestSchedule: function () {
+    var id = this.get('sourceRequestScheduleId');
+    return id != null && !isNaN(id) && id > -1;
+  }.property('sourceRequestScheduleId'),
+
+  /**
+   * @method refreshRequestScheduleInfo
+   */
+  refreshRequestScheduleInfo: function () {
+    var self = this;
+    var id = this.get('sourceRequestScheduleId');
+    batchUtils.getRequestSchedule(id, function (data) {
+      var status = Em.get(data || {}, 'RequestSchedule.status');
+      if (status) {
+        switch (status) {
+          case 'DISABLED':
+            self.set('sourceRequestScheduleRunning', false);
+            self.set('sourceRequestScheduleAborted', true);
+            break;
+          case 'COMPLETED':
+            self.set('sourceRequestScheduleRunning', false);
+            self.set('sourceRequestScheduleAborted', false);
+            break;
+          case 'SCHEDULED':
+            self.set('sourceRequestScheduleRunning', true);
+            self.set('sourceRequestScheduleAborted', false);
+            break;
+        }
+      }
+      else {
+        self.set('sourceRequestScheduleRunning', false);
+        self.set('sourceRequestScheduleAborted', false);
+      }
+    }, function () {
+      self.set('sourceRequestScheduleRunning', false);
+      self.set('sourceRequestScheduleAborted', false);
+    });
+  }.observes('sourceRequestScheduleId'),
+
+  /**
+   * Attempts to abort the current request schedule
+   *
+   * @param {{context: number}} event
+   * @method doAbortRequestSchedule
+   */
+  doAbortRequestSchedule: function (event) {
+    var self = this;
+    var id = event.context;
+    batchUtils.doAbortRequestSchedule(id, function () {
+      self.refreshRequestScheduleInfo();
+    });
+  },
+
+  /**
+   * Onclick handler for selected Host
+   *
+   * @param {{context: wrappedHost}} event
+   * @method gotoTasks
+   */
+  gotoTasks: function (event) {
+    var tasksInfo = [];
+    event.context.logTasks.forEach(function (_task) {
+      tasksInfo.pushObject(this.get("controller").createTask(_task));
+    }, this);
+    if (tasksInfo.length) {
+      this.get("controller").set("popupHeaderName", event.context.publicName);
+      this.get("controller").set("currentHostName", event.context.publicName);
+    }
+    this.switchLevel("TASKS_LIST");
+    this.set('currentHost.tasks', tasksInfo);
+    this.set("parentView.isHostListHidden", true);
+    this.set("parentView.isTaskListHidden", false);
+    $(".modal").scrollTop(0);
+    $(".modal-body").scrollTop(0);
+  },
+
+  /**
+   * @method stopRebalanceHDFS
+   * @returns {App.ModalPopup}
+   */
+  stopRebalanceHDFS: function () {
+    var hostPopup = this;
+    return App.showConfirmationPopup(function () {
+      App.ajax.send({
+        name: 'cancel.background.operation',
+        sender: hostPopup,
+        data: {
+          requestId: hostPopup.get('controller.currentServiceId')
+        }
+      });
+      hostPopup.backToServiceList();
+    });
+  },
+
+  /**
+   * Onclick handler for selected Task
+   *
+   * @method openTaskLogInDialog
+   */
+  openTaskLogInDialog: function () {
+    if ($(".task-detail-log-clipboard").length) {
+      this.destroyClipBoard();
+    }
+    var newWindow = window.open();
+    var newDocument = newWindow.document;
+    newDocument.write($(".task-detail-log-info").html());
+    newDocument.close();
+  },
+
+  /**
+   * Onclick event for show task detail info
+   *
+   * @param {{context: wrappedTask}} event
+   * @method toggleTaskLog
+   */
+  toggleTaskLog: function (event) {
+    var taskInfo = event.context;
+    this.set("parentView.isLogWrapHidden", false);
+    if ($(".task-detail-log-clipboard").length) {
+      this.destroyClipBoard();
+    }
+    this.set("parentView.isHostListHidden", true);
+    this.set("parentView.isTaskListHidden", true);
+    this.set('openedTaskId', taskInfo.id);
+    this.switchLevel("TASK_DETAILS");
+    $(".modal").scrollTop(0);
+    $(".modal-body").scrollTop(0);
+  },
+
+  /**
+   * Onclick event for copy to clipboard button
+   *
+   * @method textTrigger
+   */
+  textTrigger: function () {
+    $(".task-detail-log-clipboard").length ? this.destroyClipBoard() : this.createClipBoard();
+  },
+
+  /**
+   * Create Clip Board
+   *
+   * @method createClipBoard
+   */
+  createClipBoard: function () {
+    var logElement = $(".task-detail-log-maintext");
+    $(".task-detail-log-clipboard-wrap").html('<textarea class="task-detail-log-clipboard"></textarea>');
+    $(".task-detail-log-clipboard")
+      .html("stderr: \n" + $(".stderr").html() + "\n stdout:\n" + $(".stdout").html())
+      .css('display', 'block')
+      .width(logElement.width())
+      .height(logElement.height())
+      .select();
+
+    logElement.css("display", "none");
+  },
+
+  /**
+   * Destroy Clip Board
+   *
+   * @method destroyClipBoard
+   */
+  destroyClipBoard: function () {
+    $(".task-detail-log-clipboard").remove();
+    $(".task-detail-log-maintext").css("display", "block");
+  }
+
+});
\ No newline at end of file