You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by am...@apache.org on 2018/01/05 07:56:01 UTC

[05/45] ambari git commit: AMBARI-22687. Bulk host delete and component add & delete scenarios. (ishanbha)

AMBARI-22687. Bulk host delete and component add & delete scenarios. (ishanbha)


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

Branch: refs/heads/branch-feature-AMBARI-22008-isilon
Commit: 4a68e4e879ee418c61882c24f5511281740b94d1
Parents: 55f095a
Author: Ishan Bhatt <is...@gmail.com>
Authored: Fri Dec 22 11:21:32 2017 -0800
Committer: Ishan Bhatt <is...@gmail.com>
Committed: Fri Dec 22 11:21:32 2017 -0800

----------------------------------------------------------------------
 .../main/host/bulk_operations_controller.js     | 369 +++++++------------
 ambari-web/app/controllers/main/host/details.js |   4 +-
 ambari-web/app/messages.js                      |  43 ++-
 ambari-web/app/styles/application.less          |  40 ++
 .../main/host/bulk_add_delete_confirm_popup.hbs |  40 ++
 .../main/host/delete_hosts_dry_run_popup.hbs    |  32 --
 .../templates/main/host/delete_hosts_popup.hbs  |  21 +-
 .../main/host/delete_hosts_result_popup.hbs     |   4 +-
 ambari-web/app/utils/ajax/ajax.js               |   3 +-
 9 files changed, 260 insertions(+), 296 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/controllers/main/host/bulk_operations_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/bulk_operations_controller.js b/ambari-web/app/controllers/main/host/bulk_operations_controller.js
index 94894dc..86c0db4 100644
--- a/ambari-web/app/controllers/main/host/bulk_operations_controller.js
+++ b/ambari-web/app/controllers/main/host/bulk_operations_controller.js
@@ -65,7 +65,7 @@ App.BulkOperationsController = Em.Controller.extend({
           this.bulkOperationForHostsReinstall(operationData, hosts);
         }
         else if (operationData.action === 'DELETE'){
-          this.bulkOperationForHostsDeleteDryRun(operationData, hosts);
+          this._bulkOperationForHostsDelete(hosts);
         }
         else {
           if (operationData.action === 'PASSIVE_STATE') {
@@ -262,118 +262,83 @@ App.BulkOperationsController = Em.Controller.extend({
   },
 
   /**
-   * Calling dry_run for bulk delete selected hosts
-   * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
-   * @param {Ember.Enumerable} hosts - list of affected hosts
-   */
-  bulkOperationForHostsDeleteDryRun: function (operationData, hosts) {
-    var self = this;
-    App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
-      return App.ajax.send({
-        name: 'common.hosts.delete',
-        sender: self,
-        data: {
-          urlParams: "/?dry_run=true",
-          query: 'Hosts/host_name.in(' + hosts.mapProperty('hostName').join(',') + ')',
-          hosts: hosts.mapProperty('hostName')
-        },
-        success: 'bulkOperationForHostsDeleteDryRunCallback',
-        error: 'bulkOperationForHostsDeleteDryRunCallback',
-        showLoadingPopup: true
-      });
-    });
-  },
-
-  /**
-   * Show popup after dry_run for bulk delete hosts
-   * @method bulkOperationForHostsDeleteDryRunCallback
-   */
-  bulkOperationForHostsDeleteDryRunCallback: function (arg0, arg1, arg2, arg3, arg4) {
-    var self = this;
-    var deletableHosts = [];
-    var undeletableHosts = [];
-    if (arg1 == "error") {
-      var request = arg0;
-      var params = arg4;
-      var response = JSON.parse(request.responseText);
-      var host = Ember.Object.create({
+  * Check which hosts can be deleted and warn the user about it in advance
+  * @param {Ember.Enumerable} hosts - list of affected hosts
+  */
+  _bulkOperationForHostsDelete: function (hosts) {
+    var self = this,
+        hostNamesToDelete = [],
+        hostsNotToDelete = [];
+    var createNonDeletableComponents = function (hostName, message) {
+      return Em.Object.create({
         error: {
-          key: params.hosts[0],
-          code: response.status,
-          message: response.message
+          key: hostName,
+          message: message
         },
         isCollapsed: true,
         isBodyVisible: Em.computed.ifThenElse('isCollapsed', 'display: none;', 'display: block;')
       });
-      undeletableHosts.push(host);
-    } else {
-      var data = arg0;
-      var params = arg2;
-      if (data) {
-        data.deleteResult.forEach(function (host) {
-          if (host.deleted) {
-            deletableHosts.push(host);
-          } else {
-            var _host = Ember.Object.create({
-              error: host.error,
-              isCollapsed: true,
-              isBodyVisible: Em.computed.ifThenElse('isCollapsed', 'display: none;', 'display: block;')
-            });
-            undeletableHosts.push(_host);
-          }
-        });
+    };
+    hosts.forEach(function (host) {
+      var hostComponents = App.HostComponent.find().filterProperty('hostName', host.hostName);
+      var hostInfo = App.router.get('mainHostDetailsController').getHostComponentsInfo(hostComponents);
+      console.dir(hostInfo);
+      if (hostInfo.nonDeletableComponents.length > 0) {
+        hostsNotToDelete.push(createNonDeletableComponents(host.hostName, Em.I18n.t('hosts.bulkOperation.deleteHosts.nonDeletableComponents').format(hostInfo.nonDeletableComponents.join(", "))));
+      } else if (hostInfo.nonAddableMasterComponents.length > 0) {
+        hostsNotToDelete.push(createNonDeletableComponents(host.hostName, Em.I18n.t('hosts.bulkOperation.deleteHosts.nonAddableMasterComponents').format(hostInfo.nonAddableMasterComponents.join(", "))));
+      } else if (hostInfo.lastMasterComponents.length > 0) {
+        hostsNotToDelete.push(createNonDeletableComponents(host.hostName, Em.I18n.t('hosts.bulkOperation.deleteHosts.lastMasterComponents').format(hostInfo.lastMasterComponents.join(", "))));
+      } else if (hostInfo.runningComponents.length > 0) {
+        hostsNotToDelete.push(createNonDeletableComponents(host.hostName, Em.I18n.t('hosts.bulkOperation.deleteHosts.runningComponents').format(hostInfo.runningComponents.join(", "))));
       } else {
-        var host = {
-          deleted: {
-            key: params.hosts[0]
-          }
-        };
-        deletableHosts.push(host);
+        hostNamesToDelete.push(host.hostName);
       }
-    }
-
-    if (undeletableHosts.length) {
-      return App.ModalPopup.show({
-        header: Em.I18n.t('hosts.bulkOperation.deleteHosts.dryRun.header'),
-
-        primary: deletableHosts.length ? Em.I18n.t('hosts.bulkOperation.deleteHosts.dryRun.primary').format(deletableHosts.length) : null,
+    });
 
-        onPrimary: function () {
-          this._super();
-          self.bulkOperationForHostsDelete(deletableHosts);
-        },
-        bodyClass: Em.View.extend({
-          templateName: require('templates/main/host/delete_hosts_dry_run_popup'),
-          message: Em.I18n.t('hosts.bulkOperation.deleteHosts.dryRun.message').format(undeletableHosts.length),
-          undeletableHosts: undeletableHosts,
-          onToggleHost: function (host) {
-            host.contexts[0].toggleProperty('isCollapsed');
-          }
-        })
-      });
-    } else if (deletableHosts.length) {
-      this.bulkOperationForHostsDelete(deletableHosts);
-    }
+    return App.ModalPopup.show({
+      header: hostNamesToDelete.length ? Em.I18n.t('hosts.bulkOperation.deleteHosts.confirm.header') : Em.I18n.t('rolling.nothingToDo.header'),
+      primary: hostNamesToDelete.length ? Em.I18n.t('common.next') : null,
+      primaryClass: 'btn-default',
+      onPrimary: function () {
+        this._super();
+        self.bulkOperationForHostsDelete(hostNamesToDelete);
+      },
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/host/bulk_add_delete_confirm_popup'),
+        modifyMessage: Em.I18n.t('hosts.bulkOperation.deleteHosts.confirm.delete'),
+        skipMessage: hostNamesToDelete.length ? Em.I18n.t('hosts.bulkOperation.deleteHosts.cannot.delete1') : Em.I18n.t('hosts.bulkOperation.deleteHosts.cannot.delete2'),
+        skippedHosts: hostsNotToDelete.length ? hostsNotToDelete : null,
+        hostsToModify: hostNamesToDelete.length ? hostNamesToDelete.join("\n") : null,
+        onToggleHost: function (host) {
+          host.contexts[0].toggleProperty('isCollapsed');
+        }
+      })
+    });
   },
 
   /**
    * Bulk delete selected hosts
-   * @param {Ember.Enumerable} hosts - list of affected hosts
+   * @param {String} hosts - list of affected host names
    */
   bulkOperationForHostsDelete: function (hosts) {
-    var self = this;
+    var confirmKey = 'delete',
+        self = this;
     App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
       return App.ModalPopup.show({
         header: Em.I18n.t('hosts.bulkOperation.deleteHosts.confirmation.header'),
-
+        confirmInput: '',
+        disablePrimary: Em.computed.notEqual('confirmInput', confirmKey),
+        primary: Em.I18n.t('common.confirm'),
+        primaryClass: 'btn-warning',
         onPrimary: function () {
           this._super();
           return App.ajax.send({
             name: 'common.hosts.delete',
             sender: self,
             data: {
-              query: 'Hosts/host_name.in(' + hosts.mapProperty('deleted.key').join(',') + ')',
-              hosts: hosts.mapProperty('deleted.key')
+              query: 'Hosts/host_name.in(' + hosts.join(',') + ')',
+              hosts: hosts
             },
             success: 'bulkOperationForHostsDeleteCallback',
             error: 'bulkOperationForHostsDeleteCallback',
@@ -382,7 +347,8 @@ App.BulkOperationsController = Em.Controller.extend({
         },
         bodyClass: Em.View.extend({
           templateName: require('templates/main/host/delete_hosts_popup'),
-          hosts: hosts
+          hostNames: hosts,
+          typeMessage: Em.I18n.t('services.service.confirmDelete.popup.body.type').format(confirmKey),
         })
       });
     });
@@ -414,9 +380,7 @@ App.BulkOperationsController = Em.Controller.extend({
       var params = arg2;
       if (data) {
         data.deleteResult.forEach(function (host) {
-          if (host.deleted) {
-            deletedHosts.push(host);
-          } else {
+          if (!host.deleted) {
             var _host = Ember.Object.create({
               error: host.error,
               isCollapsed: true,
@@ -425,14 +389,8 @@ App.BulkOperationsController = Em.Controller.extend({
             undeletableHosts.push(_host);
           }
         });
-      } else {
-        var host = {
-          deleted: {
-            key: params.hosts[0]
-          }
-        };
-        deletedHosts.push(host);
       }
+      deletedHosts = params.hosts;
     }
 
     return App.ModalPopup.show({
@@ -444,7 +402,7 @@ App.BulkOperationsController = Em.Controller.extend({
         templateName: require('templates/main/host/delete_hosts_result_popup'),
         message: Em.I18n.t('hosts.bulkOperation.deleteHosts.dryRun.message').format(undeletableHosts.length),
         undeletableHosts: undeletableHosts,
-        deletedHosts: deletedHosts.sortProperty('deleted.key'),
+        deletedHosts: deletedHosts,
         onToggleHost: function (host) {
           host.contexts[0].toggleProperty('isCollapsed');
         }
@@ -542,70 +500,44 @@ App.BulkOperationsController = Em.Controller.extend({
   _getComponentsFromServerForHostComponentsAddCallback: function (operationData, data, hosts) {
     var self = this;
 
-    hosts = hosts.mapProperty('hostName');
-
     var allHostsWithComponent = data.items.mapProperty('Hosts.host_name');
-    var hostsWithComponent = hosts.filter(function (host) {
-      return allHostsWithComponent.contains(host);
+    var hostsWithComponent = [];
+    hosts.forEach(function (host) {
+      if(allHostsWithComponent.contains(host.hostName)) {
+        hostsWithComponent.push(Em.Object.create({
+          error: {
+            key: host.hostName,
+            message: Em.I18n.t('hosts.bulkOperation.confirmation.add.component.skip').format(operationData.componentNameFormatted)
+          },
+          isCollapsed: true,
+          isBodyVisible: Em.computed.ifThenElse('isCollapsed', 'display: none;', 'display: block;')
+        }));
+      }
     });
     var hostsWithOutComponent = hosts.filter(function(host) {
-      return !hostsWithComponent.contains(host);
+      return !hostsWithComponent.findProperty('error.key', host.hostName);
     });
 
-    var minShown = 3;
-
-    if (hostsWithOutComponent.length) {
-      return App.ModalPopup.show({
-        header: Em.I18n.t('hosts.bulkOperation.confirmation.header'),
-        hostNames: hostsWithOutComponent.join("\n"),
-        visibleHosts: self._showHostNames(hostsWithOutComponent, "\n", minShown),
-        hostNamesSkippedVisible: self._showHostNames(hostsWithComponent, "\n", minShown),
-        expanded: false,
-
-        hostNamesSkipped: function() {
-          return hostsWithComponent.length ? hostsWithComponent.join("\n") : false;
-        }.property(),
+    hostsWithOutComponent = hostsWithOutComponent.mapProperty('hostName');
 
-        didInsertElement: function() {
-          this._super();
-          this.set('expanded', hostsWithOutComponent.length <= minShown);
-        },
-
-        onPrimary: function() {
-          self.bulkAddHostComponents(operationData, hostsWithOutComponent);
-          this._super();
-        },
-        bodyClass: Em.View.extend({
-          templateName: require('templates/main/host/bulk_operation_confirm_popup'),
-          message: Em.I18n.t('hosts.bulkOperation.confirmation.add.component').format(operationData.message, operationData.componentNameFormatted, hostsWithOutComponent.length),
-          warningInfo: Em.I18n.t('hosts.bulkOperation.confirmation.add.component.skip').format(operationData.componentNameFormatted),
-          textareaVisible: false,
-          textTrigger: function() {
-            this.toggleProperty('textareaVisible');
-          },
-
-          showAll: function() {
-            this.set('parentView.visibleHosts', this.get('parentView.hostNames'));
-            this.set('parentView.hostNamesSkippedVisible', this.get('parentView.hostNamesSkipped'));
-            this.set('parentView.expanded', true);
-          },
-          putHostNamesToTextarea: function() {
-            var hostNames = this.get('parentView.hostNames');
-            if (this.get('textareaVisible')) {
-              var wrapper = $(".task-detail-log-maintext");
-              $('.task-detail-log-clipboard').html(hostNames).width(wrapper.width()).height(250);
-              Em.run.next(function() {
-                $('.task-detail-log-clipboard').select();
-              });
-            }
-          }.observes('textareaVisible')
-        })
-      });
-    }
     return App.ModalPopup.show({
-      header: Em.I18n.t('rolling.nothingToDo.header'),
-      body: Em.I18n.t('hosts.bulkOperation.confirmation.add.component.nothingToDo.body').format(operationData.componentNameFormatted),
-      secondary: false
+      header: hostsWithOutComponent.length ? Em.I18n.t('hosts.bulkOperation.confirmation.header') : Em.I18n.t('rolling.nothingToDo.header'),
+      primary: hostsWithOutComponent.length ? Em.I18n.t('hosts.host.addComponent.popup.confirm') : null,
+
+      onPrimary: function() {
+        self.bulkAddHostComponents(operationData, hostsWithOutComponent);
+        this._super();
+      },
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/host/bulk_add_delete_confirm_popup'),
+        modifyMessage: Em.I18n.t('hosts.bulkOperation.confirmation.add.component').format(operationData.componentNameFormatted),
+        skipMessage: hostsWithOutComponent.length ? Em.I18n.t('hosts.bulkOperation.confirmation.cannot.add1') : Em.I18n.t('hosts.bulkOperation.confirmation.cannot.add2').format(operationData.componentNameFormatted),
+        hostsToModify: hostsWithOutComponent.length ? hostsWithOutComponent.join("\n") : null,
+        skippedHosts: hostsWithComponent.length ? hostsWithComponent : null,
+        onToggleHost: function (host) {
+          host.contexts[0].toggleProperty('isCollapsed');
+        }
+      })
     });
   },
   /**
@@ -669,11 +601,11 @@ App.BulkOperationsController = Em.Controller.extend({
       hosts: hosts.mapProperty('hostName'),
       displayParams: ['host_components/HostRoles/state']
     }, function (data) {
-      return self._getComponentsFromServerForHostComponentsDeleteCallback(operationData, data);
+      return self._getComponentsFromServerForHostComponentsDeleteCallback(operationData, data, hosts);
     });
   },
 
-  _getComponentsFromServerForHostComponentsDeleteCallback: function (operationData, data) {
+  _getComponentsFromServerForHostComponentsDeleteCallback: function (operationData, data, requestedHosts) {
     var self = this;
     var minToInstall = App.StackServiceComponent.find(operationData.componentName).get('minToInstall');
     var installedCount = App.HostComponent.getCount(operationData.componentName, 'totalCount');
@@ -683,13 +615,6 @@ App.BulkOperationsController = Em.Controller.extend({
       return [App.HostComponentStatus.stopped, App.HostComponentStatus.unknown, App.HostComponentStatus.install_failed, App.HostComponentStatus.upgrade_failed, App.HostComponentStatus.init].contains(state);
     }).mapProperty('Hosts.host_name');
 
-    if (!hostsToDelete.length) {
-      return App.ModalPopup.show({
-        header: Em.I18n.t('rolling.nothingToDo.header'),
-        body: Em.I18n.t('hosts.bulkOperation.confirmation.delete.component.nothingToDo.body').format(operationData.componentNameFormatted),
-        secondary: false
-      });
-    }
     if (installedCount - hostsToDelete.length < minToInstall) {
       return App.ModalPopup.show({
         header: Em.I18n.t('rolling.nothingToDo.header'),
@@ -698,56 +623,45 @@ App.BulkOperationsController = Em.Controller.extend({
       });
     }
 
-    var hostsToSkip = installedHosts.filter(function (host) {
-      return !hostsToDelete.contains(host);
-    });
+    var hostsNotToDelete = [];
 
-    var minShown = 3;
+    requestedHosts.mapProperty('hostName').forEach(function (host) {
+      if (!hostsToDelete.contains(host)) {
+        var hostToSkip = Em.Object.create({
+          error : {
+            key: host,
+            message: null,
+          },
+          isCollapsed : true,
+          isBodyVisible: Em.computed.ifThenElse('isCollapsed', 'display: none;', 'display: block;')
+        });
+        if(installedHosts.contains(host)) {
+          hostToSkip.error.message = Em.I18n.t('hosts.bulkOperation.confirmation.delete.component.notStopped').format(operationData.componentNameFormatted);
+        } else {
+          hostToSkip.error.message = Em.I18n.t('hosts.bulkOperation.confirmation.delete.component.notInstalled').format(operationData.componentNameFormatted);
+        }
+        hostsNotToDelete.push(hostToSkip);
+      }
+    });
 
     return App.ModalPopup.show({
-      header: Em.I18n.t('hosts.bulkOperation.confirmation.header'),
-      hostNames: hostsToDelete.join("\n"),
-      visibleHosts: self._showHostNames(hostsToDelete, "\n", minShown),
-      hostNamesSkippedVisible: self._showHostNames(hostsToSkip, "\n", minShown),
-      expanded: false,
-
-      hostNamesSkipped: function() {
-        return hostsToSkip.length ? hostsToSkip.join("\n") : false;
-      }.property(),
-
-      didInsertElement: function() {
-        this.set('expanded', hostsToDelete.length <= minShown);
-        this._super();
-      },
+      header: hostsToDelete.length ? Em.I18n.t('hosts.bulkOperation.confirmation.header') : Em.I18n.t('rolling.nothingToDo.header'),
+      primary: hostsToDelete.length ? Em.I18n.t('hosts.host.deleteComponent.popup.confirm') : null,
+      primaryClass: 'btn-warning',
 
       onPrimary: function() {
         self.bulkDeleteHostComponents(operationData, hostsToDelete);
         this._super();
       },
       bodyClass: Em.View.extend({
-        templateName: require('templates/main/host/bulk_operation_confirm_popup'),
-        message: Em.I18n.t('hosts.bulkOperation.confirmation.add.component').format(operationData.message, operationData.componentNameFormatted, hostsToDelete.length),
-        warningInfo: Em.I18n.t('hosts.bulkOperation.confirmation.delete.component.skip').format(operationData.componentNameFormatted),
-        textareaVisible: false,
-        textTrigger: function() {
-          this.toggleProperty('textareaVisible');
-        },
-
-        showAll: function() {
-          this.set('parentView.visibleHosts', this.get('parentView.hostNames'));
-          this.set('parentView.hostNamesSkippedVisible', this.get('parentView.hostNamesSkipped'));
-          this.set('parentView.expanded', true);
-        },
-        putHostNamesToTextarea: function() {
-          var hostNames = this.get('parentView.hostNames');
-          if (this.get('textareaVisible')) {
-            var wrapper = $(".task-detail-log-maintext");
-            $('.task-detail-log-clipboard').html(hostNames).width(wrapper.width()).height(250);
-            Em.run.next(function() {
-              $('.task-detail-log-clipboard').select();
-            });
-          }
-        }.observes('textareaVisible')
+        templateName: require('templates/main/host/bulk_add_delete_confirm_popup'),
+        modifyMessage: Em.I18n.t('hosts.bulkOperation.confirmation.delete.component').format(operationData.componentNameFormatted),
+        skipMessage: hostsToDelete.length ? Em.I18n.t('hosts.bulkOperation.confirmation.delete.component.cannot1') :  Em.I18n.t('hosts.bulkOperation.confirmation.delete.component.cannot2').format(operationData.componentNameFormatted),
+        hostsToModify: hostsToDelete.length ? hostsToDelete.join("\n") : null,
+        skippedHosts: hostsNotToDelete.length ? hostsNotToDelete : null,
+        onToggleHost: function (host) {
+          host.contexts[0].toggleProperty('isCollapsed');
+        }
       })
     });
   },
@@ -784,6 +698,7 @@ App.BulkOperationsController = Em.Controller.extend({
   bulkOperationForHostComponentsDeleteCallback: function (arg0, arg1, arg2, arg3, arg4) {
     var deletedHosts = [];
     var undeletableHosts = [];
+    var componentName = arg2.componentName;
     if (arg1 == "error") {
       var request = arg0;
       let params = arg4;
@@ -803,9 +718,7 @@ App.BulkOperationsController = Em.Controller.extend({
       let params = arg2;
       if (data) {
         data.deleteResult.forEach(function (host) {
-          if (host.deleted) {
-            deletedHosts.push(host);
-          } else {
+          if (!host.deleted) {
             var _host = Ember.Object.create({
               error: host.error,
               isCollapsed: true,
@@ -814,9 +727,8 @@ App.BulkOperationsController = Em.Controller.extend({
             undeletableHosts.push(_host);
           }
         });
-      } else {
-        deletedHosts.pushObjects(params.hostNames.map(hostName => ({deleted: {key: `${hostName}/${params.componentName}`}})));
       }
+      deletedHosts = (params.hostNames);
     }
 
     return App.ModalPopup.show({
@@ -826,9 +738,10 @@ App.BulkOperationsController = Em.Controller.extend({
 
       bodyClass: Em.View.extend({
         templateName: require('templates/main/host/delete_hosts_result_popup'),
-        message: Em.I18n.t('hosts.bulkOperation.delete.component.dryRun.message').format(undeletableHosts.length),
+        message: Em.I18n.t('hosts.bulkOperation.delete.component.dryRun.message').format(componentName),
+        componentName: componentName,
         undeletableHosts: undeletableHosts,
-        deletedHosts: deletedHosts.sortProperty('deleted.key'),
+        deletedHosts: deletedHosts,
         deleteComponents: true,
         onToggleHost: function (host) {
           host.contexts[0].toggleProperty('isCollapsed');
@@ -1174,28 +1087,24 @@ App.BulkOperationsController = Em.Controller.extend({
       return;
     }
 
-    if ('SET_RACK_INFO' === operationData.action) {
+    if (['SET_RACK_INFO', 'ADD', 'DELETE'].contains(operationData.action)) {
       return self.bulkOperation(operationData, hosts);
     }
 
-    var hostNames = hosts.mapProperty('hostName');
-    var hostNamesSkipped = [];
-    if ('DECOMMISSION' === operationData.action) {
-      hostNamesSkipped = this._getSkippedForDecommissionHosts(json, hosts, operationData);
-    }
-    if ('PASSIVE_STATE' === operationData.action) {
-      hostNamesSkipped = this._getSkippedForPassiveStateHosts(hosts);
-    }
+     var hostNames = hosts.mapProperty('hostName');
+     var hostNamesSkipped = [];
+     if ('DECOMMISSION' === operationData.action) {
+       hostNamesSkipped = this._getSkippedForDecommissionHosts(json, hosts, operationData);
+     }
+     if ('PASSIVE_STATE' === operationData.action) {
+       hostNamesSkipped = this._getSkippedForPassiveStateHosts(hosts);
+     }
 
     var message = "";
     if (operationData.componentNameFormatted) {
       message = Em.I18n.t('hosts.bulkOperation.confirmation.hostComponents').format(operationData.message, operationData.componentNameFormatted, hostNames.length);
     } else {
-      if (operationData.action == 'DELETE') {
-        message = Em.I18n.t('hosts.bulkOperation.confirmation.delete.hosts').format(hostNames.length);
-      } else {
-        message = Em.I18n.t('hosts.bulkOperation.confirmation.hosts').format(operationData.message, hostNames.length);
-      }
+      message = Em.I18n.t('hosts.bulkOperation.confirmation.hosts').format(operationData.message, hostNames.length);
     }
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/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 e3b5471..25a27b1 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -2477,8 +2477,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
    *  - flag, that indicate whether ZooKeeper Server is installed
    * @return {Object}
    */
-  getHostComponentsInfo: function () {
-    var componentsOnHost = this.get('content.hostComponents');
+  getHostComponentsInfo: function (hostComponents) {
+    var componentsOnHost = hostComponents || this.get('content.hostComponents');
     var stoppedStates = [App.HostComponentStatus.stopped,
       App.HostComponentStatus.install_failed,
       App.HostComponentStatus.upgrade_failed,

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 57999a8..f570608 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -2616,26 +2616,37 @@ Em.I18n.translations = {
   'hosts.bulkOperation.passiveState.nothingToDo.body':'All hosts that you selected are already in Maintenance Mode.',
   'hosts.bulkOperation.warningInfo.body':'Components on these hosts are stopped so decommission will be skipped.',
   'hosts.bulkOperation.host_components.passiveState.nothingToDo.body':'All host components that you selected are already in Maintenance Mode',
-  'hosts.bulkOperation.confirmation.add.component':'You are going to <strong>{0} {1}</strong> on the following {2} hosts.',
-  'hosts.bulkOperation.confirmation.add.component.skip':'The following hosts are skipped as they already have {0} installed.',
+  'hosts.bulkOperation.confirmation.add.component':'{0} will be added to the following hosts.',
+  'hosts.bulkOperation.confirmation.add.component.skip':'{0} already installed.',
   'hosts.bulkOperation.confirmation.add.component.nothingToDo.body': 'All the selected hosts have {0} installed already.',
-  'hosts.bulkOperation.deleteHosts.dryRun.header':'Confirm Bulk Delete Hosts',
-  'hosts.bulkOperation.deleteHosts.dryRun.message':'There are <strong>{0} host(s)</strong> that cannot be deleted (expand for reason):',
-  'hosts.bulkOperation.deleteHosts.dryRun.primary':'Delete The Other {0} Host(s)',
+  'hosts.bulkOperation.confirmation.cannot.add1': 'The following hosts will be skipped (expand for reason):',
+  'hosts.bulkOperation.confirmation.cannot.add2': '{0} cannot be added to the following hosts (expand for reason):',
+  'hosts.bulkOperation.deleteHosts.nonDeletableComponents': 'Deletion of the following components is not supported: {0}',
+  'hosts.bulkOperation.deleteHosts.nonAddableMasterComponents': 'Host contains the following master components: {0}',
+  'hosts.bulkOperation.deleteHosts.lastMasterComponents': 'Cluster does not contain any other instance of the following master components: {0}',
+  'hosts.bulkOperation.deleteHosts.runningComponents': 'The following components are running and need to be stopped: {0}',
+  'hosts.bulkOperation.deleteHosts.confirm.header':'Confirm Bulk Delete Hosts',
+  'hosts.bulkOperation.deleteHosts.confirm.delete': 'The following hosts will be deleted:',
+  'hosts.bulkOperation.deleteHosts.cannot.delete1':'The following hosts will be skipped (expand for reason):',
+  'hosts.bulkOperation.deleteHosts.cannot.delete2':'Selected hosts cannot be deleted (expand for reason)',
   'hosts.bulkOperation.deleteHosts.confirmation.header':'Confirm Bulk Delete Hosts',
-  'hosts.bulkOperation.deleteHosts.confirmation.body': 'Are you sure you want to delete host(s):',
-  'hosts.bulkOperation.deleteHosts.confirmation.body.msg1': 'By removing above hosts, Ambari will ignore future communication from them. Software packages will not be removed from the hosts. The components on the hosts should not be restarted. If you wish to readd the hosts to the cluster, be sure to clean them.',
-  'hosts.bulkOperation.deleteHosts.confirmation.body.msg2': '<b>WARNING!</b> If the agent is still heartbeating, the hosts will still exist in the database.',
-  'hosts.bulkOperation.deleteHosts.confirmation.body.msg3': 'To completely delete the hosts, first stop ambari-agent on them.',
-  'hosts.bulkOperation.deleteHosts.confirmation.body.msg4': 'If the hosts were hosting a Zookeeper Server, the Zookeeper Service should be restarted. Go to the <i>Services</i> page.',
+  'hosts.bulkOperation.deleteHosts.confirmation.body.msg1': '<b>Please note: </b>Once removed, Ambari will ignore future communications from these hosts. As part of the removal process, packages will not be removed, so please do not attempt to manually start the services on the host once they have been removed from Ambari. If you wish to re-add the hosts to the cluster, please completely clean the hosts before attempting to add them.',
+  'hosts.bulkOperation.deleteHosts.confirmation.body.msg2': 'To ensure they are completely removed from Ambari database,',
+  'hosts.bulkOperation.deleteHosts.confirmation.body.msg3': 'please make sure the Ambari Agent process is completely stopped on these hosts before proceeding.',
   'hosts.bulkOperation.deleteHosts.result.header':'Delete Hosts',
-  'hosts.bulkOperation.deleteHosts.result.body': 'The following hosts and host components were deleted successfully:',
-  'hosts.bulkOperation.confirmation.delete.component.minimum.body': 'At least {0} {1} should be installed in the cluster.',
-  'hosts.bulkOperation.confirmation.delete.component.nothingToDo.body': '{0} are neither installed on selected hosts nor in the states that can be deleted.',
-  'hosts.bulkOperation.confirmation.delete.component.skip':'The following hosts are skipped as {0} on them are not in the states that can be deleted.',
+  'hosts.bulkOperation.deleteHosts.result.body': 'The following hosts were successfully deleted:',
+  'hosts.bulkOperation.confirmation.delete.component.cannot1': 'The following hosts will be skipped (expand for reason):',
+  'hosts.bulkOperation.confirmation.delete.component.cannot2': '{0} cannot be deleted from the selected hosts:',
+  'hosts.bulkOperation.confirmation.delete.component': '{0} will be deleted from the following hosts',
+  'hosts.bulkOperation.confirmation.delete.component.minimum.body': 'Cannot delete. At least {0} {1} required',
+  'hosts.bulkOperation.confirmation.delete.component.nothingToDo.notStopped': '{0} not Stopped on all selected hosts',
+  'hosts.bulkOperation.confirmation.delete.component.nothingToDo.notInstalled': '{0} not installed in any of the selected hosts',
+  'hosts.bulkOperation.confirmation.delete.component.skip':'The following hosts will be skipped',
   'hosts.bulkOperation.delete.component.result.header':'Delete Components',
-  'hosts.bulkOperation.delete.component.result.body': 'The following components were deleted successfully:',
-  'hosts.bulkOperation.delete.component.dryRun.message':'There are <strong>{0} host(s)</strong> that cannot be deleted (expand for reason):',
+  'hosts.bulkOperation.confirmation.delete.component.notStopped': '{0} not Stopped',
+  'hosts.bulkOperation.confirmation.delete.component.notInstalled': '{0} not Installed',
+  'hosts.bulkOperation.delete.component.result.body': ' was successfully removed from the following hosts:',
+  'hosts.bulkOperation.delete.component.dryRun.message':'{0} could not be deleted from the following hosts(expand for reason)',
 
   'hosts.selectHostsDialog.title': 'Select Configuration Group Hosts',
   'hosts.selectHostsDialog.message': 'Select hosts that should belong to this {0} Configuration Group. All hosts belonging to this group will have the same set of configurations.',

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index 43fead6..c12864a 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -2551,3 +2551,43 @@ a.abort-icon:hover {
   padding: 0 10px;
   background-color: @diff-background-equal;
 }
+
+.bulk-host-display {
+  margin-bottom: 10px;
+  pre {
+    max-height: 120px;
+    font-family: 'Roboto', sans-serif;
+    color: @gray-text;
+    font-size: 12px;
+  }
+}
+
+.bulk-host-skipped {
+  margin-bottom: 10px;
+  .skipped-hosts {
+    .icon {
+      color: #337AB7;
+    }
+    a {
+      color: @gray-text;
+      text-decoration: none;
+    }
+    p {
+      line-height: 1;
+      margin-bottom: 5px;
+    }
+  }
+  .skipped-hosts-text {
+    p {
+      font-size: 11px;
+      line-height: 1.2;
+    }
+  }
+}
+
+.skipped-hosts-panel.panel {
+  background-color: #f5f5f5;
+  padding: 10px;
+  max-height: 120px;
+  overflow: scroll;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/templates/main/host/bulk_add_delete_confirm_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/bulk_add_delete_confirm_popup.hbs b/ambari-web/app/templates/main/host/bulk_add_delete_confirm_popup.hbs
new file mode 100644
index 0000000..227d154
--- /dev/null
+++ b/ambari-web/app/templates/main/host/bulk_add_delete_confirm_popup.hbs
@@ -0,0 +1,40 @@
+{{!
+* 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.
+}}
+{{#if view.hostsToModify}}
+  <p>{{{view.modifyMessage }}}</p>
+  <div class="bulk-host-display">
+    <pre>{{view.hostsToModify}}</pre>
+  </div>
+{{/if}}
+
+{{#if view.skippedHosts}}
+  <p>{{{view.skipMessage}}}</p>
+  <div class="skipped-hosts-panel panel panel-default" >
+  {{#each host in view.skippedHosts}}
+    <div class="bulk-host-skipped">
+      <div class="skipped-hosts" {{action "onToggleHost" host target="view"}}>
+        <i {{bindAttr class=":pull-left :icon host.isCollapsed:icon-caret-right:icon-caret-down"}}></i>
+        <a><p>{{host.error.key}}</p></a>
+      </div>
+      <div class="skipped-hosts-text collapse in" {{bindAttr style="host.isBodyVisible"}}>
+        <p><i>{{host.error.message}}</i></p>
+      </div>
+    </div>
+  {{/each}}
+  </div>
+{{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/templates/main/host/delete_hosts_dry_run_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/delete_hosts_dry_run_popup.hbs b/ambari-web/app/templates/main/host/delete_hosts_dry_run_popup.hbs
deleted file mode 100644
index 44ebf69..0000000
--- a/ambari-web/app/templates/main/host/delete_hosts_dry_run_popup.hbs
+++ /dev/null
@@ -1,32 +0,0 @@
-{{!
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance
-* with the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-}}
-<p>{{{view.message}}}</p>
-{{#each host in view.undeletableHosts}}
-  <div class="panel panel-default">
-    <div class="panel-heading" {{action "onToggleHost" host target="view"}}>
-      <i {{bindAttr class=":pull-left :panel-toggle host.isCollapsed:icon-caret-right:icon-caret-down"}}></i>
-      <a class="panel-toggle">
-        <p>{{host.error.key}}</p>
-      </a>
-    </div>
-
-    <div class="panel-body collapse in" {{bindAttr style="host.isBodyVisible"}}>
-      <p>{{host.error.message}}</p>
-    </div>
-  </div>
-{{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/templates/main/host/delete_hosts_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/delete_hosts_popup.hbs b/ambari-web/app/templates/main/host/delete_hosts_popup.hbs
index 93ffe97..cf99136 100644
--- a/ambari-web/app/templates/main/host/delete_hosts_popup.hbs
+++ b/ambari-web/app/templates/main/host/delete_hosts_popup.hbs
@@ -15,18 +15,15 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 }}
-<p><i class="glyphicon glyphicon-warning-sign"></i> {{t hosts.bulkOperation.deleteHosts.confirmation.body}}</p>
-{{#each host in view.hosts}}
-  <div><i>{{{host.deleted.key}}}</i></div>
-{{/each}}
-<br />
-<div class='alert alert-warning'>{{{t common.important.strong}}}
-    {{t hosts.bulkOperation.deleteHosts.confirmation.body.msg1}}
-</div>
 
-<div class='alert alert-warning'>
-    {{t hosts.bulkOperation.deleteHosts.confirmation.body.msg2}}
-    <span class="text-danger">{{t hosts.bulkOperation.deleteHosts.confirmation.body.msg3}}</span>
+<div>{{{t hosts.bulkOperation.deleteHosts.confirmation.body.msg1}}}</div>
+<br />
+<div>
+  {{{t hosts.bulkOperation.deleteHosts.confirmation.body.msg2}}}
+  <span class="text-danger">{{t hosts.bulkOperation.deleteHosts.confirmation.body.msg3}}</span>
 </div>
 
-<div class='alert alert-warning'>{{{t common.important.strong}}} {{t hosts.bulkOperation.deleteHosts.confirmation.body.msg4}}</div>
\ No newline at end of file
+<div class="form-inline align-center"><br />
+  <label><strong>{{view.typeMessage}}</strong></label>&nbsp;
+  {{view Ember.TextField valueBinding="view.parentView.confirmInput" class="input-sm form-control"}}<br />
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/templates/main/host/delete_hosts_result_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/delete_hosts_result_popup.hbs b/ambari-web/app/templates/main/host/delete_hosts_result_popup.hbs
index 2e074bc..7c01a2d 100644
--- a/ambari-web/app/templates/main/host/delete_hosts_result_popup.hbs
+++ b/ambari-web/app/templates/main/host/delete_hosts_result_popup.hbs
@@ -17,14 +17,14 @@
 }}
 {{#if view.deletedHosts}}
   {{#if view.deleteComponents}}
-    <p>{{t hosts.bulkOperation.delete.component.result.body}}</p>
+    <p>{{{view.componentName}}}{{t hosts.bulkOperation.delete.component.result.body}}</p>
   {{else}}
     <p>{{t hosts.bulkOperation.deleteHosts.result.body}}</p>
   {{/if}}
 {{/if}}
 
 {{#each deletedHost in view.deletedHosts}}
-  <div><i>{{{deletedHost.deleted.key}}}</i></div>
+  <div><i>{{{deletedHost}}}</i></div>
 {{/each}}
 <br />
 {{#if view.undeletableHosts}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/4a68e4e8/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 42bb974..483be5e 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -303,8 +303,7 @@ var urls = {
       return {
         data: JSON.stringify({
           RequestInfo: {
-            query: data.query,
-            force_delete_components: true
+            query: data.query
           }
         })
       }