Repository: ambari
Updated Branches:
  refs/heads/trunk e2a743ba0 -> 15013b719


AMBARI-5143 Should not allow decommission/recommission of slave component if 
master component is not running. (ababiichuk)


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

Branch: refs/heads/trunk
Commit: 15013b719954c9a12db705196a34bbd9cbd28c51
Parents: e2a743b
Author: aBabiichuk <[email protected]>
Authored: Wed Mar 19 18:59:29 2014 +0200
Committer: aBabiichuk <[email protected]>
Committed: Wed Mar 19 19:06:30 2014 +0200

----------------------------------------------------------------------
 ambari-web/app/controllers/main/host/details.js |  10 +-
 ambari-web/app/messages.js                      |   1 +
 .../details/host_components/decommissionable.js |  44 +-
 .../templates/main/host/bulk_operation_menu.hbs | 138 ++++--
 .../app/templates/main/host/decommission.hbs    |  25 +
 .../main/host/details/host_component.hbs        |  12 +-
 ambari-web/app/views/main/host.js               |   5 +-
 .../host_component_views/regionserver_view.js   |   1 +
 .../views/main/host/hosts_table_menu_view.js    | 454 +++++++++++--------
 9 files changed, 449 insertions(+), 241 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/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 3cadd01..f4b2068 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -671,12 +671,11 @@ App.MainHostDetailsController = Em.Controller.extend({
 
   /**
    * send command to server to run decommission on DATANODE, TASKTRACKER, 
NODEMANAGER, REGIONSERVER
-   * @param event
+   * @param component
    */
-  decommission: function(event){
+  decommission: function(component){
     var self = this;
     App.showConfirmationPopup(function(){
-      var component = event.context;
       var svcName = component.get('service.serviceName');
       var hostName = self.get('content.hostName');
       // HDFS service, decommission DataNode
@@ -707,12 +706,11 @@ App.MainHostDetailsController = Em.Controller.extend({
   
   /**
    * send command to server to run recommission on DATANODE, TASKTRACKER, 
NODEMANAGER
-   * @param event
+   * @param component
    */
-  recommission: function(event){
+  recommission: function(component){
     var self = this;
     App.showConfirmationPopup(function(){
-      var component = event.context;
       var svcName = component.get('service.serviceName');
       var hostName = self.get('content.hostName');
       // HDFS service, Recommission datanode

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 38273a3..f715262 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1542,6 +1542,7 @@ Em.I18n.translations = {
   'hosts.host.alerts.st':'&nbsp;!&nbsp;',
   'hosts.decommission.popup.body':'Are you sure?',
   'hosts.decommission.popup.header':'Confirmation',
+  'hosts.decommission.tooltip.warning':'Cannot {0} since {1} is not running',
   'hosts.delete.popup.body':'Are you sure you want to delete host <i>{0}</i>?',
   'hosts.delete.popup.body.msg1':'By removing this host, Ambari will ignore 
future communications from this host. Software packages will not be removed 
from the host. The components on the host should not be restarted. If you wish 
to readd this host to the cluster, be sure to clean the host.',
   'hosts.delete.popup.body.msg2':'After deleting this host, Nagios should be 
restarted to remove this host from Nagios monitoring. Go to the <i>Services</i> 
page to restart Nagios.',

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/ambari-web/app/mixins/main/host/details/host_components/decommissionable.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/mixins/main/host/details/host_components/decommissionable.js 
b/ambari-web/app/mixins/main/host/details/host_components/decommissionable.js
index bf8e5f3..6d314de 100644
--- 
a/ambari-web/app/mixins/main/host/details/host_components/decommissionable.js
+++ 
b/ambari-web/app/mixins/main/host/details/host_components/decommissionable.js
@@ -63,6 +63,24 @@ App.Decommissionable = Em.Mixin.create({
   isComponentRecommissionAvailable: null,
 
   /**
+   * Component with stopped masters can't be docommissioned
+   * @type {bool}
+   */
+  isComponentDecommissionDisable: function() {
+    return this.get('content.service.workStatus') != 
App.HostComponentStatus.started;
+  }.property('content.service.workStatus'),
+
+  /**
+   * Tooltip message shows if decommission/recommission is disabled
+   * when masters for current component is down
+   */
+  decommissionTooltipMessage: function() {
+    if (this.get('isComponentDecommissionDisable') && 
(this.get('isComponentRecommissionAvailable') || 
this.get('isComponentDecommissionAvailable'))) {
+      var decom = this.get('isComponentRecommissionAvailable') ? 
Em.I18n.t('common.recommission') : Em.I18n.t('common.decommission');
+      return Em.I18n.t('hosts.decommission.tooltip.warning').format(decom, 
App.format.role(this.get('componentForCheckDecommission')));
+    }
+  }.property('isComponentDecommissionDisable', 
'isComponentRecommissionAvailable', 'isComponentDecommissionAvailable', 
'componentForCheckDecommission'),
+  /**
    * Recalculated component status based on decommission
    * @type {string}
    */
@@ -243,6 +261,30 @@ App.Decommissionable = Em.Mixin.create({
    */
   updateDecommissionStatus: function() {
     Em.run.once(this, 'loadComponentDecommissionStatus');
-  }.observes('content.workStatus', 'content.passiveState')
+  }.observes('content.workStatus', 'content.passiveState'),
+
 
+  decommissionView: Em.View.extend({
+
+    templateName: require('templates/main/host/decommission'),
+
+    text: function() {
+      return this.get('parentView.isComponentDecommissionAvailable') ? 
Em.I18n.t('common.decommission') : Em.I18n.t('common.recommission');
+    }.property('parentView.isComponentDecommissionAvailable'),
+
+    didInsertElement: function() {
+      this._super();
+      App.tooltip($("[rel='decommissionTooltip']"));
+    },
+
+    click: function() {
+      if (!this.get('parentView.isComponentDecommissionDisable')) {
+        if (this.get('parentView.isComponentDecommissionAvailable')) {
+          this.get('controller').decommission(this.get('parentView.content'));
+        } else {
+          this.get('controller').recommission(this.get('parentView.content'));
+        }
+      }
+    }
+  })
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/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
index 315b11d..f5d8fec 100644
--- a/ambari-web/app/templates/main/host/bulk_operation_menu.hbs
+++ b/ambari-web/app/templates/main/host/bulk_operation_menu.hbs
@@ -26,56 +26,116 @@
     <li class="dropdown-submenu">
       <a {{bindAttr 
class="view.parentView.selectedCategory.hasHosts::disabled"}} tabindex="-1" 
href="javascript:void(null);">{{view.menuItems.s.label}}
         ({{view.parentView.selectedCategory.hostsCount}})</a>
-      <ul {{bindAttr class="view.parentView.selectedCategory.hasHosts::hidden 
: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"}} href="#">{{menuL3Item.label}}</a>
-                </li>
-              {{/each}}
-            </ul>
-          </li>
-        {{/each}}
-      </ul>
+        <ul {{bindAttr 
class="view.parentView.selectedCategory.hasHosts::hidden :dropdown-menu"}}>
+          {{#view view.hostItemView}}
+              <a href="javascript:void(null);">{{view.label}}</a>
+              <ul class="dropdown-menu">
+                {{#each operation in view.operationsInfo}}
+                  {{#if operation.label.length}}
+                      <li>
+                        {{#view view.operationView 
contentBinding="operation.operationData" selection="s"}}
+                            <a 
href="javascript:void(null);">{{operation.label}}</a>
+                        {{/view}}
+                      </li>
+                  {{/if}}
+                {{/each}}
+              </ul>
+          {{/view}}
+          {{#each component in view.components}}
+            {{#view view.slaveItemView contentBinding="component"}}
+                <a 
href="javascript:void(null);">{{component.componentNameFormatted}}</a>
+                <ul class="dropdown-menu">
+                  {{#each operation in view.operationsInfo}}
+                    {{#if operation.decommission}}
+                      {{#view view.advancedOperationView 
contentBinding="operation.operationData" selection="s"}}
+                          <a 
href="javascript:void(null);">{{operation.label}}</a>
+                      {{/view}}
+                    {{else}}
+                      {{#view view.commonOperationView 
contentBinding="operation.operationData" selection="s"}}
+                          <a 
href="javascript:void(null);">{{operation.label}}</a>
+                      {{/view}}
+                    {{/if}}
+                  {{/each}}
+                </ul>
+            {{/view}}
+          {{/each}}
+        </ul>
     </li>
     <li class="dropdown-submenu">
       <a {{bindAttr class="view.parentView.hasFilteredItems::disabled"}} 
tabindex="-1" href="javascript:void(null);">{{view.menuItems.f.label}}
         ({{view.parentView.filteredContent.length}})</a>
-      <ul {{bindAttr class="view.parentView.hasFilteredItems::hidden 
:dropdown-menu"}}>
-        {{#each subMenuItem in view.menuItems.f.submenu}}
-          <li class="dropdown-submenu">
-            <a href="javascript:void(null);">{{subMenuItem.label}}</a>
+        <ul {{bindAttr class="view.parentView.hasFilteredItems::hidden 
:dropdown-menu"}}>
+          {{#view view.hostItemView}}
+            <a href="javascript:void(null);">{{view.label}}</a>
             <ul class="dropdown-menu">
-              {{#each menuL3Item in subMenuItem.submenu}}
-                <li>
-                  <a {{action "bulkOperationConfirm" menuL3Item.operationData 
target="view.parentView"}} href="#">{{menuL3Item.label}}</a>
-                </li>
+              {{#each operation in view.operationsInfo}}
+                {{#if operation.label.length}}
+                  <li>
+                    {{#view view.operationView 
contentBinding="operation.operationData" selection="f"}}
+                      <a href="javascript:void(null);">{{operation.label}}</a>
+                    {{/view}}
+                  </li>
+                {{/if}}
               {{/each}}
             </ul>
-          </li>
-        {{/each}}
-      </ul>
+          {{/view}}
+          {{#each component in view.components}}
+              {{#view view.slaveItemView contentBinding="component"}}
+                <a 
href="javascript:void(null);">{{component.componentNameFormatted}}</a>
+                  <ul class="dropdown-menu">
+                    {{#each operation in view.operationsInfo}}
+                      {{#if operation.decommission}}
+                        {{#view view.advancedOperationView 
contentBinding="operation.operationData" selection="f"}}
+                        <a 
href="javascript:void(null);">{{operation.label}}</a>
+                        {{/view}}
+                      {{else}}
+                        {{#view view.commonOperationView 
contentBinding="operation.operationData" selection="f"}}
+                        <a 
href="javascript:void(null);">{{operation.label}}</a>
+                        {{/view}}
+                      {{/if}}
+                    {{/each}}
+                  </ul>
+              {{/view}}
+          {{/each}}
+        </ul>
     </li>
     <li class="dropdown-submenu">
       <a tabindex="-1" href="javascript:void(null);">{{view.menuItems.a.label}}
         ({{view.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"}} href="#">{{menuL3Item.label}}</a>
-                </li>
-              {{/each}}
-            </ul>
-          </li>
-        {{/each}}
-      </ul>
+        <ul class="dropdown-menu">
+          {{#view view.hostItemView}}
+              <a href="javascript:void(null);">{{view.label}}</a>
+              <ul class="dropdown-menu">
+                {{#each operation in view.operationsInfo}}
+                  {{#if operation.label.length}}
+                      <li>
+                        {{#view view.operationView 
contentBinding="operation.operationData" selection="a"}}
+                            <a 
href="javascript:void(null);">{{operation.label}}</a>
+                        {{/view}}
+                      </li>
+                  {{/if}}
+                {{/each}}
+              </ul>
+          {{/view}}
+          {{#each component in view.components}}
+            {{#view view.slaveItemView contentBinding="component"}}
+                <a 
href="javascript:void(null);">{{component.componentNameFormatted}}</a>
+                <ul class="dropdown-menu">
+                  {{#each operation in view.operationsInfo}}
+                    {{#if operation.decommission}}
+                      {{#view view.advancedOperationView 
contentBinding="operation.operationData" selection="a"}}
+                          <a 
href="javascript:void(null);">{{operation.label}}</a>
+                      {{/view}}
+                    {{else}}
+                      {{#view view.commonOperationView 
contentBinding="operation.operationData" selection="a"}}
+                          <a 
href="javascript:void(null);">{{operation.label}}</a>
+                      {{/view}}
+                    {{/if}}
+                  {{/each}}
+                </ul>
+            {{/view}}
+          {{/each}}
+        </ul>
     </li>
   </ul>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/ambari-web/app/templates/main/host/decommission.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/decommission.hbs 
b/ambari-web/app/templates/main/host/decommission.hbs
new file mode 100644
index 0000000..03d5937
--- /dev/null
+++ b/ambari-web/app/templates/main/host/decommission.hbs
@@ -0,0 +1,25 @@
+{{!
+* 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.
+}}
+
+
+<li rel='decommissionTooltip' {{bindAttr 
data-original-title="view.parentView.decommissionTooltipMessage" 
class="view.parentView.noActionAvailable"}}>
+    <a href="javascript:void(null)" data-toggle="modal" {{bindAttr 
class="view.parentView.isComponentDecommissionDisable:disabled"}}>
+      {{view.text}}
+    </a>
+</li>
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/ambari-web/app/templates/main/host/details/host_component.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/details/host_component.hbs 
b/ambari-web/app/templates/main/host/details/host_component.hbs
index b21516d..ee28fde 100644
--- a/ambari-web/app/templates/main/host/details/host_component.hbs
+++ b/ambari-web/app/templates/main/host/details/host_component.hbs
@@ -50,18 +50,10 @@
       </a>
       <ul class="dropdown-menu">
         {{#if view.isComponentDecommissionAvailable}}
-          <li {{bindAttr class="view.noActionAvailable"}}>
-            <a href="javascript:void(null)" data-toggle="modal" {{action 
"decommission" view.content target="controller"}}>
-              {{t common.decommission}}
-            </a>
-          </li>
+          {{view view.decommissionView}}
         {{/if}}
         {{#if view.isComponentRecommissionAvailable}}
-          <li {{bindAttr class="view.noActionAvailable"}}>
-            <a href="javascript:void(null)" data-toggle="modal" {{action 
"recommission" view.content target="controller"}}>
-              {{t common.recommission}}
-            </a>
-          </li>
+          {{view view.decommissionView}}
         {{/if}}
         {{#if view.isRestartableComponent}}
           <li {{bindAttr class="view.isRestartComponentDisabled:hidden"}}>

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/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 d1bc1e8..f9d0f0f 100644
--- a/ambari-web/app/views/main/host.js
+++ b/ambari-web/app/views/main/host.js
@@ -110,11 +110,10 @@ App.MainHostView = App.TableView.extend({
   /**
    * Confirmation Popup for bulk Operations
    */
-  bulkOperationConfirm: function(event) {
-    var operationData = event.context;
+  bulkOperationConfirm: function(operationData, selection) {
     var hosts = [];
     var self = this;
-    switch(operationData.selection) {
+    switch(selection) {
       case 's':
         hosts = this.get('content').filterProperty('selected');
         break;

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/ambari-web/app/views/main/host/details/host_component_views/regionserver_view.js
----------------------------------------------------------------------
diff --git 
a/ambari-web/app/views/main/host/details/host_component_views/regionserver_view.js
 
b/ambari-web/app/views/main/host/details/host_component_views/regionserver_view.js
index 3d3720b..beab003 100644
--- 
a/ambari-web/app/views/main/host/details/host_component_views/regionserver_view.js
+++ 
b/ambari-web/app/views/main/host/details/host_component_views/regionserver_view.js
@@ -20,6 +20,7 @@ var App = require('app');
 
 App.RegionServerComponentView = 
App.HostComponentView.extend(App.Decommissionable, {
 
+  componentForCheckDecommission: 'HBASE_MASTER',
   /**
    * load Recommission/Decommission status of RegionServer
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/15013b71/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 c0638bf..819cd8e 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
@@ -22,195 +22,285 @@ App.HostTableMenuView = Em.View.extend({
 
   templateName: require('templates/main/host/bulk_operation_menu'),
 
+  menuItems: function () {
+    return {
+      s: {label: Em.I18n.t('hosts.table.menu.l1.selectedHosts')},
+      f: {label: Em.I18n.t('hosts.table.menu.l1.filteredHosts')},
+      a: {label: Em.I18n.t('hosts.table.menu.l1.allHosts')}
+    };
+  }.property('App.router.clusterController.isLoaded'),
+
+  components: function(){
+    var menuItems = [
+    Em.Object.create({
+      serviceName: 'HDFS',
+      componentName: 'DATANODE',
+      masterComponentName: 'NAMENODE',
+      componentNameFormatted: Em.I18n.t('dashboard.services.hdfs.datanodes')
+    }),
+    Em.Object.create({
+      serviceName: 'YARN',
+      componentName: 'NODEMANAGER',
+      masterComponentName: 'RESOURCEMANAGER',
+      componentNameFormatted: Em.I18n.t('dashboard.services.yarn.nodeManagers')
+    }),
+    Em.Object.create({
+      serviceName: 'HBASE',
+      componentName: 'HBASE_REGIONSERVER',
+      masterComponentName: 'HBASE_MASTER',
+      componentNameFormatted: 
Em.I18n.t('dashboard.services.hbase.regionServers')
+    }),
+    Em.Object.create({
+      serviceName: 'MAPREDUCE',
+      componentName: 'TASKTRACKER',
+      masterComponentName: 'JOBTRACKER',
+      componentNameFormatted: 
Em.I18n.t('dashboard.services.mapreduce.taskTrackers')
+    }),
+    Em.Object.create({
+      serviceName: 'STORM',
+      componentName: 'SUPERVISOR',
+      masterComponentName: 'SUPERVISOR',
+      componentNameFormatted: Em.I18n.t('dashboard.services.storm.supervisors')
+    })];
+
+    return menuItems.filter(function(item){
+      return App.Service.find().findProperty('serviceName',item.serviceName);
+    });
+  }.property(),
+
+
   /**
-   * Get third-level menu items for slave components
-   * @param {String} componentNameForDecommission for decommission and 
recommission used another component name
-   * @param {String} componentNameForOtherActions host component name that 
should be processed
-   * operationData format:
-   * <code>
-   *  {
-   *    action: 'STARTED|INSTALLED|RESTART|DECOMMISSION|DECOMMISSION_OFF', // 
action for selected host components
-   *    message: 'some text', // just text to BG popup
-   *    componentName: 'DATANODE|NODEMANAGER...', //component name that should 
be processed
-   *    realComponentName: 'DATANODE|NODEMANAGER...', // used only for 
decommission(_off) actions
-   *    serviceName: 'HDFS|YARN|HBASE...', // service name of the processed 
component
-   *    componentNameFormatted: 'DataNodes|NodeManagers...' // "user-friendly" 
string with component name (used in BG popup)
-   *  }
-   *  </code>
-   *
-   * @returns {Array}
+   * slaveItemView build second-level menu
+   * for slave component
    */
-  getSlaveItemsTemplate: function(componentNameForDecommission, 
componentNameForOtherActions) {
-    var menuItems = Em.A([
-      Em.Object.create({
-        label: Em.I18n.t('common.start'),
-        operationData: Em.Object.create({
-          action: App.HostComponentStatus.started,
-          message: Em.I18n.t('common.start'),
-          componentName: componentNameForOtherActions
-        })
-      }),
-      Em.Object.create({
-        label: Em.I18n.t('common.stop'),
-        operationData: Em.Object.create({
-          action: App.HostComponentStatus.stopped,
-          message: Em.I18n.t('common.stop'),
-          componentName: componentNameForOtherActions
-        })
-      }),
-      Em.Object.create({
-        label: Em.I18n.t('common.restart'),
-        operationData: Em.Object.create({
-          action: 'RESTART',
-          message: Em.I18n.t('common.restart'),
-          componentName: componentNameForOtherActions
-        })
-      })
-    ]);
-    
if(App.get('components.decommissionAllowed').contains(componentNameForOtherActions))
 {
-      menuItems.pushObject(Em.Object.create({
-        label: Em.I18n.t('common.decommission'),
-        operationData: Em.Object.create({
-          action: 'DECOMMISSION',
-          message: Em.I18n.t('common.decommission'),
-          componentName: componentNameForDecommission,
-          realComponentName: componentNameForOtherActions
-        })
-      }));
-      menuItems.pushObject(Em.Object.create({
-        label: Em.I18n.t('common.recommission'),
-        operationData: Em.Object.create({
-          action: 'DECOMMISSION_OFF',
-          message: Em.I18n.t('common.recommission'),
-          componentName: componentNameForDecommission,
-          realComponentName: componentNameForOtherActions
+
+  slaveItemView: Em.View.extend({
+
+    tagName: 'li',
+
+    classNames: ['dropdown-submenu'],
+
+    /**
+     * Get third-level menu items ingo for slave components
+     * operationData format:
+     * <code>
+     *  {
+     *    action: 'STARTED|INSTALLED|RESTART|DECOMMISSION|DECOMMISSION_OFF', 
// action for selected host components
+     *    message: 'some text', // just text to BG popup
+     *    componentName: 'DATANODE|NODEMANAGER...', //component name that 
should be processed
+     *    realComponentName: 'DATANODE|NODEMANAGER...', // used only for 
decommission(_off) actions
+     *    serviceName: 'HDFS|YARN|HBASE...', // service name of the processed 
component
+     *    componentNameFormatted: 'DataNodes|NodeManagers...' // 
"user-friendly" string with component name (used in BG popup)
+     *  }
+     *  </code>
+     *
+     * @returns {Array}
+     */
+
+    operationsInfo: function () {
+      var content = this.get('content');
+      var menuItems = Em.A([
+        Em.Object.create({
+          label: Em.I18n.t('common.start'),
+          operationData: Em.Object.create({
+            action: App.HostComponentStatus.started,
+            message: Em.I18n.t('common.start'),
+            componentName: content.componentName,
+            serviceName: content.serviceName,
+            componentNameFormatted: content.componentNameFormatted
+          })
+        }),
+        Em.Object.create({
+          label: Em.I18n.t('common.stop'),
+          operationData: Em.Object.create({
+            action: App.HostComponentStatus.stopped,
+            message: Em.I18n.t('common.stop'),
+            componentName: content.componentName,
+            serviceName: content.serviceName,
+            componentNameFormatted: content.componentNameFormatted
+          })
+        }),
+        Em.Object.create({
+          label: Em.I18n.t('common.restart'),
+          operationData: Em.Object.create({
+            action: 'RESTART',
+            message: Em.I18n.t('common.restart'),
+            componentName: content.componentName,
+            serviceName: content.serviceName,
+            componentNameFormatted: content.componentNameFormatted
+          })
         })
-      }));
-    }
-    return menuItems;
-  },
+      ]);
+      if 
(App.get('components.decommissionAllowed').contains(content.componentName)) {
+        menuItems.pushObject(Em.Object.create({
+          label: Em.I18n.t('common.decommission'),
+          decommission: true,
+          operationData: Em.Object.create({
+            action: 'DECOMMISSION',
+            message: Em.I18n.t('common.decommission'),
+            componentName: content.masterComponentName,
+            realComponentName: content.componentName,
+            serviceName: content.serviceName,
+            componentNameFormatted: content.componentNameFormatted
+          })
+        }));
+        menuItems.pushObject(Em.Object.create({
+          label: Em.I18n.t('common.recommission'),
+          decommission: true,
+          operationData: Em.Object.create({
+            action: 'DECOMMISSION_OFF',
+            message: Em.I18n.t('common.recommission'),
+            componentName: content.masterComponentName,
+            realComponentName: content.componentName,
+            serviceName: content.serviceName,
+            componentNameFormatted: content.componentNameFormatted
+          })
+        }));
+      }
+      return menuItems;
+    }.property("content"),
+
+    /**
+     * commonOperationView is used for third-level menu items
+     * for simple operations ('START','STOP','RESTART')
+     */
+    commonOperationView: Em.View.extend({
+      tagName: 'li',
+
+      /**
+       * click function use
+       * App.MainHostView as a thirl level parent
+       * and runs it's function
+       */
+      click: function () {
+        
this.get('parentView.parentView.parentView').bulkOperationConfirm(this.get('content'),
 this.get('selection'));
+      }
+    }),
+
+    /**
+     * advancedOperationView is used for third level menu item
+     * for advanced operations ('RECOMMISSION','DECOMMISSION')
+     */
+    advancedOperationView: Em.View.extend({
+      tagName: 'li',
+      rel: 'menuTooltip',
+      classNameBindings: ['disabledElement'],
+      attributeBindings: ['tooltipMsg:data-original-title'],
+
+      service: function () {
+        return 
App.router.get('mainServiceController.content').findProperty('serviceName', 
this.get('content.serviceName'))
+      }.property('App.router.mainServiceController.content.@each', 'content'),
+
+      tooltipMsg: function () {
+        return (this.get('disabledElement') == 'disabled') ?
+           
Em.I18n.t('hosts.decommission.tooltip.warning').format(this.get('content.message'),
  App.format.role(this.get('content.componentName'))) : '';
+      }.property('disabledElement','content.componentName'),
+
+      disabledElement: function () {
+        return (this.get('service.workStatus') != 'STARTED') ? 'disabled' : '';
+      }.property('service.workStatus'),
+
+      /**
+       * click function use
+       * App.MainHostView as a thirl level parent
+       * and runs it's function
+       */
+      click: function () {
+        if (this.get('disabledElement') == 'disabled') {
+          return;
+        }
+        
this.get('parentView.parentView.parentView').bulkOperationConfirm(this.get('content'),
 this.get('selection'));
+      },
+
+      didInsertElement: function () {
+        App.tooltip($(this.get('element')));
+      }
+    })
+  }),
 
   /**
-   * Get third-level menu items for Hosts
-   * operationData format:
-   * <code>
-   *  {
-   *    action: 'STARTED|INSTALLED|RESTART..', // action for selected hosts 
(will be applied for each host component in selected hosts)
-   *    actionToCheck: 'INSTALLED|STARTED..' // state to filter host 
components should be processed
-   *    message: 'some text', // just text to BG popup
-   *  }
-   *  </code>
-   * @returns {Array}
+   * hostItemView build second-level menu
+   * for host
    */
-  getHostItemsTemplate: function() {
-    return Em.A([
-      Em.Object.create({
-        label: Em.I18n.t('hosts.host.details.startAllComponents'),
-        operationData: Em.Object.create({
-          action: 'STARTED',
-          actionToCheck: 'INSTALLED',
-          message: Em.I18n.t('hosts.host.details.startAllComponents')
-        })
-      }),
-      Em.Object.create({
-        label: Em.I18n.t('hosts.host.details.stopAllComponents'),
-        operationData: Em.Object.create({
-          action: 'INSTALLED',
-          actionToCheck: 'STARTED',
-          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',
-          message: Em.I18n.t('hosts.table.menu.l2.restartAllComponents')
-        })
-      }),
-      Em.Object.create({
-        label: Em.I18n.t('passiveState.turnOn'),
-        operationData: Em.Object.create({
-          state: 'ON',
-          action: 'PASSIVE_STATE',
-          message: Em.I18n.t('passiveState.turnOnFor').format('hosts')
-        })
-      }),
-      Em.Object.create({
-        label: Em.I18n.t('passiveState.turnOff'),
-        operationData: Em.Object.create({
-          state: 'OFF',
-          action: 'PASSIVE_STATE',
-          message: Em.I18n.t('passiveState.turnOffFor').format('hosts')
+
+  hostItemView: Em.View.extend({
+
+    tagName: 'li',
+
+    classNames: ['dropdown-submenu'],
+
+    label: Em.I18n.t('common.hosts'),
+
+    /** Get third-level menu items for Hosts
+     * operationData format:
+     * <code>
+     *  {
+     *    action: 'STARTED|INSTALLED|RESTART..', // action for selected hosts 
(will be applied for each host component in selected hosts)
+     *    actionToCheck: 'INSTALLED|STARTED..' // state to filter host 
components should be processed
+     *    message: 'some text', // just text to BG popup
+     *  }
+     *  </code>
+     * @returns {Array}
+     */
+    operationsInfo: function () {
+      return Em.A([
+        Em.Object.create({
+          label: Em.I18n.t('hosts.host.details.startAllComponents'),
+          operationData: Em.Object.create({
+            action: 'STARTED',
+            actionToCheck: 'INSTALLED',
+            message: Em.I18n.t('hosts.host.details.startAllComponents')
+          })
+        }),
+        Em.Object.create({
+          label: Em.I18n.t('hosts.host.details.stopAllComponents'),
+          operationData: Em.Object.create({
+            action: 'INSTALLED',
+            actionToCheck: 'STARTED',
+            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',
+            message: Em.I18n.t('hosts.table.menu.l2.restartAllComponents')
+          })
+        }),
+        Em.Object.create({
+          label: Em.I18n.t('passiveState.turnOn'),
+          operationData: Em.Object.create({
+            state: 'ON',
+            action: 'PASSIVE_STATE',
+            message: Em.I18n.t('passiveState.turnOnFor').format('hosts')
+          })
+        }),
+        Em.Object.create({
+          label: Em.I18n.t('passiveState.turnOff'),
+          operationData: Em.Object.create({
+            state: 'OFF',
+            action: 'PASSIVE_STATE',
+            message: Em.I18n.t('passiveState.turnOffFor').format('hosts')
+          })
         })
-      })
-    ]);
-  },
+      ]);
+    }.property(),
 
-  /**
-   * Get second-level menu
-   * @param {String} selection
-   * <code>
-   *   "s" - selected hosts
-   *   "a" - all hosts
-   *   "f" - filtered hosts
-   * </code>
-   * @returns {Array}
-   */
-  getSubMenuItemsTemplate: function(selection) {
-    var submenu = Em.A([{label: Em.I18n.t('common.hosts'), submenu: 
this.getHostItemsTemplate()}]);
-
-    if (!!App.HDFSService.find().content.length) {
-      var slaveItemsForHdfs = this.getSlaveItemsTemplate('NAMENODE', 
'DATANODE');
-      slaveItemsForHdfs.setEach('operationData.serviceName', 'HDFS');
-      slaveItemsForHdfs.setEach('operationData.componentNameFormatted', 
Em.I18n.t('dashboard.services.hdfs.datanodes'));
-      submenu.push({label: Em.I18n.t('dashboard.services.hdfs.datanodes'), 
submenu: slaveItemsForHdfs});
-    }
-
-    if (!!App.YARNService.find().content.length) {
-      var slaveItemsForYarn = this.getSlaveItemsTemplate('RESOURCEMANAGER', 
'NODEMANAGER');
-      slaveItemsForYarn.setEach('operationData.serviceName', 'YARN');
-      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('HBASE_MASTER', 
'HBASE_REGIONSERVER');
-      slaveItemsForHBase.setEach('operationData.serviceName', 'HBASE');
-      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('JOBTRACKER', 
'TASKTRACKER');
-      slaveItemsForMapReduce.setEach('operationData.serviceName', 'MAPREDUCE');
-      slaveItemsForMapReduce.setEach('operationData.componentNameFormatted', 
Em.I18n.t('dashboard.services.mapreduce.taskTrackers'));
-      submenu.push({label: 
Em.I18n.t('dashboard.services.mapreduce.taskTrackers'), submenu: 
slaveItemsForMapReduce});
-    }
-
-    if (!!App.Service.find().filterProperty('serviceName', 'STORM').length) {
-      var slaveItemsForStorm = this.getSlaveItemsTemplate('SUPERVISOR', 
'SUPERVISOR');
-      slaveItemsForStorm.setEach('operationData.serviceName', 'STORM');
-      slaveItemsForStorm.setEach('operationData.componentNameFormatted', 
Em.I18n.t('dashboard.services.storm.supervisors'));
-      submenu.push({label: Em.I18n.t('dashboard.services.storm.supervisors'), 
submenu: slaveItemsForStorm});
-    }
-
-    submenu.forEach(function(item) {
-      item.submenu.forEach(function(subitem) {
-        subitem.operationData.selection = selection;
-      });
-    });
-    return submenu;
-  },
+    /**
+     * commonOperationView is used for third-level menu items
+     * for all operations for host
+     */
+    operationView: Em.View.extend({
+      tagName: 'li',
 
-  /**
-   * Menu-items for Hosts table
-   * @type {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')
+      /**
+       * click function use
+       * App.MainHostView as a thirl level parent
+       * and runs it's function
+       */
+      click: function () {
+        
this.get('parentView.parentView.parentView').bulkOperationConfirm(this.get('content'),
 this.get('selection'));
+      }
+    })
+  })
 });

Reply via email to