This is an automated email from the ASF dual-hosted git repository. ababiichuk pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push: new 3c4f830 AMBARI-23052 Extend service actions dropdowns with namespace-specific items. (ababiichuk) 3c4f830 is described below commit 3c4f83048bd35e5b11838dfbdc11eb765f208cfc Author: aBabiichuk <ababiic...@hortonworks.com> AuthorDate: Fri Feb 23 19:49:13 2018 +0200 AMBARI-23052 Extend service actions dropdowns with namespace-specific items. (ababiichuk) --- ambari-web/app/app.js | 2 +- ambari-web/app/controllers/main/service/item.js | 281 +++++++++++++------ ambari-web/app/messages.js | 6 + ambari-web/app/models/host_component.js | 87 +++++- ambari-web/app/models/service.js | 6 + ambari-web/app/models/service/hdfs.js | 25 +- .../app/templates/main/service/info/summary.hbs | 2 +- ambari-web/app/templates/main/service/item.hbs | 73 ++--- ambari-web/app/utils/ajax/ajax.js | 26 ++ ambari-web/app/utils/batch_scheduled_requests.js | 45 +++- ambari-web/app/views/main/service/info/summary.js | 23 +- ambari-web/app/views/main/service/item.js | 298 +++++++++++---------- .../test/controllers/main/service/item_test.js | 87 +++--- ambari-web/test/views/main/service/item_test.js | 41 ++- 14 files changed, 641 insertions(+), 361 deletions(-) diff --git a/ambari-web/app/app.js b/ambari-web/app/app.js index d3d5bf9..2334cb2 100644 --- a/ambari-web/app/app.js +++ b/ambari-web/app/app.js @@ -243,7 +243,7 @@ module.exports = Em.Application.create({ }.property('router.clusterController.dataLoadList.services', 'router.clusterController.isServiceContentFullyLoaded'), hasNameNodeFederation: function () { - return App.HostComponent.find().filterProperty('componentName', 'NAMENODE').length > 2; + return App.HDFSService.find().objectAt(0).get('masterComponentGroups.length') > 1; }.property('router.clusterController.dataLoadList.services', 'router.clusterController.isServiceContentFullyLoaded'), /** diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js index 5bd160f..a4a8ca1 100644 --- a/ambari-web/app/controllers/main/service/item.js +++ b/ambari-web/app/controllers/main/service/item.js @@ -253,14 +253,15 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow startStopPopupSuccessCallback: function (data, ajaxOptions, params) { if (data && data.Requests) { params.query.set('status', 'SUCCESS'); - var config = this.get('callBackConfig')[(JSON.parse(ajaxOptions.data)).Body.ServiceInfo.state]; - var self = this; if (App.get('testMode')) { - self.set('content.workStatus', App.Service.Health[config.f]); - self.get('content.hostComponents').setEach('workStatus', App.HostComponentStatus[config.f]); - setTimeout(function () { - self.set('content.workStatus', App.Service.Health[config.c2]); - self.get('content.hostComponents').setEach('workStatus', App.HostComponentStatus[config.hs]); + const requestData = JSON.parse(ajaxOptions.data), + state = requestData.Body.ServiceInfo.state || requestData.Body.HostRoles.state, + config = this.get('callBackConfig')[state]; + this.set('content.workStatus', App.Service.Health[config.f]); + this.get('content.hostComponents').setEach('workStatus', App.HostComponentStatus[config.f]); + setTimeout(() => { + this.set('content.workStatus', App.Service.Health[config.c2]); + this.get('content.hostComponents').setEach('workStatus', App.HostComponentStatus[config.hs]); }, App.get('testModeDelayForActions')); } // load data (if we need to show this background operations popup) from persist @@ -279,41 +280,83 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow }, /** * Confirmation popup for start/stop services - * @param event * @param serviceHealth - 'STARTED' or 'INSTALLED' */ - startStopPopup: function(event, serviceHealth) { - if ($(event.target).hasClass('disabled') || $(event.target.parentElement).hasClass('disabled')) { - return; - } - var self = this; - var serviceDisplayName = this.get('content.displayName'); - var isMaintenanceOFF = this.get('content.passiveState') === 'OFF'; - - var msg = isMaintenanceOFF && serviceHealth == 'INSTALLED'? Em.I18n.t('services.service.stop.warningMsg.turnOnMM').format(serviceDisplayName) : null; - msg = self.addAdditionalWarningMessage(serviceHealth, msg, serviceDisplayName); - - var bodyMessage = Em.Object.create({ - putInMaintenance: (serviceHealth == 'INSTALLED' && isMaintenanceOFF) || (serviceHealth == 'STARTED' && !isMaintenanceOFF), - turnOnMmMsg: serviceHealth == 'INSTALLED' ? Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName) : Em.I18n.t('passiveState.turnOffFor').format(serviceDisplayName), - confirmMsg: serviceHealth == 'INSTALLED'? Em.I18n.t('services.service.stop.confirmMsg').format(serviceDisplayName) : Em.I18n.t('services.service.start.confirmMsg').format(serviceDisplayName), - confirmButton: serviceHealth == 'INSTALLED'? Em.I18n.t('services.service.stop.confirmButton') : Em.I18n.t('services.service.start.confirmButton'), - additionalWarningMsg: msg + startStopPopup: function (serviceHealth) { + const serviceDisplayName = this.get('content.displayName'), + isMaintenanceOFF = this.get('content.passiveState') === 'OFF'; + + let msg = isMaintenanceOFF && serviceHealth === 'INSTALLED' ? + Em.I18n.t('services.service.stop.warningMsg.turnOnMM').format(serviceDisplayName) : null; + msg = this.addAdditionalWarningMessage(serviceHealth, msg, serviceDisplayName); + + const bodyMessage = Em.Object.create({ + putInMaintenance: (serviceHealth === 'INSTALLED' && isMaintenanceOFF) || + (serviceHealth === 'STARTED' && !isMaintenanceOFF), + turnOnMmMsg: serviceHealth === 'INSTALLED' ? + Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName) : + Em.I18n.t('passiveState.turnOffFor').format(serviceDisplayName), + confirmMsg: serviceHealth === 'INSTALLED' + ? Em.I18n.t('services.service.stop.confirmMsg').format(serviceDisplayName) : + Em.I18n.t('services.service.start.confirmMsg').format(serviceDisplayName), + confirmButton: serviceHealth === 'INSTALLED' ? + Em.I18n.t('services.service.stop.confirmButton') : Em.I18n.t('services.service.start.confirmButton'), + additionalWarningMsg: msg }); // check HDFS NameNode checkpoint before stop service - if (this.get('content.serviceName') == 'HDFS' && serviceHealth == 'INSTALLED' && - this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) { + if (serviceHealth === 'INSTALLED' && this.hasStartedNameNode()) { this.checkNnLastCheckpointTime(function () { - return App.showConfirmationFeedBackPopup(function(query, runMmOperation) { - self.set('isPending', true); - self.startStopWithMmode(serviceHealth, query, runMmOperation); + return App.showConfirmationFeedBackPopup((query, runMmOperation) => { + this.set('isPending', true); + this.startStopWithMmode(serviceHealth, query, runMmOperation); }, bodyMessage); }); } else { - return App.showConfirmationFeedBackPopup(function(query, runMmOperation) { - self.set('isPending', true); - self.startStopWithMmode(serviceHealth, query, runMmOperation); + return App.showConfirmationFeedBackPopup((query, runMmOperation) => { + this.set('isPending', true); + this.startStopWithMmode(serviceHealth, query, runMmOperation); + }, bodyMessage); + } + }, + + /** + * Confirmation popup for start/stop services + * @param context + * @param serviceHealth - 'STARTED' or 'INSTALLED' + */ + startStopCertainPopup: function (context, serviceHealth) { + const {components, hosts, label} = context, + serviceDisplayName = this.get('content.displayName'), + isMaintenanceOFF = this.get('content.passiveState') === 'OFF', + confirmDisplayName = Em.I18n.t('services.service.componentsInNameSpace').format(label); + let msg = isMaintenanceOFF && serviceHealth == 'INSTALLED' ? + Em.I18n.t('services.service.stopCertain.warningMsg.turnOnMM').format(serviceDisplayName) : null; + msg = this.addAdditionalWarningMessage(serviceHealth, msg, serviceDisplayName); + const bodyMessage = Em.Object.create({ + putInMaintenance: (serviceHealth === 'INSTALLED' && isMaintenanceOFF) || + (serviceHealth === 'STARTED' && !isMaintenanceOFF), + turnOnMmMsg: serviceHealth === 'INSTALLED' ? + Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName) : + Em.I18n.t('passiveState.turnOffFor').format(serviceDisplayName), + confirmMsg: serviceHealth === 'INSTALLED' ? + Em.I18n.t('services.service.stop.confirmMsg').format(confirmDisplayName) : + Em.I18n.t('services.service.start.confirmMsg').format(confirmDisplayName), + confirmButton: serviceHealth === 'INSTALLED' ? + Em.I18n.t('services.service.stop.confirmButton') : + Em.I18n.t('services.service.start.confirmButton'), + additionalWarningMsg: msg + }); + // check HDFS NameNode checkpoint before stop components + if (serviceHealth === 'INSTALLED' && this.hasStartedNameNode(label)) { + this.checkNnLastCheckpointTime(() => App.showConfirmationFeedBackPopup((query, runMmOperation) => { + this.set('isPending', true); + this.startStopWithMmode(serviceHealth, query, runMmOperation, components, hosts, label); + }, bodyMessage)); + } else { + return App.showConfirmationFeedBackPopup((query, runMmOperation) => { + this.set('isPending', true); + this.startStopWithMmode(serviceHealth, query, runMmOperation, components, hosts, label); }, bodyMessage); } }, @@ -459,62 +502,89 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow return msg; }, - startStopWithMmode: function(serviceHealth, query, runMmOperation) { - var self = this; + startStopWithMmode: function(serviceHealth, query, runMmOperation, components, hosts, label) { if (runMmOperation) { - if (serviceHealth == "STARTED") { - this.startStopPopupPrimary(serviceHealth, query).complete(function() { - batchUtils.turnOnOffPassiveRequest("OFF", Em.I18n.t('passiveState.turnOff'), self.get('content.serviceName').toUpperCase()); + if (serviceHealth === 'STARTED') { + this.startStopPopupPrimary(serviceHealth, query, components, hosts, label).complete(() => { + batchUtils.turnOnOffPassiveRequest('OFF', Em.I18n.t('passiveState.turnOff'), this.get('content.serviceName').toUpperCase()); }); } else { - batchUtils.turnOnOffPassiveRequest("ON", Em.I18n.t('passiveState.turnOn'), this.get('content.serviceName').toUpperCase()).complete(function() { - self.startStopPopupPrimary(serviceHealth, query); + batchUtils.turnOnOffPassiveRequest('ON', Em.I18n.t('passiveState.turnOn'), this.get('content.serviceName').toUpperCase()).complete(() => { + this.startStopPopupPrimary(serviceHealth, query, components, hosts, label); }) } } else { - this.startStopPopupPrimary(serviceHealth, query); + this.startStopPopupPrimary(serviceHealth, query, components, hosts, label); } - }, - startStopPopupPrimary: function (serviceHealth, query) { - var requestInfo = (serviceHealth == "STARTED") - ? App.BackgroundOperationsController.CommandContexts.START_SERVICE.format(this.get('content.serviceName')) - : App.BackgroundOperationsController.CommandContexts.STOP_SERVICE.format(this.get('content.serviceName')); - - var data = { - 'context': requestInfo, - 'serviceName': this.get('content.serviceName').toUpperCase(), - 'ServiceInfo': { - 'state': serviceHealth - }, - 'query': query - }; + startStopPopupPrimary: function (serviceHealth, query, components, hosts, label) { + const isStart = (serviceHealth === 'STARTED'), + serviceName = this.get('content.serviceName'); + if (components || hosts) { + batchUtils.getComponentsFromServer({ + hosts, + components, + passiveState: 'OFF', + displayParams: ['host_components/HostRoles/component_name'] + }, data => { + const contextKey = isStart ? 'services.service.startAllComponents' : 'services.service.stopAllComponents', + context = Em.I18n.t(contextKey).format(label), + requestQuery = data.items.map(host => { + const hostName = host.Hosts.host_name, + componentNames = host.host_components.mapProperty('HostRoles.component_name').join(','); + return `(HostRoles/component_name.in(${componentNames})&HostRoles/host_name=${hostName})` + }).join('|'); + App.ajax.send({ + name: 'common.service.host_component.update', + sender: this, + data: { + context, + serviceName: serviceName.toUpperCase(), + state: serviceHealth, + query: requestQuery + }, + showLoadingPopup: true + }); + }); + } else { + const context = isStart + ? App.BackgroundOperationsController.CommandContexts.START_SERVICE.format(serviceName) + : App.BackgroundOperationsController.CommandContexts.STOP_SERVICE.format(serviceName), + data = { + context, + serviceName: serviceName.toUpperCase(), + ServiceInfo: { + state: serviceHealth + }, + query, + success: 'startStopPopupSuccessCallback', + error: 'startStopPopupErrorCallback' + }; - return App.ajax.send({ - 'name': 'common.service.update', - 'sender': this, - 'success': 'startStopPopupSuccessCallback', - 'error': 'startStopPopupErrorCallback', - 'data': data, - 'showLoadingPopup': true - }); + return App.ajax.send({ + name: 'common.service.update', + sender: this, + success: 'startStopPopupSuccessCallback', + error: 'startStopPopupErrorCallback', + data: data, + showLoadingPopup: true + }); + } }, /** * On click callback for <code>start service</code> button - * @param event */ - startService: function (event) { - this.startStopPopup(event, App.HostComponentStatus.started); + startService: function () { + this.startStopPopup(App.HostComponentStatus.started); }, /** * On click callback for <code>stop service</code> button - * @param event */ - stopService: function (event) { - this.startStopPopup(event, App.HostComponentStatus.stopped); + stopService: function () { + this.startStopPopup(App.HostComponentStatus.stopped); }, /** @@ -836,19 +906,18 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow }); }, - restartAllHostComponents : function(serviceName) { - var serviceDisplayName = this.get('content.displayName'); - var bodyMessage = Em.Object.create({ - putInMaintenance: this.get('content.passiveState') === 'OFF', - turnOnMmMsg: Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName), - confirmMsg: Em.I18n.t('services.service.restartAll.confirmMsg').format(serviceDisplayName), - confirmButton: Em.I18n.t('services.service.restartAll.confirmButton'), - additionalWarningMsg: this.get('content.passiveState') === 'OFF' ? Em.I18n.t('services.service.restartAll.warningMsg.turnOnMM').format(serviceDisplayName): null - }); + restartAllHostComponents: function (serviceName) { + const serviceDisplayName = this.get('content.displayName'), + bodyMessage = Em.Object.create({ + putInMaintenance: this.get('content.passiveState') === 'OFF', + turnOnMmMsg: Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName), + confirmMsg: Em.I18n.t('services.service.restartAll.confirmMsg').format(serviceDisplayName), + confirmButton: Em.I18n.t('services.service.restartAll.confirmButton'), + additionalWarningMsg: this.get('content.passiveState') === 'OFF' ? Em.I18n.t('services.service.restartAll.warningMsg.turnOnMM').format(serviceDisplayName) : null + }); // check HDFS NameNode checkpoint before stop service - if (this.get('content.serviceName') == 'HDFS' && - this.get('content.hostComponents').filterProperty('componentName', 'NAMENODE').someProperty('workStatus', App.HostComponentStatus.started)) { + if (this.hasStartedNameNode()) { this.checkNnLastCheckpointTime(function () { return App.showConfirmationFeedBackPopup(function(query, runMmOperation) { batchUtils.restartAllServiceHostComponents(serviceDisplayName, serviceName, false, query, runMmOperation); @@ -861,6 +930,58 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow } }, + restartCertainHostComponents: function (context) { + const serviceDisplayName = this.get('content.displayName'), + {components, hosts, label, serviceName} = context; + if (hosts) { + const bodyMessage = Em.Object.create({ + putInMaintenance: this.get('content.passiveState') === 'OFF', + turnOnMmMsg: Em.I18n.t('passiveState.turnOnFor').format(serviceDisplayName), + confirmMsg: Em.I18n.t('services.service.restartAll.confirmMsg').format(Em.I18n.t('services.service.componentsInNameSpace').format(label)), + confirmButton: Em.I18n.t('services.service.restartAll.confirmButton'), + additionalWarningMsg: this.get('content.passiveState') === 'OFF' ? + Em.I18n.t('services.service.restartCertain.warningMsg.turnOnMM').format(serviceDisplayName) : null + }); + if (this.hasStartedNameNode(label)) { + this.checkNnLastCheckpointTime(function () { + return App.showConfirmationFeedBackPopup(function (query, runMmOperation) { + batchUtils.restartCertainServiceHostComponents(serviceName, components, hosts, label, query, runMmOperation); + }, bodyMessage); + }); + } else { + App.showConfirmationFeedBackPopup(function (query, runMmOperation) { + batchUtils.restartCertainServiceHostComponents(serviceName, components, hosts, label, query, runMmOperation); + }, bodyMessage); + } + } else { + this.restartAllHostComponents(serviceName); + } + }, + + startCertainHostComponents: function (context) { + if (context.hosts) { + this.startStopCertainPopup(context, App.HostComponentStatus.started); + } else { + this.startService(); + } + }, + + stopCertainHostComponents: function (context) { + if (context.hosts) { + this.startStopCertainPopup(context, App.HostComponentStatus.stopped); + } else { + this.stopService(); + } + }, + + hasStartedNameNode: function (nameSpace) { + return this.get('content.serviceName') == 'HDFS' && this.get('content.hostComponents').some(component => { + return component.get('componentName') === 'NAMENODE' + && (!nameSpace || component.get('haNameSpace') === nameSpace) + && component.get('workStatus') === App.HostComponentStatus.started; + }); + }, + turnOnOffPassive: function(label) { var self = this; var state = this.get('content.passiveState') == 'OFF' ? 'ON' : 'OFF'; diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index fa2100f..11db9ce 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -1963,6 +1963,7 @@ Em.I18n.translations = { 'admin.stackUpgrade.dialog.skipped.failures':'There were automatically skipped failed steps. Please resolve each failure before continuing with the upgrade.', 'services.service.start':'Start', 'services.service.stop':'Stop', + 'services.service.allComponents':'All Components', 'services.service.metrics':'Metrics', 'services.nothingToAdd':'Nothing to add', 'services.service.summary.version':'Version', @@ -2313,6 +2314,8 @@ Em.I18n.translations = { 'services.service.add':'Add Service', 'services.service.startAll':'Start All', 'services.service.stopAll':'Stop All', + 'services.service.startAllComponents': 'Start all components for {0}', + 'services.service.stopAllComponents': 'Stop all components for {0}', 'services.service.restartAllRequired':'Restart All Required', 'services.service.downloadAllClientConfigs':'Download All Client Configs', 'services.service.startAll.confirmMsg' : 'You are about to start all services', @@ -2324,10 +2327,13 @@ Em.I18n.translations = { 'services.service.stop.confirmButton': 'Confirm Stop', 'services.service.start.confirmButton' : 'Confirm Start', 'services.service.stop.warningMsg.turnOnMM': 'This will generate alerts as the service is stopped. To suppress alerts, turn on Maintenance Mode for {0} prior to stopping.', + 'services.service.stopCertain.warningMsg.turnOnMM': 'This will generate alerts as components are stopped. To suppress alerts, turn on Maintenance Mode for {0} prior to stopping.', 'services.service.stop.warningMsg.dependent.services': 'Stopping {0} may impair the functioning of its dependent service(s): {1}.', 'services.service.restartAll.confirmButton': 'Confirm Restart All', 'services.service.restartAll.confirmMsg': 'You are about to restart {0}', 'services.service.restartAll.warningMsg.turnOnMM': 'This will trigger alerts as the service is restarted. To suppress alerts, turn on Maintenance Mode for {0} prior to running restart all', + 'services.service.restartCertain.warningMsg.turnOnMM': 'This will trigger alerts as components are restarted. To suppress alerts, turn on Maintenance Mode for {0} prior to running restart all', + 'services.service.componentsInNameSpace': 'components in {0} namespace', 'services.service.stop.HDFS.warningMsg.checkPointNA': 'Could not determine the age of the last HDFS checkpoint. Please ensure that you have a recent checkpoint. Otherwise, the NameNode(s) can take a very long time to start up.', 'services.service.stop.HDFS.warningMsg.checkPointTooOld.instructions': '<br><ol>' + diff --git a/ambari-web/app/models/host_component.js b/ambari-web/app/models/host_component.js index f028aab..7b60a27 100644 --- a/ambari-web/app/models/host_component.js +++ b/ambari-web/app/models/host_component.js @@ -295,22 +295,55 @@ App.HostComponentStatus = { }; App.HostComponentActionMap = { - getMap: function(ctx) { - var NN = ctx.get('controller.content.hostComponents').findProperty('componentName', 'NAMENODE'); - var RM = ctx.get('controller.content.hostComponents').findProperty('componentName', 'RESOURCEMANAGER'); - var RA = ctx.get('controller.content.hostComponents').findProperty('componentName', 'RANGER_ADMIN'); - var HM = ctx.get('controller.content.hostComponents').findProperty('componentName', 'HAWQMASTER'); - var HS = ctx.get('controller.content.hostComponents').findProperty('componentName', 'HAWQSTANDBY'); - var HMComponent = App.MasterComponent.find('HAWQMASTER'); - var HSComponent = App.MasterComponent.find('HAWQSTANDBY'); + getMap: function (ctx) { + const NN = ctx.get('controller.content.hostComponents').findProperty('componentName', 'NAMENODE'), + RM = ctx.get('controller.content.hostComponents').findProperty('componentName', 'RESOURCEMANAGER'), + RA = ctx.get('controller.content.hostComponents').findProperty('componentName', 'RANGER_ADMIN'), + HM = ctx.get('controller.content.hostComponents').findProperty('componentName', 'HAWQMASTER'), + HS = ctx.get('controller.content.hostComponents').findProperty('componentName', 'HAWQSTANDBY'), + HMComponent = App.MasterComponent.find('HAWQMASTER'), + HSComponent = App.MasterComponent.find('HAWQSTANDBY'), + hasMultipleMasterComponentGroups = ctx.get('service.hasMultipleMasterComponentGroups'), + isClientsOnlyService = ctx.get('controller.isClientsOnlyService'), + isStartDisabled = ctx.get('controller.isStartDisabled'), + isStopDisabled = ctx.get('controller.isStopDisabled'); return { + START_ALL: { + action: hasMultipleMasterComponentGroups ? '' : 'startService', + label: Em.I18n.t('services.service.start'), + cssClass: `glyphicon glyphicon-play ${isStartDisabled ? 'disabled' : 'enabled'}`, + isHidden: isClientsOnlyService, + disabled: isStartDisabled, + hasSubmenu: !isStartDisabled && hasMultipleMasterComponentGroups, + submenuOptions: !isStartDisabled && hasMultipleMasterComponentGroups ? this.getMastersSubmenu(ctx, 'startCertainHostComponents') : [] + }, + STOP_ALL: { + action: hasMultipleMasterComponentGroups ? '' : 'stopService', + label: Em.I18n.t('services.service.stop'), + cssClass: `glyphicon glyphicon-stop ${isStopDisabled ? 'disabled' : 'enabled'}`, + isHidden: isClientsOnlyService, + disabled: isStopDisabled, + hasSubmenu: !isStopDisabled && hasMultipleMasterComponentGroups, + submenuOptions: !isStopDisabled && hasMultipleMasterComponentGroups ? this.getMastersSubmenu(ctx, 'stopCertainHostComponents') : [] + }, RESTART_ALL: { - action: 'restartAllHostComponents', + action: hasMultipleMasterComponentGroups ? '' : 'restartAllHostComponents', context: ctx.get('serviceName'), - label: Em.I18n.t('restart.service.all'), - cssClass: 'glyphicon glyphicon-repeat', - disabled: false + label: hasMultipleMasterComponentGroups ? Em.I18n.t('common.restart') : Em.I18n.t('restart.service.all'), + cssClass: 'glyphicon glyphicon-time', + disabled: false, + hasSubmenu: hasMultipleMasterComponentGroups, + submenuOptions: hasMultipleMasterComponentGroups ? this.getMastersSubmenu(ctx, 'restartCertainHostComponents') : [] + }, + RESTART_NAMENODES: { + action: '', + label: Em.I18n.t('rollingrestart.dialog.title').format(pluralize(App.format.role('NAMENODE', false))), + cssClass: 'glyphicon glyphicon-time', + isHidden: !hasMultipleMasterComponentGroups, + disabled: false, + hasSubmenu: true, + submenuOptions: this.getMastersSubmenu(ctx, 'restartCertainHostComponents', ['NAMENODE']) }, RUN_SMOKE_TEST: { action: 'runSmokeTest', @@ -513,5 +546,35 @@ App.HostComponentActionMap = { //todo: provide disabled flag } }; + }, + + getMastersSubmenu: function (context, action, components) { + const serviceName = context.get('service.serviceName'), + groups = context.get('service.masterComponentGroups') || [], + allItem = { + action, + context: Object.assign({ + label: Em.I18n.t('services.service.allComponents') + }, components ? { + components, + hosts: groups.mapProperty('hosts').reduce((acc, groupHosts) => [...acc, ...groupHosts], []).uniq() + } : { + serviceName + }), + disabled: false + }, + groupItems = groups.map(group => { + return { + action, + context: { + label: group.title, + hosts: group.hosts, + components: components || group.components, + serviceName + }, + disabled: false + }; + }); + return [allItem, ...groupItems]; } }; diff --git a/ambari-web/app/models/service.js b/ambari-web/app/models/service.js index 138ba70..f9bedb1 100644 --- a/ambari-web/app/models/service.js +++ b/ambari-web/app/models/service.js @@ -41,6 +41,12 @@ App.Service = DS.Model.extend({ slaveComponents: DS.hasMany('App.SlaveComponent'), masterComponents: DS.hasMany('App.MasterComponent'), + masterComponentGroups: DS.attr('array', { + defaultValue: [] + }), + + hasMultipleMasterComponentGroups: Em.computed.gt('masterComponentGroups.length', 1), + /** * Check master/slave component state of service * and general services state to define if it can be removed diff --git a/ambari-web/app/models/service/hdfs.js b/ambari-web/app/models/service/hdfs.js index cacbd65..fa279f9 100644 --- a/ambari-web/app/models/service/hdfs.js +++ b/ambari-web/app/models/service/hdfs.js @@ -52,7 +52,30 @@ App.HDFSService = App.Service.extend({ upgradeStatus: DS.attr('string'), safeModeStatus: DS.attr('string'), nameNodeRpc: DS.attr('number'), - metricsNotAvailable: DS.attr('boolean') + metricsNotAvailable: DS.attr('boolean'), + masterComponentGroups: function () { + let result = []; + this.get('hostComponents').forEach(component => { + const nameSpace = component.get('haNameSpace'); + if (nameSpace) { + const hostName = component.get('hostName'), + existingNameSpace = result.findProperty('name', nameSpace), + currentNameSpace = existingNameSpace || { + name: nameSpace, + title: nameSpace, + hosts: [], + components: ['NAMENODE', 'ZKFC'] + }; + if (!existingNameSpace) { + result.push(currentNameSpace); + } + if (!currentNameSpace.hosts.contains(hostName)) { + currentNameSpace.hosts.push(hostName); + } + } + }); + return result; + }.property('hostComponents') }); App.HDFSService.FIXTURES = []; diff --git a/ambari-web/app/templates/main/service/info/summary.hbs b/ambari-web/app/templates/main/service/info/summary.hbs index 73ef14c..5f6219a 100644 --- a/ambari-web/app/templates/main/service/info/summary.hbs +++ b/ambari-web/app/templates/main/service/info/summary.hbs @@ -50,7 +50,7 @@ <ul class="nav nav-tabs"> {{#each group in view.mastersObj}} <li {{bindAttr class="group.isActive:active"}}> - <a href="#" {{action setActiveComponentGroup group.title target="view"}}>{{group.title}}</a> + <a href="#" {{action setActiveComponentGroup group.name target="view"}}>{{group.title}}</a> </li> {{/each}} </ul> diff --git a/ambari-web/app/templates/main/service/item.hbs b/ambari-web/app/templates/main/service/item.hbs index 9abcaf3..ad616e8 100644 --- a/ambari-web/app/templates/main/service/item.hbs +++ b/ambari-web/app/templates/main/service/item.hbs @@ -32,53 +32,32 @@ <ul class="pull-right dropdown-menu"> <!-- dropdown menu links --> - <!-- Start/Stop service actions --> - {{#isAuthorized "SERVICE.START_STOP"}} - {{#unless controller.isClientsOnlyService}} - <li {{bindAttr class="controller.isStartDisabled:disabled"}}> - <a href="javascript:void(null)" {{bindAttr class="controller.isStartDisabled:disabled" }} - {{action "startService" target="controller"}}> - <i {{bindAttr class=":glyphicon :glyphicon-play controller.isStartDisabled:disabled:enabled" }}></i> - {{t services.service.start}} - </a> - </li> - <li {{bindAttr class="controller.isStopDisabled:disabled"}}> - <a href="javascript:void(null)" {{bindAttr class="controller.isStopDisabled:disabled" }} - data-toggle="modal" {{action "stopService" target="controller"}}> - <i {{bindAttr class=":glyphicon :glyphicon-stop controller.isStopDisabled:disabled:enabled" }}></i> - {{t services.service.stop}} - </a> - </li> - {{/unless}} - {{/isAuthorized}} - {{#isAuthorized "SERVICE.RUN_CUSTOM_COMMAND, SERVICE.RUN_SERVICE_CHECK, SERVICE.TOGGLE_MAINTENANCE, SERVICE.ENABLE_HA"}} - {{#if view.maintenance.length}} - <!-- Other service actions --> - {{#each option in view.maintenance}} - {{#unless option.isHidden}} - <li {{bindAttr class="option.disabled option.hasSubmenu:dropdown-submenu option.hasSubmenu:submenu-left"}}> - <a {{action "doAction" option target="controller" href=true}} {{bindAttr data-title="option.tooltip"}} rel="HealthTooltip"> - <i {{bindAttr class="option.cssClass"}}></i> - {{option.label}} - </a> - {{#if option.hasSubmenu}} - <div class="dropdown-menu-wrap"> - <ul class="dropdown-menu"> - {{#each item in option.submenuOptions}} - <li> - <a {{action "doAction" item target="controller" href=true}}>{{item.context.label}}</a> - </li> - {{/each}} - </ul> - </div> - {{/if}} - </li> - {{/unless}} - {{/each}} - {{else}} - {{view App.SpinnerView classNames="service-button-spinner"}} - {{/if}} - {{/isAuthorized}} + {{#if view.maintenance.length}} + <!-- Service actions --> + {{#each option in view.maintenance}} + {{#unless option.isHidden}} + <li {{bindAttr class="option.disabled option.hasSubmenu:dropdown-submenu option.hasSubmenu:submenu-left"}}> + <a {{action "doAction" option target="controller" href=true}} {{bindAttr data-title="option.tooltip"}} rel="HealthTooltip"> + <i {{bindAttr class="option.cssClass"}}></i> + {{option.label}} + </a> + {{#if option.hasSubmenu}} + <div class="dropdown-menu-wrap"> + <ul class="dropdown-menu"> + {{#each item in option.submenuOptions}} + <li> + <a {{action "doAction" item target="controller" href=true}}>{{item.context.label}}</a> + </li> + {{/each}} + </ul> + </div> + {{/if}} + </li> + {{/unless}} + {{/each}} + {{else}} + {{view App.SpinnerView classNames="service-button-spinner"}} + {{/if}} </ul> </div> {{/if}} diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js index ec07b6b..af229c4 100644 --- a/ambari-web/app/utils/ajax/ajax.js +++ b/ambari-web/app/utils/ajax/ajax.js @@ -330,6 +330,32 @@ var urls = { } }, + 'common.service.host_component.update': { + 'real': '/clusters/{clusterName}/host_components', + 'mock': '', + 'type': 'PUT', + 'format': function (data) { + return { + data: JSON.stringify({ + RequestInfo: { + context: data.context, + operation_level: { + level: 'SERVICE', + cluster_name: data.clusterName, + service_name: data.serviceName + }, + query: data.query + }, + Body: { + HostRoles: { + state: data.state + } + } + }) + } + } + }, + 'common.host.host_component.passive': { 'real': '/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}', 'mock': '', diff --git a/ambari-web/app/utils/batch_scheduled_requests.js b/ambari-web/app/utils/batch_scheduled_requests.js index c3201e4..0e5a58f 100644 --- a/ambari-web/app/utils/batch_scheduled_requests.js +++ b/ambari-web/app/utils/batch_scheduled_requests.js @@ -73,8 +73,7 @@ module.exports = { * @param {bool} runMmOperation */ restartAllServiceHostComponents: function(serviceDisplayName, serviceName, staleConfigsOnly, query, runMmOperation) { - var self = this; - var context = staleConfigsOnly ? + const context = staleConfigsOnly ? Em.I18n.t('rollingrestart.context.allWithStaleConfigsForSelectedService').format(serviceDisplayName) : Em.I18n.t('rollingrestart.context.allForSelectedService').format(serviceDisplayName); @@ -86,18 +85,40 @@ module.exports = { staleConfigs: staleConfigsOnly ? staleConfigsOnly : null, passiveState: 'OFF', displayParams: ['host_components/HostRoles/component_name'] - }, function (data) { - var hostComponents = []; - data.items.forEach(function (host) { - host.host_components.forEach(function (hostComponent) { - hostComponents.push(Em.Object.create({ - componentName: hostComponent.HostRoles.component_name, - hostName: host.Hosts.host_name - })) - }); + }, data => { + const hostComponents = this.getRestartComponentsArray(data); + this.restartHostComponents(hostComponents, context, 'SERVICE', query); + }); + }, + + restartCertainServiceHostComponents: function (serviceName, components, hosts, label, query, runMmOperation) { + const context = Em.I18n.t('rollingrestart.context.allForSelectedService').format(label); + if (runMmOperation) { + this.turnOnOffPassiveRequest('ON', Em.I18n.t('passiveState.turnOnFor').format(serviceName), serviceName); + } + this.getComponentsFromServer({ + services: serviceName && [serviceName], + hosts, + components, + passiveState: 'OFF', + displayParams: ['host_components/HostRoles/component_name'] + }, data => { + const hostComponents = this.getRestartComponentsArray(data); + this.restartHostComponents(hostComponents, context, 'SERVICE', query); + }); + }, + + getRestartComponentsArray: function (data) { + let hostComponents = []; + data.items.forEach(host => { + host.host_components.forEach(hostComponent => { + hostComponents.push(Em.Object.create({ + componentName: hostComponent.HostRoles.component_name, + hostName: host.Hosts.host_name + })); }); - self.restartHostComponents(hostComponents, context, "SERVICE", query); }); + return hostComponents; }, /** diff --git a/ambari-web/app/views/main/service/info/summary.js b/ambari-web/app/views/main/service/info/summary.js index 3260456..e35ca40 100644 --- a/ambari-web/app/views/main/service/info/summary.js +++ b/ambari-web/app/views/main/service/info/summary.js @@ -431,28 +431,27 @@ App.MainServiceInfoSummaryView = Em.View.extend({ getGroupedMasterComponents: function (components) { switch (this.get('serviceName')) { case 'HDFS': - const hostComponents = this.get('service.hostComponents'), + const masterComponentGroups = this.get('service.masterComponentGroups'), + hostComponents = this.get('service.hostComponents'), zkfcs = hostComponents.filterProperty('componentName', 'ZKFC'), - hasNameNodeFederation = App.get('hasNameNodeFederation'); + hasNameNodeFederation = this.get('service.hasMultipleMasterComponentGroups'); let groups = []; hostComponents.forEach(component => { if (component.get('isMaster') && component.get('componentName') !== 'JOURNALNODE') { const hostName = component.get('hostName'), zkfc = zkfcs.findProperty('hostName', hostName); if (hasNameNodeFederation) { - const title = component.get('haNameSpace'), - existingGroup = groups.findProperty('title', title), - currentGroup = existingGroup || { - title, - isActive: title === this.get('activeMasterComponentGroup'), - components: [], - hosts: [] - }; + const name = component.get('haNameSpace'), + existingGroup = groups.findProperty('name', name), + currentGroup = existingGroup || Object.assign({}, masterComponentGroups.findProperty('name', name)); if (!existingGroup) { groups.push(currentGroup); + Em.setProperties(currentGroup, { + isActive: name === this.get('activeMasterComponentGroup'), + components: [] + }); } currentGroup.components.push(component); - currentGroup.hosts.push(hostName); if (zkfc) { zkfc.set('isSubComponent', true); currentGroup.components.push(zkfc); @@ -484,6 +483,6 @@ App.MainServiceInfoSummaryView = Em.View.extend({ setActiveComponentGroup: function (event) { const groupName = event.context; - this.get('mastersObj').forEach(group => Em.set(group, 'isActive', group.title === groupName)); + this.get('mastersObj').forEach(group => Em.set(group, 'isActive', group.name === groupName)); } }); diff --git a/ambari-web/app/views/main/service/item.js b/ambari-web/app/views/main/service/item.js index dc6f568..9240af8 100644 --- a/ambari-web/app/views/main/service/item.js +++ b/ambari-web/app/views/main/service/item.js @@ -127,181 +127,191 @@ App.MainServiceItemView = Em.View.extend({ var serviceCheckSupported = App.get('services.supportsServiceCheck').contains(service.get('serviceName')); var hasConfigTab = this.get('hasConfigTab'); var excludedCommands = this.get('mastersExcludedCommands'); + var serviceName = service.get('serviceName'); + var hasMultipleMasterComponentGroups = this.get('service.hasMultipleMasterComponentGroups'); - if (this.get('controller.isClientsOnlyService')) { - if (serviceCheckSupported) { - options.push(actionMap.RUN_SMOKE_TEST); - } - if (hasConfigTab) { - options.push(actionMap.REFRESH_CONFIGS); - } - } else { - if (this.get('serviceName') === 'FLUME') { - options.push(actionMap.REFRESH_CONFIGS); - } - if (this.get('serviceName') === 'YARN') { - options.push(actionMap.REFRESHQUEUES); - } - options.push(actionMap.RESTART_ALL); - allSlaves.concat(allMasters).filter(function (_component) { - return App.get('components.rollinRestartAllowed').contains(_component); - }).forEach(function(_component) { - var _componentNamePluralized = pluralize(App.format.role(_component, false)); - options.push(self.createOption(actionMap.ROLLING_RESTART, { - context: _component, - label: actionMap.ROLLING_RESTART.label.format(_componentNamePluralized) - })); - }); - allMasters.filter(function(master) { - return App.get('components.reassignable').contains(master); - }).forEach(function(master) { - options.push(self.createOption(actionMap.MOVE_COMPONENT, { - context: master, - label: actionMap.MOVE_COMPONENT.label.format(App.format.role(master, false)), - disabled: App.allHostNames.length === App.HostComponent.find().filterProperty('componentName', master).mapProperty('hostName').length - })); - }); - // add "Manage JournalNode" when NNHA is enabled and there is more hosts than JNs - var JNCount = App.HostComponent.find().filterProperty('componentName', 'JOURNALNODE').get('length'); - if (App.get('supports.manageJournalNode') && service.get('serviceName') == 'HDFS' && service.get('serviceTypes').contains('HA_MODE') + if (App.isAuthorized('SERVICE.START_STOP')) { + options.push(actionMap.START_ALL); + options.push(actionMap.STOP_ALL); + } + if (App.isAuthorized('SERVICE.RUN_CUSTOM_COMMAND, SERVICE.RUN_SERVICE_CHECK, SERVICE.TOGGLE_MAINTENANCE, SERVICE.ENABLE_HA')) { + if (this.get('controller.isClientsOnlyService')) { + if (serviceCheckSupported) { + options.push(actionMap.RUN_SMOKE_TEST); + } + if (hasConfigTab) { + options.push(actionMap.REFRESH_CONFIGS); + } + } else { + if (this.get('serviceName') === 'FLUME') { + options.push(actionMap.REFRESH_CONFIGS); + } + if (this.get('serviceName') === 'YARN') { + options.push(actionMap.REFRESHQUEUES); + } + options.push(actionMap.RESTART_ALL); + if (hasMultipleMasterComponentGroups && this.get('serviceName') === 'HDFS') { + options.push(actionMap.RESTART_NAMENODES); + } + allSlaves.concat(allMasters).filter(function (_component) { + return App.get('components.rollinRestartAllowed').contains(_component); + }).forEach(function (_component) { + var _componentNamePluralized = pluralize(App.format.role(_component, false)); + options.push(self.createOption(actionMap.ROLLING_RESTART, { + context: _component, + label: actionMap.ROLLING_RESTART.label.format(_componentNamePluralized) + })); + }); + allMasters.filter(function (master) { + return App.get('components.reassignable').contains(master); + }).forEach(function (master) { + options.push(self.createOption(actionMap.MOVE_COMPONENT, { + context: master, + label: actionMap.MOVE_COMPONENT.label.format(App.format.role(master, false)), + disabled: App.allHostNames.length === App.HostComponent.find().filterProperty('componentName', master).mapProperty('hostName').length + })); + }); + // add "Manage JournalNode" when NNHA is enabled and there is more hosts than JNs + var JNCount = App.HostComponent.find().filterProperty('componentName', 'JOURNALNODE').get('length'); + if (App.get('supports.manageJournalNode') && service.get('serviceName') == 'HDFS' && service.get('serviceTypes').contains('HA_MODE') && (App.router.get('mainHostController.totalCount') > JNCount || JNCount > 3)) { - options.push(actionMap.MANAGE_JN); - } - if (service.get('serviceTypes').contains('HA_MODE') && App.isAuthorized('SERVICE.ENABLE_HA')) { - switch (service.get('serviceName')) { - case 'HDFS': - options.push(actionMap.TOGGLE_NN_HA); - break; - case 'YARN': - options.push(actionMap.TOGGLE_RM_HA); - break; - case 'RANGER': - options.push(actionMap.TOGGLE_RA_HA); - break; - case 'HAWQ': - options.push(actionMap.TOGGLE_ADD_HAWQ_STANDBY); - break; + options.push(actionMap.MANAGE_JN); } - } - if (service.get('serviceTypes').contains('FEDERATION') && App.isAuthorized('SERVICE.ENABLE_HA')) { - switch (service.get('serviceName')) { - case 'HDFS': - options.push(actionMap.TOGGLE_NN_FEDERATION); - break; + if (service.get('serviceTypes').contains('HA_MODE') && App.isAuthorized('SERVICE.ENABLE_HA')) { + switch (service.get('serviceName')) { + case 'HDFS': + options.push(actionMap.TOGGLE_NN_HA); + break; + case 'YARN': + options.push(actionMap.TOGGLE_RM_HA); + break; + case 'RANGER': + options.push(actionMap.TOGGLE_RA_HA); + break; + case 'HAWQ': + options.push(actionMap.TOGGLE_ADD_HAWQ_STANDBY); + break; + } } - } - if (serviceCheckSupported) { - options.push(actionMap.RUN_SMOKE_TEST); - } - options.push(actionMap.TOGGLE_PASSIVE); - var serviceName = service.get('serviceName'); - var nnComponent = App.StackServiceComponent.find().findProperty('componentName', 'NAMENODE'); - var knoxGatewayComponent = App.StackServiceComponent.find().findProperty('componentName', 'KNOX_GATEWAY'); - if (serviceName === 'HDFS' && nnComponent) { - var namenodeCustomCommands = nnComponent.get('customCommands'); - if (namenodeCustomCommands && namenodeCustomCommands.contains('REBALANCEHDFS')) { - options.push(actionMap.REBALANCEHDFS); + if (service.get('serviceTypes').contains('FEDERATION') && App.isAuthorized('SERVICE.ENABLE_HA')) { + switch (service.get('serviceName')) { + case 'HDFS': + options.push(actionMap.TOGGLE_NN_FEDERATION); + break; + } } - } - - if (serviceName === 'KNOX' && knoxGatewayComponent) { - var knoxGatewayCustomCommands = knoxGatewayComponent.get('customCommands'); - knoxGatewayCustomCommands.forEach(function(command) { - if (actionMap[command]) { - options.push(actionMap[command]); + if (serviceCheckSupported) { + options.push(actionMap.RUN_SMOKE_TEST); + } + options.push(actionMap.TOGGLE_PASSIVE); + var nnComponent = App.StackServiceComponent.find().findProperty('componentName', 'NAMENODE'); + var knoxGatewayComponent = App.StackServiceComponent.find().findProperty('componentName', 'KNOX_GATEWAY'); + if (serviceName === 'HDFS' && nnComponent) { + var namenodeCustomCommands = nnComponent.get('customCommands'); + if (namenodeCustomCommands && namenodeCustomCommands.contains('REBALANCEHDFS')) { + options.push(actionMap.REBALANCEHDFS); } - }); - } + } - if (serviceName === 'HIVE') { - var hiveServerInteractiveComponent = App.StackServiceComponent.find().findProperty('componentName', 'HIVE_SERVER_INTERACTIVE'); - var isHiveInteractiveServerPresent = allMasters.contains('HIVE_SERVER_INTERACTIVE'); - if (hiveServerInteractiveComponent && isHiveInteractiveServerPresent) { - var LLAPCustomCommands = hiveServerInteractiveComponent.get('customCommands'); - LLAPCustomCommands.forEach(function (command) { + if (serviceName === 'KNOX' && knoxGatewayComponent) { + var knoxGatewayCustomCommands = knoxGatewayComponent.get('customCommands'); + knoxGatewayCustomCommands.forEach(function (command) { if (actionMap[command]) { options.push(actionMap[command]); } }); } - } - /** - * Display all custom commands of Master and StandBy on Service page. - **/ - if(serviceName === 'HAWQ') { - var hawqMasterComponent = App.StackServiceComponent.find().findProperty('componentName','HAWQMASTER'); - var hawqStandByComponent = App.StackServiceComponent.find().findProperty('componentName','HAWQSTANDBY'); - [hawqMasterComponent,hawqStandByComponent].forEach(function(component){ - component.get('customCommands').forEach(function(command){ - options.push(self.createOption(actionMap[command], { - context: { - label: actionMap[command].context, - service: component.get('serviceName'), - component: component.get('componentName'), - command: command + if (serviceName === 'HIVE') { + var hiveServerInteractiveComponent = App.StackServiceComponent.find().findProperty('componentName', 'HIVE_SERVER_INTERACTIVE'); + var isHiveInteractiveServerPresent = allMasters.contains('HIVE_SERVER_INTERACTIVE'); + if (hiveServerInteractiveComponent && isHiveInteractiveServerPresent) { + var LLAPCustomCommands = hiveServerInteractiveComponent.get('customCommands'); + LLAPCustomCommands.forEach(function (command) { + if (actionMap[command]) { + options.push(actionMap[command]); } - })); + }); + } + } + + /** + * Display all custom commands of Master and StandBy on Service page. + **/ + if (serviceName === 'HAWQ') { + var hawqMasterComponent = App.StackServiceComponent.find().findProperty('componentName', 'HAWQMASTER'); + var hawqStandByComponent = App.StackServiceComponent.find().findProperty('componentName', 'HAWQSTANDBY'); + [hawqMasterComponent, hawqStandByComponent].forEach(function (component) { + component.get('customCommands').forEach(function (command) { + options.push(self.createOption(actionMap[command], { + context: { + label: actionMap[command].context, + service: component.get('serviceName'), + component: component.get('componentName'), + command: command + } + })); + }); }); - }); - } + } - if(App.isAuthorized('HOST.ADD_DELETE_COMPONENTS')){ - self.addActionMap().filterProperty('service', serviceName).forEach(function(item) { - if (App.get('components.addableToHost').contains(item.component)) { + if (App.isAuthorized('HOST.ADD_DELETE_COMPONENTS')) { + self.addActionMap().filterProperty('service', serviceName).forEach(function (item) { + if (App.get('components.addableToHost').contains(item.component)) { - var isEnabled = App.HostComponent.find().filterProperty('componentName', item.component).length < App.get('allHostNames.length'); + var isEnabled = App.HostComponent.find().filterProperty('componentName', item.component).length < App.get('allHostNames.length'); - if (item.component === 'OOZIE_SERVER') { - isEnabled = isEnabled && !(Em.isEmpty(self.get('controller.configs.oozie-env.oozie_database')) || self.get('controller.configs.oozie-env.oozie_database') === 'New Derby Database'); - } + if (item.component === 'OOZIE_SERVER') { + isEnabled = isEnabled && !(Em.isEmpty(self.get('controller.configs.oozie-env.oozie_database')) || self.get('controller.configs.oozie-env.oozie_database') === 'New Derby Database'); + } - item.action = 'addComponent'; - item.disabled = isEnabled ? '' : 'disabled'; - item.tooltip = isEnabled ? '' : Em.I18n.t('services.summary.allHostsAlreadyRunComponent').format(item.component); - item.context = item.component; + item.action = 'addComponent'; + item.disabled = isEnabled ? '' : 'disabled'; + item.tooltip = isEnabled ? '' : Em.I18n.t('services.summary.allHostsAlreadyRunComponent').format(item.component); + item.context = item.component; - options.push(item); + options.push(item); + } + }); } - }); - } - - if (App.get('isKerberosEnabled')){ - options.push(actionMap.REGENERATE_KEYTAB_FILE_OPERATIONS); - } - - allMasters.forEach(function(master) { - var component = App.StackServiceComponent.find(master); - var commands = component.get('customCommands'); - if (!commands.length) { - return; + if (App.get('isKerberosEnabled')) { + options.push(actionMap.REGENERATE_KEYTAB_FILE_OPERATIONS); } - commands.forEach(function(command) { - if (excludedCommands[master] && excludedCommands[master].contains(command)){ + allMasters.forEach(function (master) { + var component = App.StackServiceComponent.find(master); + var commands = component.get('customCommands'); + + if (!commands.length) { return; } - options.push(self.createOption(actionMap.MASTER_CUSTOM_COMMAND, { - label: Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(App.format.normalizeNameBySeparators(command, ["_", "-", " "])), - context: { - label: Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(App.format.normalizeNameBySeparators(command, ["_", "-", " "])), - service: component.get('serviceName'), - component: component.get('componentName'), - command: command + commands.forEach(function (command) { + if (excludedCommands[master] && excludedCommands[master].contains(command)) { + return; } - })); + + options.push(self.createOption(actionMap.MASTER_CUSTOM_COMMAND, { + label: Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(App.format.normalizeNameBySeparators(command, ["_", "-", " "])), + context: { + label: Em.I18n.t('services.service.actions.run.executeCustomCommand.menu').format(App.format.normalizeNameBySeparators(command, ["_", "-", " "])), + service: component.get('serviceName'), + component: component.get('componentName'), + command: command + } + })); + }); }); - }); - } + } - if (hasConfigTab) { - options.push(actionMap.DOWNLOAD_CLIENT_CONFIGS); - } + if (hasConfigTab) { + options.push(actionMap.DOWNLOAD_CLIENT_CONFIGS); + } - if (App.isAuthorized("SERVICE.ADD_DELETE_SERVICES") && App.supports.enableAddDeleteServices) { - options.push(actionMap.DELETE_SERVICE); + if (App.isAuthorized("SERVICE.ADD_DELETE_SERVICES") && App.supports.enableAddDeleteServices) { + options.push(actionMap.DELETE_SERVICE); + } } if (this.get('maintenance.length')) { diff --git a/ambari-web/test/controllers/main/service/item_test.js b/ambari-web/test/controllers/main/service/item_test.js index 6162603..2ac35a2 100644 --- a/ambari-web/test/controllers/main/service/item_test.js +++ b/ambari-web/test/controllers/main/service/item_test.js @@ -283,12 +283,12 @@ describe('App.MainServiceItemController', function () { mainServiceItemController.startStopPopup.restore(); }); it("start service", function () { - mainServiceItemController.startService({}); - expect(mainServiceItemController.startStopPopup.calledWith({},App.HostComponentStatus.started)).to.equal(true); + mainServiceItemController.startService(); + expect(mainServiceItemController.startStopPopup.calledWith(App.HostComponentStatus.started)).to.equal(true); }); it("stop service", function () { - mainServiceItemController.stopService({}); - expect(mainServiceItemController.startStopPopup.calledWith({},App.HostComponentStatus.stopped)).to.equal(true); + mainServiceItemController.stopService(); + expect(mainServiceItemController.startStopPopup.calledWith(App.HostComponentStatus.stopped)).to.equal(true); }); }); @@ -356,27 +356,26 @@ describe('App.MainServiceItemController', function () { }); describe("#startStopPopup", function () { - var el = document.createElement("BUTTON"); - el.disabled = false; - var event = { - target: el - }; var mainServiceItemController = App.MainServiceItemController.create({ content: { - serviceName: "HDFS", - hostComponents: [ { - componentName: 'NAMENODE', - workStatus: 'INSTALLED' - }] + serviceName: 'HDFS', + hostComponents: [ + Em.Object.create({ + componentName: 'NAMENODE', + workStatus: 'INSTALLED' + }) + ] } }); var mainServiceItemControllerHdfsStarted = App.MainServiceItemController.create({ content: { - serviceName: "HDFS", - hostComponents: [ { - componentName: 'NAMENODE', - workStatus: 'STARTED' - }] + serviceName: 'HDFS', + hostComponents: [ + Em.Object.create({ + componentName: 'NAMENODE', + workStatus: 'STARTED' + }) + ] } }); beforeEach(function () { @@ -394,12 +393,12 @@ describe('App.MainServiceItemController', function () { Em.I18n.t.restore(); }); it("start start/stop service popup", function () { - mainServiceItemController.startStopPopup(event, "").onPrimary(); + mainServiceItemController.startStopPopup("").onPrimary(); expect(mainServiceItemController.startStopPopupPrimary.calledOnce).to.equal(true); }); it ("should popup warning to check last checkpoint time if work status is STARTED", function() { - mainServiceItemControllerHdfsStarted.startStopPopup(event, "INSTALLED"); + mainServiceItemControllerHdfsStarted.startStopPopup("INSTALLED"); expect(mainServiceItemControllerHdfsStarted.checkNnLastCheckpointTime.calledOnce).to.equal(true); }); @@ -445,13 +444,13 @@ describe('App.MainServiceItemController', function () { }); it ("should confirm stop if serviceHealth is INSTALLED", function() { - mainServiceItemController.startStopPopup(event, "INSTALLED"); + mainServiceItemController.startStopPopup("INSTALLED"); expect(Em.I18n.t.calledWith('services.service.stop.confirmMsg')).to.be.ok; expect(Em.I18n.t.calledWith('services.service.stop.confirmButton')).to.be.ok; }); it ("should confirm start if serviceHealth is not INSTALLED", function() { - mainServiceItemController.startStopPopup(event, ""); + mainServiceItemController.startStopPopup(""); expect(Em.I18n.t.calledWith('services.service.start.confirmMsg')).to.be.ok; expect(Em.I18n.t.calledWith('services.service.start.confirmButton')).to.be.ok; }); @@ -459,7 +458,7 @@ describe('App.MainServiceItemController', function () { it ("should not display a dependent list if it is to start a service", function() { var _mainServiceItemController = App.MainServiceItemController.create( {content: {serviceName: "HDFS", passiveState:'OFF'}}); - _mainServiceItemController.startStopPopup(event, ""); + _mainServiceItemController.startStopPopup(""); expect(Em.I18n.t.calledWith('services.service.stop.warningMsg.dependent.services')).to.not.be.ok; }); @@ -467,15 +466,17 @@ describe('App.MainServiceItemController', function () { beforeEach(function () { var _mainServiceItemController = App.MainServiceItemController.create( {content: { - serviceName: "HDFS", + serviceName: 'HDFS', passiveState:'OFF', - hostComponents: [{ - componentName: 'NAMENODE', - workStatus: 'INSTALLED' - }] + hostComponents: [ + Em.Object.create({ + componentName: 'NAMENODE', + workStatus: 'INSTALLED' + }) + ] }} ); - _mainServiceItemController.startStopPopup(event, "INSTALLED"); + _mainServiceItemController.startStopPopup("INSTALLED"); this.dependencies = Em.I18n.t('services.service.stop.warningMsg.dependent.services').format("HDFS", "HBase,YARN"); this.msg = Em.I18n.t('services.service.stop.warningMsg.turnOnMM').format("HDFS"); this.fullMsg = _mainServiceItemController.addAdditionalWarningMessage("INSTALLED", this.msg, "HDFS"); @@ -497,7 +498,7 @@ describe('App.MainServiceItemController', function () { beforeEach(function () { var _mainServiceItemController = App.MainServiceItemController.create( {content: {serviceName: "HIVE", passiveState:'OFF'}}); - _mainServiceItemController.startStopPopup(event, "INSTALLED"); + _mainServiceItemController.startStopPopup("INSTALLED"); this.dependencies = Em.I18n.t('services.service.stop.warningMsg.dependent.services').format("HIVE", "Spark"); this.msg = Em.I18n.t('services.service.stop.warningMsg.turnOnMM').format("HIVE"); this.fullMsg = _mainServiceItemController.addAdditionalWarningMessage("INSTALLED", this.msg, "HIVE"); @@ -521,11 +522,13 @@ describe('App.MainServiceItemController', function () { var temp = batchUtils.restartAllServiceHostComponents; var mainServiceItemController = App.MainServiceItemController.create({ content: { - serviceName: "HDFS", - hostComponents: [{ - componentName: 'NAMENODE', - workStatus: 'STARTED' - }] + serviceName: 'HDFS', + hostComponents: [ + Em.Object.create({ + componentName: 'NAMENODE', + workStatus: 'STARTED' + }) + ] } }); beforeEach(function () { @@ -548,11 +551,13 @@ describe('App.MainServiceItemController', function () { it("start restartAllHostComponents for service", function () { var controller = App.MainServiceItemController.create({ content: { - serviceName: "HDFS", - hostComponents: [{ - componentName: 'NAMENODE', - workStatus: 'INSTALLED' - }] + serviceName: 'HDFS', + hostComponents: [ + Em.Object.create({ + componentName: 'NAMENODE', + workStatus: 'INSTALLED' + }) + ] } }); controller.restartAllHostComponents({}).onPrimary(); diff --git a/ambari-web/test/views/main/service/item_test.js b/ambari-web/test/views/main/service/item_test.js index b86d021..f2ea9f4 100644 --- a/ambari-web/test/views/main/service/item_test.js +++ b/ambari-web/test/views/main/service/item_test.js @@ -173,7 +173,9 @@ describe('App.MainServiceItemView', function () { }) ], result: [ - {"action": "restartAllHostComponents", "context": "HDFS", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "HDFS", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "rollingRestart", "label": "Restart DataNodes", "cssClass": "glyphicon glyphicon-time", "disabled": false, "context": "DATANODE"}, {"action": "reassignMaster", "context": "NAMENODE", "label": "Move NameNode", "cssClass": "glyphicon glyphicon-share-alt", "disabled": false}, {"action": "reassignMaster", "context": "SECONDARY_NAMENODE", "label": "Move SNameNode", "cssClass": "glyphicon glyphicon-share-alt", "disabled": false}, @@ -207,7 +209,9 @@ describe('App.MainServiceItemView', function () { {'isAddDisabled-ZOOKEEPER_SERVER': 'disabled'} ], result: [ - {"action": "restartAllHostComponents", "context": "ZOOKEEPER", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "ZOOKEEPER", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for ZooKeeper", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, {"cssClass": "glyphicon glyphicon-plus", "label": "Add ZooKeeper Server", "service": "ZOOKEEPER", "component": "ZOOKEEPER_SERVER", "action": "addComponent", "disabled": "", tooltip: ''}, @@ -244,8 +248,10 @@ describe('App.MainServiceItemView', function () { }) ], result: [ + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, {"action": "refreshYarnQueues", "customCommand": "REFRESHQUEUES", "context": "Refresh YARN Capacity Scheduler", "label": "Refresh YARN Capacity Scheduler", "cssClass": "glyphicon glyphicon-refresh", "disabled": false}, - {"action": "restartAllHostComponents", "context": "YARN", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "restartAllHostComponents", "context": "YARN", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "rollingRestart", "label": "Restart NodeManagers", "cssClass": "glyphicon glyphicon-time", "disabled": false, "context": "NODEMANAGER"}, {"action": "reassignMaster", "context": "APP_TIMELINE_SERVER", "label": "Move App Timeline Server", "cssClass": "glyphicon glyphicon-share-alt", "disabled": false}, {"action": "reassignMaster", "context": "RESOURCEMANAGER", "label": "Move ResourceManager", "cssClass": "glyphicon glyphicon-share-alt", "disabled": false}, @@ -274,7 +280,9 @@ describe('App.MainServiceItemView', function () { }) ], result: [ - {"action": "restartAllHostComponents", "context": "MAPREDUCE2", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "MAPREDUCE2", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for MapReduce2", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, {"action": "downloadClientConfigs", "label": "Download Client Configs", "cssClass": "glyphicon glyphicon-download-alt", "isHidden": false, "disabled": false, "hasSubmenu": false, "submenuOptions": []} @@ -294,7 +302,9 @@ describe('App.MainServiceItemView', function () { }) ], result: [ - {"action": "restartAllHostComponents", "context": "KAFKA", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "KAFKA", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for Kafka", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, {"action": "downloadClientConfigs", "label": "Download Client Configs", "cssClass": "glyphicon glyphicon-download-alt", "isHidden": true, "disabled": false, "hasSubmenu": false, "submenuOptions": []} @@ -318,8 +328,10 @@ describe('App.MainServiceItemView', function () { {'isAddDisabled-FLUME_HANDLER': ''} ], result: [ + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, {"action": "refreshConfigs", "label": "Refresh configs", "cssClass": "glyphicon glyphicon-refresh", "disabled": false}, - {"action": "restartAllHostComponents", "context": "FLUME", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "restartAllHostComponents", "context": "FLUME", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "rollingRestart", "label": "Restart Flumes", "cssClass": "glyphicon glyphicon-time", "disabled": false, "context": "FLUME_HANDLER"}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for Flume", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, @@ -355,7 +367,9 @@ describe('App.MainServiceItemView', function () { {'isAddDisabled-HBASE_MASTER': ''} ], result: [ - {"action": "restartAllHostComponents", "context": "HBASE", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "HBASE", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "rollingRestart", "label": "Restart RegionServers", "cssClass": "glyphicon glyphicon-time", "disabled": false, "context": "HBASE_REGIONSERVER"}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for HBase", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, @@ -382,7 +396,9 @@ describe('App.MainServiceItemView', function () { }) ], result: [ - {"action": "restartAllHostComponents", "context": "OOZIE", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "OOZIE", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "reassignMaster", "context": "OOZIE_SERVER", "label": "Move Oozie Server", "cssClass": "glyphicon glyphicon-share-alt", "disabled": false}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for Oozie", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, @@ -404,7 +420,9 @@ describe('App.MainServiceItemView', function () { }) ], result: [ - {"action": "restartAllHostComponents", "context": "KNOX", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "KNOX", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for Knox", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, {"action": "startLdapKnox", "customCommand": "STARTDEMOLDAP", "context": "Start Demo LDAP", "label": "Start Demo LDAP", "cssClass": "glyphicon glyphicon-play-sign", "disabled": false}, @@ -426,7 +444,9 @@ describe('App.MainServiceItemView', function () { }) ], result: [ - {"action": "restartAllHostComponents", "context": "STORM", "label": "Restart All", "cssClass": "glyphicon glyphicon-repeat", "disabled": false}, + {"action": "startService", "label": "Start", "cssClass": "glyphicon glyphicon-play enabled", "disabled": false}, + {"action": "stopService", "label": "Stop", "cssClass": "glyphicon glyphicon-stop enabled", "disabled": false}, + {"action": "restartAllHostComponents", "context": "STORM", "label": "Restart All", "cssClass": "glyphicon glyphicon-time", "disabled": false}, {"action": "runSmokeTest", "label": "Run Service Check", "cssClass": "glyphicon glyphicon-thumbs-up", "disabled": false}, {"action": "turnOnOffPassive", "context": "Turn On Maintenance Mode for Storm", "label": "Turn On Maintenance Mode", "cssClass": "icon-medkit", "disabled": false}, {"action": "downloadClientConfigs", "label": "Download Client Configs", "cssClass": "glyphicon glyphicon-download-alt", "isHidden": true, "disabled": false, "hasSubmenu": false, "submenuOptions": []} @@ -547,6 +567,7 @@ describe('App.MainServiceItemView', function () { }), isSeveralClients: false, clientComponents: [], + isStartDisabled: false, isStopDisabled: false, isSmokeTestDisabled: false }), -- To stop receiving notification emails like this one, please contact ababiic...@apache.org.