Переглянути джерело

AMBARI-6720. Include 'Add HBase Master' and 'Add ZooKeeper Server' in Service Actions menu

Srimanth Gunturi 10 роки тому
батько
коміт
55c4401c99

+ 4 - 56
ambari-web/app/controllers/main/host/details.js

@@ -18,6 +18,7 @@
 
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 
 App.MainHostDetailsController = Em.Controller.extend({
 
@@ -161,8 +162,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method ajaxErrorCallback
    */
   ajaxErrorCallback: function (request, ajaxOptions, error, opt, params) {
-    console.log('error on change component host status');
-    App.ajax.defaultErrorHandler(request, opt.url, opt.method);
+    return componentsUtils.ajaxErrorCallback(request, ajaxOptions, error, opt, params);
   },
   /**
    * mimic status transition in test mode
@@ -477,61 +477,9 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method primary
    */
   primary: function (component) {
-    var self = this;
-    var componentName = component.get('componentName');
-    var displayName = component.get('displayName');
-    App.ajax.send({
-      name: 'host.host_component.add_new_component',
-      sender: self,
-      data: {
-        hostName: self.get('content.hostName'),
-        component: component,
-        data: JSON.stringify({
-          RequestInfo: {
-            "context": Em.I18n.t('requestInfo.installHostComponent') + " " + displayName
-          },
-          Body: {
-            host_components: [
-              {
-                HostRoles: {
-                  component_name: componentName
-                }
-              }
-            ]
-          }
-        })
-      },
-      success: 'addNewComponentSuccessCallback',
-      error: 'ajaxErrorCallback'
-    });
-  },
 
-  /**
-   * Success callback for add host component request
-   * @param {object} data
-   * @param {object} opt
-   * @param {object} params
-   * @method addNewComponentSuccessCallback
-   */
-  addNewComponentSuccessCallback: function (data, opt, params) {
-    console.log('Send request for ADDING NEW COMPONENT successfully');
-    App.ajax.send({
-      name: 'common.host.host_component.update',
-      sender: this,
-      data: {
-        hostName: this.get('content.hostName'),
-        componentName: params.component.get('componentName'),
-        serviceName: params.component.get('serviceName'),
-        component: params.component,
-        "context": Em.I18n.t('requestInfo.installNewHostComponent') + " " + params.component.get('displayName'),
-        HostRoles: {
-          state: 'INSTALLED'
-        },
-        urlParams: "HostRoles/state=INIT"
-      } ,
-      success: 'installNewComponentSuccessCallback',
-      error: 'ajaxErrorCallback'
-    });
+    var self = this;
+    componentsUtils.installHostComponent(self.get('content.hostName'), component);
   },
 
   /**

+ 162 - 0
ambari-web/app/controllers/main/service/item.js

@@ -18,6 +18,7 @@
 
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
+var componentsUtils = require('utils/components');
 
 App.MainServiceItemController = Em.Controller.extend({
   name: 'mainServiceItemController',
@@ -44,6 +45,39 @@ App.MainServiceItemController = Em.Controller.extend({
     }
   },
 
+  initHosts: function() {
+    if (App.get('components.masters').length !== 0) {
+      var self = this;
+
+      var hostNames = App.Host.find().mapProperty('hostName');
+      this.set('allHosts', hostNames);
+
+      ['HBASE_MASTER', 'ZOOKEEPER_SERVER'].forEach(function(componentName) {
+        self.loadHostsWithoutComponent(componentName);
+      });
+    }
+  }.observes('App.components.masters', 'content.hostComponents.length'),
+
+  loadHostsWithoutComponent: function (componentName) {
+    var hostsWithComponent = App.HostComponent.find().filterProperty('componentName', componentName).mapProperty('hostName');
+
+    var hostsWithoutComponent = this.get('allHosts').filter(function(hostName) {
+      return !hostsWithComponent.contains(hostName);
+    });
+
+
+    if (componentName === 'HBASE_MASTER') {
+      this.set('hostsWithoutHBaseMaster', hostsWithoutComponent);
+      var disabledMsg = Em.I18n.t('services.summary.allHostsAlreadyRunComponent').format(Em.I18n.t('dashboard.services.hbase.masterServer'));
+      this.set('addHBaseMasterDisabledMsg', disabledMsg);
+    }
+    if (componentName === 'ZOOKEEPER_SERVER') {
+      this.set('hostsWithoutZookeeperServer', hostsWithoutComponent);
+      var disabledMsg = Em.I18n.t('services.summary.allHostsAlreadyRunComponent').format(Em.I18n.t('dashboard.services.zookeeper.server'));
+      this.set('addZookeeperServerDisabledMsg', disabledMsg);
+    }
+  },
+
   /**
    * flag to control router switch between service summary and configs
    * @type {boolean}
@@ -58,6 +92,36 @@ App.MainServiceItemController = Em.Controller.extend({
     return !App.get('services.noConfigTypes').concat('HCATALOG').contains(this.get('content.serviceName'));
   }.property('App.services.noConfigTypes','content.serviceName'),
 
+  isAddHBaseMasterDisabled: function() {
+    return this.get('hostsWithoutHBaseMaster').length === 0;
+  }.property('hostsWithoutHBaseMaster'),
+
+  addHBaseMasterDisabledMsg: null,
+
+  addHBaseMasterDisabledTooltip: function() {
+    if (this.get('isAddHBaseMasterDisabled')) {
+      return this.get('addHBaseMasterDisabledMsg');
+    }
+  }.property('isAddHBaseMasterDisabled', 'addHBaseMasterDisabledMsg'),
+
+  hostsWithoutHBaseMaster: [],
+
+  isAddZooKeeperServerDisabled: function() {
+    return this.get('hostsWithoutZookeeperServer').length === 0;
+  }.property('hostsWithoutZookeeperServer'),
+
+  addZookeeperServerDisabledMsg: null,
+
+  addZooKeeperServerDisabledTooltip: function() {
+    if (this.get('isAddZooKeeperServerDisabled')) {
+      return this.get('addZookeeperServerDisabledMsg');
+    }
+  }.property('isAddZooKeeperServerDisabled', 'addZookeeperServerDisabledMsg'),
+
+  hostsWithoutZookeeperServer: [],
+
+  allHosts: [],
+
   /**
    * Common method for ajax (start/stop service) responses
    * @param data
@@ -381,6 +445,104 @@ App.MainServiceItemController = Em.Controller.extend({
     }
   },
 
+  addHbaseMaster: function () {
+    this.addClientComponent('HBASE_MASTER');
+  },
+
+  addZooKeeperServer: function () {
+    this.addClientComponent('ZOOKEEPER_SERVER');
+  },
+
+  /**
+   * Send command to server to install client on selected host
+   * @param componentName
+   */
+  addClientComponent: function (componentName) {
+    var self = this;
+    var component = App.HostComponent.find().findProperty('componentName', componentName);
+    var componentDisplayName = component.get('displayName');
+
+    self.loadHostsWithoutComponent(componentName);
+
+    return App.ModalPopup.show({
+      primary: function() {
+        if (this.get('anyHostsWithoutComponent')) {
+          return Em.I18n.t('hosts.host.addComponent.popup.confirm')
+        } else {
+          return undefined;
+        }
+      }.property('anyHostsWithoutComponent'),
+
+      header: Em.I18n.t('popup.confirmation.commonHeader'),
+
+      addComponentMsg: function () {
+        return Em.I18n.t('hosts.host.addComponent.msg').format(componentDisplayName);
+      }.property(),
+
+      selectHostMsg: function () {
+        return Em.I18n.t('services.summary.selectHostForComponent').format(this.get('componentDisplayName'))
+      }.property('componentDisplayName'),
+
+      thereIsNoHostsMsg: function () {
+        return Em.I18n.t('services.summary.allHostsAlreadyRunComponent').format(this.get('componentDisplayName'))
+      }.property('componentDisplayName'),
+
+      hostsWithoutComponent: function() {
+        if (this.get('componentName') === 'HBASE_MASTER') {
+          return self.hostsWithoutHBaseMaster;
+        }
+        if (this.get('componentName') === 'ZOOKEEPER_SERVER') {
+          return self.hostsWithoutZookeeperServer;
+        }
+      }.property('componentName', 'self.hostsWithoutHBaseMaster', 'self.hostsWithoutZookeeperServer'),
+
+      anyHostsWithoutComponent: function() {
+        return this.get('hostsWithoutComponent').length > 0
+      }.property('hostsWithoutComponent'),
+
+      selectedHost: null,
+
+      componentName: function() {
+        return componentName;
+      }.property(),
+
+      componentDisplayName: function() {
+        return componentDisplayName;
+      }.property(),
+
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/service/add_host_popup')
+      }),
+
+      restartNagiosMsg: Em.View.extend({
+        template: Em.Handlebars.compile(Em.I18n.t('hosts.host.addComponent.note').format(componentDisplayName))
+      }),
+
+      onPrimary: function () {
+        var selectedHost = this.get('selectedHost');
+
+        // Install
+        componentsUtils.installHostComponent(selectedHost, component);
+
+        // Remove host from 'without' collection to immediate recalculate add menu item state
+        var hostsWithoutComponent = this.get('hostsWithoutComponent');
+        var index = hostsWithoutComponent.indexOf(this.get('selectedHost'));
+        if (index > -1) {
+          hostsWithoutComponent.splice(index, 1);
+        }
+
+        if (this.get('componentName') === 'HBASE_MASTER') {
+          self.set('hostsWithoutHBaseMaster', hostsWithoutComponent)
+        }
+        if (this.get('componentName') === 'ZOOKEEPER_SERVER') {
+          self.set('hostsWithoutZookeeperServer', hostsWithoutComponent)
+        }
+
+        this.hide();
+      }
+    });
+  },
+
   /**
    * set property isPending (if this property is true - means that service has task in BGO)
    * and this makes start/stop button disabled

+ 5 - 0
ambari-web/app/messages.js

@@ -340,6 +340,9 @@ Em.I18n.translations = {
   'services.alerts.goToService': 'Go to Service',
   'services.alerts.goToNagios': 'Go to Nagios Web UI',
 
+  'services.summary.selectHostForComponent': 'Select the host to add {0} component',
+  'services.summary.allHostsAlreadyRunComponent': 'All hosts already running {0} component',
+
   'topnav.logo.href':'/#/main/dashboard',
   'topnav.help.href':'https://cwiki.apache.org/confluence/display/AMBARI/Ambari',
 
@@ -1950,6 +1953,8 @@ Em.I18n.translations = {
   'dashboard.services.configs.popup.restartService.header' : 'Restart service',
   'dashboard.services.configs.popup.restartService.body' : 'Service needs to be restarted for reconfiguration',
 
+  'dashboard.services.zookeeper.server' : 'ZooKeeper Server',
+
   'dashboard.configHistory.title': 'Configs',
   'dashboard.configHistory.table.version.title' : 'Service: version',
   'dashboard.configHistory.table.modified.title' : 'Modified',

+ 29 - 0
ambari-web/app/templates/main/service/add_host_popup.hbs

@@ -0,0 +1,29 @@
+{{!
+* 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 anyHostsWithoutComponent}}
+  {{selectHostMsg}}<br />
+  <div>
+    {{view Ember.Select contentBinding="hostsWithoutComponent" selectionBinding="selectedHost"}}
+  </div>
+
+  {{addComponentMsg}}<br /><br />
+  <div class='alert'>{{view restartNagiosMsg}}</div>
+{{else}}
+  {{thereIsNoHostsMsg}}
+{{/if}}

+ 1 - 1
ambari-web/app/templates/main/service/item.hbs

@@ -84,7 +84,7 @@
         <!-- Other service actions -->
         {{#each option in view.maintenance}}
         <li {{bindAttr class="option.disabled:disabled option.isHidden:hidden"}}>
-          <a {{action "doAction" option target="controller" href=true}}><i {{bindAttr class="option.cssClass"}}></i>
+          <a {{action "doAction" option target="controller" href=true}} {{bindAttr data-title="option.tooltip"}} rel="HealthTooltip"><i {{bindAttr class="option.cssClass"}}></i>
 {{option.label}}</a>
         </li>
         {{/each}}

+ 5 - 0
ambari-web/app/utils/ajax/ajax.js

@@ -214,6 +214,11 @@ var urls = {
     }
   },
 
+  'common.host.with_host_component': {
+    'real': '/clusters/{clusterName}/hosts?host_components/HostRoles/component_name={componentName}&minimal_response=true',
+    'mock': ''
+  },
+
   'common.delete.host': {
     'real': '/clusters/{clusterName}/hosts/{hostName}',
     'type': 'DELETE'

+ 92 - 0
ambari-web/app/utils/components.js

@@ -0,0 +1,92 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+module.exports = {
+  installHostComponent: function(hostName, component) {
+    var self = this;
+    var componentName = component.get('componentName');
+    var displayName = component.get('displayName');
+    App.ajax.send({
+      name: 'host.host_component.add_new_component',
+      sender: self,
+      data: {
+        hostName: hostName,
+        component: component,
+        data: JSON.stringify({
+          RequestInfo: {
+            "context": Em.I18n.t('requestInfo.installHostComponent') + " " + displayName
+          },
+          Body: {
+            host_components: [
+              {
+                HostRoles: {
+                  component_name: componentName
+                }
+              }
+            ]
+          }
+        })
+      },
+      success: 'addNewComponentSuccessCallback',
+      error: 'ajaxErrorCallback'
+    });
+  },
+
+  /**
+   * Success callback for add host component request
+   * @param {object} data
+   * @param {object} opt
+   * @param {object} params
+   * @method addNewComponentSuccessCallback
+   */
+  addNewComponentSuccessCallback: function (data, opt, params) {
+    console.log('Send request for ADDING NEW COMPONENT successfully');
+    App.ajax.send({
+      name: 'common.host.host_component.update',
+      sender: App.router.get('mainHostDetailsController'),
+      data: {
+        hostName: params.hostName,
+        componentName: params.component.get('componentName'),
+        serviceName: params.component.get('serviceName'),
+        component: params.component,
+        "context": Em.I18n.t('requestInfo.installNewHostComponent') + " " + params.component.get('displayName'),
+        HostRoles: {
+          state: 'INSTALLED'
+        },
+        urlParams: "HostRoles/state=INIT"
+      },
+      success: 'installNewComponentSuccessCallback',
+      error: 'ajaxErrorCallback'
+    });
+  },
+
+  /**
+   * Default error-callback for ajax-requests in current page
+   * @param {object} request
+   * @param {object} ajaxOptions
+   * @param {string} error
+   * @param {object} opt
+   * @param {object} params
+   * @method ajaxErrorCallback
+   */
+  ajaxErrorCallback: function (request, ajaxOptions, error, opt, params) {
+    console.log('error on change component host status');
+    App.ajax.defaultErrorHandler(request, opt.url, opt.method);
+  }
+};

+ 23 - 0
ambari-web/app/views/main/service/item.js

@@ -92,6 +92,20 @@ App.MainServiceItemView = Em.View.extend({
         label: Em.I18n.t('services.service.actions.reassign.master'),
         cssClass: 'icon-share-alt',
         disabled: false
+      },
+      ADD_HBASE_MASTER_COMPONENT: {
+        action: 'addHbaseMaster',
+        cssClass: 'icon-plus',
+        'label': '{0} {1}'.format(Em.I18n.t('add'), Em.I18n.t('dashboard.services.hbase.masterServer')),
+        disabled: this.get('controller.isAddHBaseMasterDisabled'),
+        tooltip: this.get('controller.addHBaseMasterDisabledTooltip')
+      },
+      ADD_ZOO_KEEPER_SERVER_COMPONENT: {
+        action: 'addZooKeeperServer',
+        cssClass: 'icon-plus',
+        'label': '{0} {1}'.format(Em.I18n.t('add'), Em.I18n.t('dashboard.services.zookeeper.server')),
+        disabled: this.get('controller.isAddZooKeeperServerDisabled'),
+        tooltip: this.get('controller.addZooKeeperServerDisabledTooltip')
       }
     }
   },
@@ -154,8 +168,17 @@ App.MainServiceItemView = Em.View.extend({
             break;
         }
       }
+
       options.push(actionMap.RUN_SMOKE_TEST);
       options.push(actionMap.TOGGLE_PASSIVE);
+
+      var serviceName = service.get('serviceName');
+      if (serviceName === 'HBASE') {
+        options.push(actionMap.ADD_HBASE_MASTER_COMPONENT);
+      }
+      if (serviceName === 'ZOOKEEPER') {
+        options.push(actionMap.ADD_ZOO_KEEPER_SERVER_COMPONENT);
+      }
     }
     return options;
   }.property('controller.content', 'controller.isStopDisabled','controller.isClientsOnlyService', 'controller.content.isRestartRequired', 'isPassive'),