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 2014/01/10 15:28:18 UTC

git commit: AMBARI-4260. Bulk Operation icon and menu (with confirmation). (onechiporenko)

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 <on...@apache.org>
Authored: Fri Jan 10 16:19:32 2014 +0200
Committer: Oleg Nechiporenko <on...@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