Updated Branches: refs/heads/trunk 0ad06758f -> bbccf1226
AMBARI-4260. Bulk Operation icon and menu (with confirmation). (onechiporenko) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/bbccf122 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/bbccf122 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/bbccf122 Branch: refs/heads/trunk Commit: bbccf12265c515dd41deb59b5a56f29350467384 Parents: 0ad0675 Author: Oleg Nechiporenko <onechipore...@apache.org> Authored: Fri Jan 10 16:19:32 2014 +0200 Committer: Oleg Nechiporenko <onechipore...@apache.org> Committed: Fri Jan 10 16:28:11 2014 +0200 ---------------------------------------------------------------------- ambari-web/app/controllers/main/host.js | 9 + ambari-web/app/messages.js | 11 ++ ambari-web/app/templates/main/host.hbs | 4 +- .../main/host/bulk_operation_confirm_popup.hbs | 35 ++++ .../templates/main/host/bulk_operation_menu.hbs | 77 ++++++++ ambari-web/app/views.js | 1 + ambari-web/app/views/main/host.js | 54 +++++- .../views/main/host/hosts_table_menu_view.js | 187 +++++++++++++++++++ 8 files changed, 376 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/controllers/main/host.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/host.js b/ambari-web/app/controllers/main/host.js index 65f9981..8b094c6 100644 --- a/ambari-web/app/controllers/main/host.js +++ b/ambari-web/app/controllers/main/host.js @@ -141,6 +141,15 @@ App.MainHostController = Em.ArrayController.extend({ var hosts = this.get('content'); var selectedHosts = hosts.filterProperty('id', host_id); this.get('fullContent').removeObjects(selectedHosts); + }, + + /** + * Do bulk operation for selected hosts or hostComponents + * @param {Object} operationData - data about bulk operation (action, hosts or hostComponents etc) + * @param {Array} hostNames - list of affected hostNames + */ + bulkOperation: function(operationData, hostNames) { + } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index cc30221..bd88b18 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -173,6 +173,8 @@ Em.I18n.translations = { 'maintenance.turnOn':'Turn On Maintenance Mode', 'maintenance.turnOff':'Turn Off Maintenance Mode', + 'maintenance.turnOnFor':'Turn On Maintenance Mode for', + 'maintenance.turnOffFor':'Turn Off Maintenance Mode for', 'requestInfo.installComponents':'Install Components', 'requestInfo.installServices':'Install Services', @@ -1317,6 +1319,15 @@ Em.I18n.translations = { 'hosts.table.componentsInMaintenance.withNames':'{0} in maintenance mode', 'hosts.table.componentsInMaintenance.withoutNames':'{0} components in maintenance mode', + 'hosts.table.menu.l1.selectedHosts':'Selected Hosts', + 'hosts.table.menu.l1.filteredHosts':'Filtered Hosts', + 'hosts.table.menu.l1.allHosts':'All Hosts', + 'hosts.table.menu.l2.restartAllComponents':'Restart All Components', + + 'hosts.bulkOperation.confirmation.header':'Confirm Bulk Operation', + 'hosts.bulkOperation.confirmation.hosts':'Are you sure you want to <strong>{0}</strong> on the following {1} hosts?', + 'hosts.bulkOperation.confirmation.hostComponents':'Are you sure you want to <strong>{0} {1}</strong> on the following {2} hosts?', + '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 {0} configurations.', 'hosts.selectHostsDialog.filter.placeHolder': 'Filter...', http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/templates/main/host.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/host.hbs b/ambari-web/app/templates/main/host.hbs index e42dfa7..07002aa 100644 --- a/ambari-web/app/templates/main/host.hbs +++ b/ambari-web/app/templates/main/host.hbs @@ -68,7 +68,9 @@ <table class="datatable table table-bordered table-striped" id="hosts-table"> <thead> {{#view view.sortView classNames="label-row" contentBinding="view.filteredContent"}} - <th class="first"> </th> + <th class="first"> + {{view App.HostTableMenuView}} + </th> <th> </th> {{view view.parentView.nameSort}} <th> </th> http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/templates/main/host/bulk_operation_confirm_popup.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/host/bulk_operation_confirm_popup.hbs b/ambari-web/app/templates/main/host/bulk_operation_confirm_popup.hbs new file mode 100644 index 0000000..e3676f4 --- /dev/null +++ b/ambari-web/app/templates/main/host/bulk_operation_confirm_popup.hbs @@ -0,0 +1,35 @@ +{{! +* 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> + +<div class="task-top-wrap"> + <div class="task-detail-ico-wrap"> + <a href="#" title="Click to Copy" {{action "textTrigger"}} class="task-detail-copy"><i class="icon-copy"></i> {{t common.copy}}</a> + </div> +</div> +<div class="task-detail-log-info"> + <div class="content-area"> + <div {{bindAttr class="view.textareaVisible::hidden :task-detail-log-clipboard-wrap"}}> + <textarea class="task-detail-log-clipboard"></textarea> + </div> + <div {{bindAttr class="view.textareaVisible:hidden :task-detail-log-maintext"}}> + <pre>{{hostNames}}</pre> + </div> + </div> +</div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/templates/main/host/bulk_operation_menu.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/host/bulk_operation_menu.hbs b/ambari-web/app/templates/main/host/bulk_operation_menu.hbs new file mode 100644 index 0000000..d54d64f --- /dev/null +++ b/ambari-web/app/templates/main/host/bulk_operation_menu.hbs @@ -0,0 +1,77 @@ +{{! +* 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. +}} + +<div class="dropdown"> + <a class="dropdown-toggle" data-toggle="dropdown" href="#"><span class="icon-asterisk"></span></a> + <ul class="dropdown-menu"> + <li class="dropdown-submenu"> + <a tabindex="-1" href="javascript:void(null);">{{view.menuItems.s.label}} + ({{view.parentView.parentView.selectedCategory.hostsCount}})</a> + <ul class="dropdown-menu"> + {{#each subMenuItem in view.menuItems.s.submenu}} + <li class="dropdown-submenu"> + <a href="javascript:void(null);">{{subMenuItem.label}}</a> + <ul class="dropdown-menu"> + {{#each menuL3Item in subMenuItem.submenu}} + <li> + <a {{action "bulkOperationConfirm" menuL3Item.operationData target="view.parentView.parentView"}} href="#">{{menuL3Item.label}}</a> + </li> + {{/each}} + </ul> + </li> + {{/each}} + </ul> + </li> + <li class="dropdown-submenu"> + <a tabindex="-1" href="javascript:void(null);">{{view.menuItems.f.label}} + ({{view.parentView.parentView.filteredContent.length}})</a> + <ul class="dropdown-menu"> + {{#each subMenuItem in view.menuItems.f.submenu}} + <li class="dropdown-submenu"> + <a href="javascript:void(null);">{{subMenuItem.label}}</a> + <ul class="dropdown-menu"> + {{#each menuL3Item in subMenuItem.submenu}} + <li> + <a {{action "bulkOperationConfirm" menuL3Item.operationData target="view.parentView.parentView"}} href="#">{{menuL3Item.label}}</a> + </li> + {{/each}} + </ul> + </li> + {{/each}} + </ul> + </li> + <li class="dropdown-submenu"> + <a tabindex="-1" href="javascript:void(null);">{{view.menuItems.a.label}} + ({{view.parentView.parentView.content.length}})</a> + <ul class="dropdown-menu"> + {{#each subMenuItem in view.menuItems.a.submenu}} + <li class="dropdown-submenu"> + <a href="javascript:void(null);">{{subMenuItem.label}}</a> + <ul class="dropdown-menu"> + {{#each menuL3Item in subMenuItem.submenu}} + <li> + <a {{action "bulkOperationConfirm" menuL3Item.operationData target="view.parentView.parentView"}} href="#">{{menuL3Item.label}}</a> + </li> + {{/each}} + </ul> + </li> + {{/each}} + </ul> + </li> + </ul> +</div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/views.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js index f517ff9..9a9b36a 100644 --- a/ambari-web/app/views.js +++ b/ambari-web/app/views.js @@ -39,6 +39,7 @@ require('views/main'); require('views/main/menu'); require('views/main/charts'); require('views/main/host'); +require('views/main/host/hosts_table_menu_view'); require('views/main/host/details'); require('views/main/host/menu'); require('views/main/host/summary'); http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/ambari-web/app/views/main/host.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/host.js b/ambari-web/app/views/main/host.js index 31187d9..1096f87 100644 --- a/ambari-web/app/views/main/host.js +++ b/ambari-web/app/views/main/host.js @@ -95,6 +95,59 @@ App.MainHostView = App.TableView.extend({ this.clearFilters(); }, + /** + * Confirmation Popup for bulk Operations + */ + bulkOperationConfirm: function(event) { + var operationData = event.context; + var hostNames = []; + var self = this; + switch(operationData.selection) { + case 's': + hostNames = this.get('content').filterProperty('selected').mapProperty('hostName'); + break; + case 'f': + hostNames = this.get('filteredContent').mapProperty('hostName'); + break; + case 'a': + hostNames = this.get('content').mapProperty('hostName'); + break; + } + var message; + if (operationData.componentNameFormatted) { + message = Em.I18n.t('hosts.bulkOperation.confirmation.hostComponents').format(operationData.message, operationData.componentNameFormatted, hostNames.length); + } + else { + message = Em.I18n.t('hosts.bulkOperation.confirmation.hosts').format(operationData.message, hostNames.length); + } + App.ModalPopup.show({ + header: Em.I18n.t('hosts.bulkOperation.confirmation.header'), + hostNames: hostNames.join("\n"), + onPrimary: function() { + self.get('controller').bulkOperation(operationData, hostNames); + this._super(); + }, + bodyClass: Em.View.extend({ + templateName: require('templates/main/host/bulk_operation_confirm_popup'), + message: message, + textareaVisible: false, + textTrigger: function() { + this.set('textareaVisible', !this.get('textareaVisible')); + }, + 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') + }) + }); + }, + sortView: sort.wrapperView, nameSort: sort.fieldView.extend({ column: 1, @@ -395,7 +448,6 @@ App.MainHostView = App.TableView.extend({ } }), - /** * Filter view for name column * Based on <code>filters</code> library http://git-wip-us.apache.org/repos/asf/ambari/blob/bbccf122/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 new file mode 100644 index 0000000..a3cac2a --- /dev/null +++ b/ambari-web/app/views/main/host/hosts_table_menu_view.js @@ -0,0 +1,187 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +App.HostTableMenuView = Em.View.extend({ + + templateName: require('templates/main/host/bulk_operation_menu'), + + /** + * Get third-level menu items for slave components (but not for DataNode!) + * @returns {Array} + */ + getSlaveItemsTemplate: function() { + return Em.A([ + Em.Object.create({ + label: Em.I18n.t('common.start'), + operationData: Em.Object.create({ + action: 'start', + message: Em.I18n.t('common.start') + }) + }), + Em.Object.create({ + label: Em.I18n.t('common.stop'), + operationData: Em.Object.create({ + action: 'stop', + message: Em.I18n.t('common.stop') + }) + }), + Em.Object.create({ + label: Em.I18n.t('common.restart'), + operationData: Em.Object.create({ + action: 'restart', + message: Em.I18n.t('common.restart') + }) + }), + Em.Object.create({ + label: Em.I18n.t('maintenance.turnOn'), + operationData: Em.Object.create({ + action: 'turn_on_maintenance', + message: Em.I18n.t('maintenance.turnOnFor') + }) + }), + Em.Object.create({ + label: Em.I18n.t('maintenance.turnOff'), + operationData: Em.Object.create({ + action: 'turn_off_maintenance', + message: Em.I18n.t('maintenance.turnOffFor') + }) + }) + ]); + }, + + /** + * Get third-level menu items for DataNode + * @returns {Array} + */ + getDataNodeItemsTemplate: function() { + var dataNodesItems = this.getSlaveItemsTemplate(); + dataNodesItems.push(Em.Object.create({ + label: Em.I18n.t('common.decommission'), + operationData: Em.Object.create({ + action: 'decommission', + message: Em.I18n.t('common.decommission') + }) + })); + dataNodesItems.push(Em.Object.create({ + label: Em.I18n.t('common.recommission'), + operationData: Em.Object.create({ + action: 'recommission', + message: Em.I18n.t('common.recommission') + }) + })); + dataNodesItems.setEach('operationData.componentNameFormatted', Em.I18n.t('dashboard.services.hdfs.datanodes')); + dataNodesItems.setEach('operationData.componentName', 'DATANODE'); + return dataNodesItems; + }, + + /** + * Get third-level menu items for Hosts + * @returns {Array} + */ + getHostItemsTemplate: function() { + return Em.A([ + Em.Object.create({ + label: Em.I18n.t('hosts.host.details.startAllComponents'), + operationData: Em.Object.create({ + action: 'start_all', + message: Em.I18n.t('hosts.host.details.startAllComponents') + }) + }), + Em.Object.create({ + label: Em.I18n.t('hosts.host.details.stopAllComponents'), + operationData: Em.Object.create({ + action: 'stop_all', + message: Em.I18n.t('hosts.host.details.stopAllComponents') + }) + }), + Em.Object.create({ + label: Em.I18n.t('hosts.table.menu.l2.restartAllComponents'), + operationData: Em.Object.create({ + action: 'restart_all', + message: Em.I18n.t('hosts.table.menu.l2.restartAllComponents') + }) + }), + Em.Object.create({ + label: Em.I18n.t('maintenance.turnOn'), + operationData: Em.Object.create({ + action: 'turn_on_maintenance', + message: Em.I18n.t('maintenance.turnOn') + }) + }), + Em.Object.create({ + label: Em.I18n.t('maintenance.turnOff'), + operationData: Em.Object.create({ + action: 'turn_off_maintenance', + message: Em.I18n.t('maintenance.turnOff') + }) + }) + ]); + }, + + /** + * Get second-level menu + * @param {String} selection + * @returns {Array} + */ + getSubMenuItemsTemplate: function(selection) { + var submenu = [{label: Em.I18n.t('common.hosts'), submenu: this.getHostItemsTemplate()}]; + + if (!!App.HDFSService.find().content.length) { + submenu.push({label: Em.I18n.t('dashboard.services.hdfs.datanodes'), submenu: this.getDataNodeItemsTemplate()}); + } + + if (!!App.YARNService.find().content.length) { + var slaveItemsForYarn = this.getSlaveItemsTemplate(); + slaveItemsForYarn.setEach('operationData.componentName', 'NODEMANAGER'); + slaveItemsForYarn.setEach('operationData.componentNameFormatted', Em.I18n.t('dashboard.services.yarn.nodeManagers')); + submenu.push({label: Em.I18n.t('dashboard.services.yarn.nodeManagers'), submenu: slaveItemsForYarn}); + } + + if (!!App.HBaseService.find().content.length) { + var slaveItemsForHBase = this.getSlaveItemsTemplate(); + slaveItemsForHBase.setEach('operationData.componentName', 'HBASE_REGIONSERVER'); + slaveItemsForHBase.setEach('operationData.componentNameFormatted', Em.I18n.t('dashboard.services.hbase.regionServers')); + submenu.push({label: Em.I18n.t('dashboard.services.hbase.regionServers'), submenu: slaveItemsForHBase}); + } + + if (!!App.MapReduceService.find().content.length) { + var slaveItemsForMapReduce = this.getSlaveItemsTemplate(); + slaveItemsForMapReduce.setEach('operationData.componentName', 'TASKTRACKER'); + slaveItemsForMapReduce.setEach('operationData.componentNameFormatted', Em.I18n.t('dashboard.services.mapreduce.taskTrackers')); + submenu.push({label: Em.I18n.t('dashboard.services.mapreduce.taskTrackers'), submenu: slaveItemsForMapReduce}); + } + submenu.forEach(function(item) { + item.submenu.forEach(function(subitem) { + subitem.operationData.selection = selection; + }); + }); + return submenu; + }, + + /** + * Menu-items for Hosts table + * {Object} + */ + menuItems: function() { + return { + s: {label: Em.I18n.t('hosts.table.menu.l1.selectedHosts'), submenu: this.getSubMenuItemsTemplate('s')}, + f: {label: Em.I18n.t('hosts.table.menu.l1.filteredHosts'), submenu: this.getSubMenuItemsTemplate('f')}, + a: {label: Em.I18n.t('hosts.table.menu.l1.allHosts'), submenu: this.getSubMenuItemsTemplate('a')} + }; + }.property('App.router.clusterController.isLoaded') +}); \ No newline at end of file