Repository: ambari
Updated Branches:
  refs/heads/branch-2.5 8581975f9 -> 4db1a1e98


AMBARI-10908 Usability: ability to perform bulk delete host (zhewang)


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

Branch: refs/heads/branch-2.5
Commit: 4db1a1e982fba628bf876c8bf983ea49b2fdec3a
Parents: 8581975
Author: Zhe (Joe) Wang <[email protected]>
Authored: Tue Jun 7 11:24:41 2016 -0700
Committer: Zhe (Joe) Wang <[email protected]>
Committed: Tue Sep 13 13:06:00 2016 -0700

----------------------------------------------------------------------
 .../main/host/bulk_operations_controller.js     | 202 +++++++++++++++++++
 ambari-web/app/messages.js                      |  12 ++
 ambari-web/app/styles/application.less          |   4 +
 .../main/host/delete_hosts_dry_run_popup.hbs    |  34 ++++
 .../templates/main/host/delete_hosts_popup.hbs  |  32 +++
 .../main/host/delete_hosts_result_popup.hbs     |  44 ++++
 ambari-web/app/utils/ajax/ajax.js               |  14 ++
 .../views/main/host/hosts_table_menu_view.js    |   9 +
 8 files changed, 351 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/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 09d6a52..0e73c0c 100644
--- a/ambari-web/app/controllers/main/host/bulk_operations_controller.js
+++ b/ambari-web/app/controllers/main/host/bulk_operations_controller.js
@@ -61,6 +61,9 @@ App.BulkOperationsController = Em.Controller.extend({
         else if (operationData.action === 'REINSTALL'){
           this.bulkOperationForHostsReinstall(operationData, hosts);
         }
+        else if (operationData.action === 'DELETE'){
+          this.bulkOperationForHostsDeleteDryRun(operationData, hosts);
+        }
         else {
           if (operationData.action === 'PASSIVE_STATE') {
             this.bulkOperationForHostsPassiveState(operationData, hosts);
@@ -255,6 +258,205 @@ 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'
+      });
+    });
+  },
+
+  /**
+   * 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({
+        error: {
+          key: params.hosts[0],
+          code: response.status,
+          message: response.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);
+          }
+        });
+      } else {
+        var host = {
+          deleted: {
+            key: params.hosts[0]
+          }
+        };
+        deletableHosts.push(host);
+      }
+    }
+
+    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);
+    }
+  },
+
+  /**
+   * Bulk delete selected hosts
+   * @param {Ember.Enumerable} hosts - list of affected hosts
+   */
+  bulkOperationForHostsDelete: function (hosts) {
+    var self = this;
+    App.get('router.mainAdminKerberosController').getKDCSessionState(function 
() {
+      return App.ModalPopup.show({
+        header: 
Em.I18n.t('hosts.bulkOperation.deleteHosts.confirmation.header'),
+
+        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')
+            },
+            success: 'bulkOperationForHostsDeleteCallback',
+            error: 'bulkOperationForHostsDeleteCallback'
+          });
+        },
+        bodyClass: Em.View.extend({
+          templateName: require('templates/main/host/delete_hosts_popup'),
+          hosts: hosts
+        })
+      });
+    });
+  },
+
+  /**
+   * Show popup after bulk delete hosts
+   * @method bulkOperationForHostsDeleteCallback
+   */
+  bulkOperationForHostsDeleteCallback: function (arg0, arg1, arg2, arg3, arg4) 
{
+    var deletedHosts = [];
+    var undeletableHosts = [];
+    if (arg1 == "error") {
+      var request = arg0;
+      var params = arg4;
+      var response = JSON.parse(request.responseText);
+      var host = Ember.Object.create({
+        error: {
+          key: params.hosts[0],
+          code: response.status,
+          message: response.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) {
+            deletedHosts.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);
+          }
+        });
+      } else {
+        var host = {
+          deleted: {
+            key: params.hosts[0]
+          }
+        };
+        deletedHosts.push(host);
+      }
+    }
+
+    return App.ModalPopup.show({
+      header: Em.I18n.t('hosts.bulkOperation.deleteHosts.result.header'),
+
+      secondary: null,
+
+      bodyClass: Em.View.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,
+        onToggleHost: function (host) {
+          host.contexts[0].toggleProperty('isCollapsed');
+        }
+      }),
+
+      onPrimary: function () {
+        location.reload();
+        this._super();
+      },
+
+      onClose: function () {
+        location.reload();
+        this._super();
+      }
+    });
+  },
+
+  /**
    * Bulk turn on/off passive state for selected hosts
    * @param {Object} operationData - data about bulk operation (action, 
hostComponents etc)
    * @param {Array} hosts - list of affected hosts

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 076aa6d..751f64f 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -2404,6 +2404,17 @@ Em.I18n.translations = {
   '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.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.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.result.header':'Delete Hosts',
+  'hosts.bulkOperation.deleteHosts.result.body': 'The following hosts were 
deleted successfully:',
 
   '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.',
@@ -2478,6 +2489,7 @@ Em.I18n.translations = {
   'hosts.host.details.startAllComponents':'Start All Components',
   'hosts.host.details.stopAllComponents':'Stop All Components',
   'hosts.host.details.restartAllComponents':'Restart All Components',
+  'hosts.host.details.deleteHosts':'Delete Hosts',
   'hosts.host.details.refreshConfigs':'Refresh configs',
   'hosts.host.details.for.postfix':'{0} for host',
   'hosts.host.details.setRackId':'Set Rack',

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less 
b/ambari-web/app/styles/application.less
index 691580d..7fb5639 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -6165,6 +6165,10 @@ input[type="radio"].align-checkbox, 
input[type="checkbox"].align-checkbox {
   background-color: #FFFFFF;
 }
 
+.accordion-heading {
+  background-color: #f0f0f0;
+}
+
 .config-manage-nav {
   .config-groups-dropdown {
     display: inline-block;

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/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
new file mode 100644
index 0000000..7424459
--- /dev/null
+++ b/ambari-web/app/templates/main/host/delete_hosts_dry_run_popup.hbs
@@ -0,0 +1,34 @@
+{{!
+* 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="accordion">
+    <div class="accordion-heading" {{action "onToggleHost" host 
target="view"}}>
+      <i {{bindAttr class=":pull-left :accordion-toggle 
host.isCollapsed:icon-caret-right:icon-caret-down"}}></i>
+      <a class="accordion-toggle">
+        <p>{{host.error.key}}</p>
+      </a>
+    </div>
+
+    <div class="accordion-body collapse in" {{bindAttr 
style="host.isBodyVisible"}}>
+      <div class="accordion-inner">
+        <p>{{host.error.message}}</p>
+      </div>
+    </div>
+  </div>
+{{/each}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/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
new file mode 100644
index 0000000..d45dab8
--- /dev/null
+++ b/ambari-web/app/templates/main/host/delete_hosts_popup.hbs
@@ -0,0 +1,32 @@
+{{!
+* 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><i class="icon-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'>{{{t common.important.strong}}}
+    {{t hosts.bulkOperation.deleteHosts.confirmation.body.msg1}}
+</div>
+
+<div class='alert'>
+    {{t hosts.bulkOperation.deleteHosts.confirmation.body.msg2}}
+    <span style="color: red;">{{t 
hosts.bulkOperation.deleteHosts.confirmation.body.msg3}}</span>
+</div>
+
+<div class='alert'>{{{t common.important.strong}}} {{t 
hosts.bulkOperation.deleteHosts.confirmation.body.msg4}}</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/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
new file mode 100644
index 0000000..9e9c772
--- /dev/null
+++ b/ambari-web/app/templates/main/host/delete_hosts_result_popup.hbs
@@ -0,0 +1,44 @@
+{{!
+* 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.deletedHosts}}
+  <p>{{t hosts.bulkOperation.deleteHosts.result.body}}</p>
+{{/if}}
+
+{{#each deletedHost in view.deletedHosts}}
+  <div><i>{{{deletedHost.deleted.key}}}</i></div>
+{{/each}}
+<br />
+{{#if view.undeletableHosts}}
+  <p>{{{view.message}}}</p>
+  {{#each undeletableHost in view.undeletableHosts}}
+    <div class="accordion">
+      <div class="accordion-heading" {{action "onToggleHost" undeletableHost 
target="view"}}>
+        <i {{bindAttr class=":pull-left :accordion-toggle 
undeletableHost.isCollapsed:icon-caret-right:icon-caret-down"}}></i>
+        <a class="accordion-toggle">
+          <p>{{undeletableHost.error.key}}</p>
+        </a>
+      </div>
+
+      <div class="accordion-body collapse in" {{bindAttr 
style="undeletableHost.isBodyVisible"}}>
+        <div class="accordion-inner">
+          <p>{{undeletableHost.error.message}}</p>
+        </div>
+      </div>
+    </div>
+  {{/each}}
+{{/if}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/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 e148363..38a75e4 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -285,6 +285,20 @@ var urls = {
     }
   },
 
+  'common.hosts.delete': {
+    'real': '/clusters/{clusterName}/hosts{urlParams}',
+    'type': 'DELETE',
+    'format': function (data) {
+      return {
+        data: JSON.stringify({
+          RequestInfo: {
+            query: data.query
+          },
+        })
+      }
+    }
+  },
+
   'common.service.passive': {
     'real': '/clusters/{clusterName}/services/{serviceName}',
     'mock': '',

http://git-wip-us.apache.org/repos/asf/ambari/blob/4db1a1e9/ambari-web/app/views/main/host/hosts_table_menu_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host/hosts_table_menu_view.js 
b/ambari-web/app/views/main/host/hosts_table_menu_view.js
index 5e4b0e0..dccffaa 100644
--- a/ambari-web/app/views/main/host/hosts_table_menu_view.js
+++ b/ambari-web/app/views/main/host/hosts_table_menu_view.js
@@ -306,6 +306,15 @@ App.HostTableMenuView = Em.View.extend({
           message: Em.I18n.t('hosts.host.details.setRackId').format('hosts')
         })
       }));
+      if (App.isAuthorized("HOST.ADD_DELETE_HOSTS")) {
+        result = result.concat(O.create({
+          label: Em.I18n.t('hosts.host.details.deleteHosts'),
+          operationData: O.create({
+            action: 'DELETE',
+            message: Em.I18n.t('hosts.host.details.deleteHosts')
+          })
+        }));
+      }
       return result;
     }.property(),
 

Reply via email to