/** * 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'); App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMixin, { name: 'mainServiceInfoSummaryController', selectedFlumeAgent: null, /** * Indicates whether Ranger plugins status update polling is active * @type {boolean} */ isRangerUpdateWorking: false, /** * Indicates whether array with initial Ranger plugins data is set * @type {boolean} */ isRangerPluginsArraySet: false, /** * Indicates whether previous AJAX request for Ranger plugins config properties has failed * @type {boolean} */ isPreviousRangerConfigsCallFailed: false, layoutNameSuffix: "_dashboard", sectionNameSuffix: "_SUMMARY", /** * Ranger plugins data * @type {array} */ rangerPlugins: [ { serviceName: 'HDFS', type: 'ranger-hdfs-plugin-properties', propertyName: 'ranger-hdfs-plugin-enabled', valueForEnable: 'Yes' }, { serviceName: 'YARN', type: 'ranger-yarn-plugin-properties', propertyName: 'ranger-yarn-plugin-enabled', valueForEnable: 'Yes' }, { serviceName: 'HBASE', type: 'ranger-hbase-plugin-properties', propertyName: 'ranger-hbase-plugin-enabled', valueForEnable: 'Yes' }, { serviceName: 'HIVE', type: 'hive-env', propertyName: 'hive_security_authorization', valueForEnable: 'Ranger' }, { serviceName: 'KNOX', type: 'ranger-knox-plugin-properties', propertyName: 'ranger-knox-plugin-enabled', valueForEnable: 'Yes' }, { serviceName: 'STORM', type: 'ranger-storm-plugin-properties', propertyName: 'ranger-storm-plugin-enabled', valueForEnable: 'Yes' }, { serviceName: 'KAFKA', type: 'ranger-kafka-plugin-properties', propertyName: 'ranger-kafka-plugin-enabled', valueForEnable: 'Yes' }, { serviceName: 'KAFKA', type: 'ranger-kafka-plugin-properties', propertyName: 'ranger-kafka-plugin-enabled', valueForEnable: 'Yes' } ], /** * Some widget has type `GRAPH` * * @type {boolean} */ someWidgetGraphExists: Em.computed.someBy('widgets', 'widgetType', 'GRAPH'), /** * @type {boolean} */ showTimeRangeControl: Em.computed.or('!isServiceWithEnhancedWidgets', 'someWidgetGraphExists'), /** * Set initial Ranger plugins data * @method setRangerPlugins */ setRangerPlugins: function () { if (App.get('router.clusterController.isLoaded') && !this.get('isRangerPluginsArraySet')) { // Display order of ranger plugin for services should be decided from App.StackService.displayOrder to keep consistency // with display order of services at other places in the application like `select service's page` and `service menu bar` var displayOrderLength = App.StackService.displayOrder.length; var rangerPlugins = this.get('rangerPlugins').map(function (item, index) { var displayOrderIndex = App.StackService.displayOrder.indexOf(item.serviceName); return $.extend(item, { index: displayOrderIndex == -1 ? displayOrderLength + index : displayOrderIndex }); }).sortProperty('index'); this.setProperties({ rangerPlugins: rangerPlugins.map(function (item) { var stackService = App.StackService.find().findProperty('serviceName', item.serviceName); var displayName = (stackService) ? stackService.get('displayName') : item.serviceName; return $.extend(item, { pluginTitle: Em.I18n.t('services.service.summary.ranger.plugin.title').format(displayName), isDisplayed: App.Service.find().someProperty('serviceName', item.serviceName) && stackService.get('configTypes').hasOwnProperty(item.type), status: Em.I18n.t('services.service.summary.ranger.plugin.loadingStatus') }); }), isRangerPluginsArraySet: true }); } }.observes('App.router.clusterController.isLoaded'), /** * Get latest config tags * @method updateRangerPluginsStatus * @param callback */ updateRangerPluginsStatus: function (callback) { App.ajax.send({ name: 'config.tags', sender: this, success: 'getRangerPluginsStatus', callback: callback }); }, /** * Get latest Ranger plugins config properties * @method getRangerPluginsStatus * @param data */ getRangerPluginsStatus: function (data) { var urlParams = []; this.get('rangerPlugins').forEach(function (item) { if (App.Service.find().someProperty('serviceName', item.serviceName) && data.Clusters.desired_configs.hasOwnProperty(item.type)) { var currentTag = data.Clusters.desired_configs[item.type].tag; var isTagChanged = item.tag != currentTag; Em.set(item, 'isDisplayed', true); //Request for properties should be sent either if configs have changed or if previous Ranger plugins config properties has failed if (isTagChanged || this.get('isPreviousRangerConfigsCallFailed')) { Em.set(item, 'tag', currentTag); urlParams.push('(type=' + item.type + '&tag=' + currentTag + ')'); } } else { Em.set(item, 'isDisplayed', false); } }, this); if (urlParams.length) { App.ajax.send({ name: 'reassign.load_configs', sender: this, data: { urlParams: urlParams.join('|') }, success: 'getRangerPluginsStatusSuccess', error: 'getRangerPluginsStatusError' }); } }, /** * Set Ranger plugins statuses * @method getRangerPluginsStatusSuccess * @param data */ getRangerPluginsStatusSuccess: function (data) { this.set('isPreviousRangerConfigsCallFailed', false); data.items.forEach(function (item) { var serviceName = this.get('rangerPlugins').findProperty('type', item.type).serviceName; var propertyName = this.get('rangerPlugins').findProperty('type', item.type).propertyName; var propertyValue = this.get('rangerPlugins').findProperty('type', item.type).valueForEnable; var statusString; if (item.properties[propertyName]) { statusString = item.properties[propertyName] == propertyValue ? 'alerts.table.state.enabled' : 'alerts.table.state.disabled'; } else { statusString = 'common.unknown'; } Em.set(this.get('rangerPlugins').findProperty('serviceName', serviceName), 'status', Em.I18n.t(statusString)); }, this); }, /** * Method executed if Ranger plugins config properties request has failed * @method getRangerPluginsStatusError */ getRangerPluginsStatusError: function () { this.set('isPreviousRangerConfigsCallFailed', true); }, /** * Send start command for selected Flume Agent * @method startFlumeAgent */ startFlumeAgent: function () { var selectedFlumeAgent = arguments[0].context; if (selectedFlumeAgent && selectedFlumeAgent.get('status') === 'NOT_RUNNING') { var self = this; App.showConfirmationPopup(function () { var state = 'STARTED'; var context = Em.I18n.t('services.service.summary.flume.start.context').format(selectedFlumeAgent.get('name')); self.sendFlumeAgentCommandToServer(state, context, selectedFlumeAgent); }); } }, /** * Send stop command for selected Flume Agent * @method stopFlumeAgent */ stopFlumeAgent: function () { var selectedFlumeAgent = arguments[0].context; if (selectedFlumeAgent && selectedFlumeAgent.get('status') === 'RUNNING') { var self = this; App.showConfirmationPopup(function () { var state = 'INSTALLED'; var context = Em.I18n.t('services.service.summary.flume.stop.context').format(selectedFlumeAgent.get('name')); self.sendFlumeAgentCommandToServer(state, context, selectedFlumeAgent); }); } }, /** * Send command for Flume Agent to server * @param {string} state * @param {string} context * @param {Object} agent * @method sendFlumeAgentCommandToServer */ sendFlumeAgentCommandToServer: function (state, context, agent) { App.ajax.send({ name: 'service.flume.agent.command', sender: this, data: { state: state, context: context, agentName: agent.get('name'), host: agent.get('hostName') }, success: 'commandSuccessCallback' }); }, /** * Callback, that shows Background operations popup if request was successful */ commandSuccessCallback: function () { // load data (if we need to show this background operations popup) from persist App.router.get('userSettingsController').dataLoading('show_bg').done(function (showPopup) { if (showPopup) { App.router.get('backgroundOperationsController').showPopup(); } }); }, gotoConfigs: function () { App.router.get('mainServiceItemController').set('routeToConfigs', true); App.router.transitionTo('main.services.service.configs', this.get('content')); App.router.get('mainServiceItemController').set('routeToConfigs', false); }, showServiceAlertsPopup: function (event) { var service = event.context; return App.ModalPopup.show({ header: Em.I18n.t('services.service.summary.alerts.popup.header').format(service.get('displayName')), autoHeight: false, classNames: ['forty-percent-width-modal'], bodyClass: Em.View.extend({ templateName: require('templates/main/service/info/service_alert_popup'), classNames: ['service-alerts'], controllerBinding: 'App.router.mainAlertDefinitionsController', didInsertElement: function () { Em.run.next(this, function () { App.tooltip(this.$(".timeago")); }); }, willDestroyElement:function () { this.$(".timeago").tooltip('destroy'); }, alerts: function () { var serviceDefinitions = this.get('controller.content').filterProperty('service', service); // definitions should be sorted in order: critical, warning, ok, unknown, other var criticalDefinitions = [], warningDefinitions = [], okDefinitions = [], unknownDefinitions = []; serviceDefinitions.forEach(function (definition) { if (definition.get('isCritical')) { criticalDefinitions.push(definition); serviceDefinitions = serviceDefinitions.without(definition); } else if (definition.get('isWarning')) { warningDefinitions.push(definition); serviceDefinitions = serviceDefinitions.without(definition); } else if (definition.get('isOK')) { okDefinitions.push(definition); serviceDefinitions = serviceDefinitions.without(definition); } else if (definition.get('isUnknown')) { unknownDefinitions.push(definition); serviceDefinitions = serviceDefinitions.without(definition); } }); serviceDefinitions = criticalDefinitions.concat(warningDefinitions, okDefinitions, unknownDefinitions, serviceDefinitions); return serviceDefinitions; }.property('controller.content'), gotoAlertDetails: function (event) { if (event && event.context) { this.get('parentView').hide(); App.router.transitionTo('main.alerts.alertDetails', event.context); } }, closePopup: function () { this.get('parentView').hide(); } }), isHideBodyScroll: false, primary: Em.I18n.t('common.close'), secondary: null }); }, /** * @type {boolean} */ isWidgetLayoutsLoaded: false, /** * @type {boolean} */ isAllSharedWidgetsLoaded: false, /** * @type {boolean} */ isMineWidgetsLoaded: false, /** * load widget layouts across all users in CLUSTER scope * @returns {$.ajax} */ loadWidgetLayouts: function () { this.set('isWidgetLayoutsLoaded', false); return App.ajax.send({ name: 'widgets.layouts.get', sender: this, data: { sectionName: this.get('sectionName') }, success: 'loadWidgetLayoutsSuccessCallback' }); }, loadWidgetLayoutsSuccessCallback: function (data) { App.widgetLayoutMapper.map(data); this.set('isWidgetLayoutsLoaded', true); }, /** * load all shared widgets to show on widget browser * @returns {$.ajax} */ loadAllSharedWidgets: function () { this.set('isAllSharedWidgetsLoaded', false); return App.ajax.send({ name: 'widgets.all.shared.get', sender: this, success: 'loadAllSharedWidgetsSuccessCallback' }); }, /** * success callback of loadAllSharedWidgets * @param {object|null} data */ loadAllSharedWidgetsSuccessCallback: function (data) { var widgetIds = this.get('widgets').mapProperty('id'); if (data.items[0] && data.items.length) { this.set("allSharedWidgets", data.items.filter(function (widget) { return widget.WidgetInfo.widget_type != "HEATMAP"; }).map(function (widget) { var widgetType = widget.WidgetInfo.widget_type; var widgetName = widget.WidgetInfo.widget_name; var widgetId = widget.WidgetInfo.id; return Em.Object.create({ id: widgetId, widgetName: widgetName, description: widget.WidgetInfo.description, widgetType: widgetType, iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png", serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'), added: widgetIds.contains(widgetId), isShared: widget.WidgetInfo.scope == "CLUSTER" }); }) ); } this.set('isAllSharedWidgetsLoaded', true); }, allSharedWidgets: [], mineWidgets: [], /** * load all mine widgets of current user to show on widget browser * @returns {$.ajax} */ loadMineWidgets: function () { this.set('isMineWidgetsLoaded', false); return App.ajax.send({ name: 'widgets.all.mine.get', sender: this, data: { loginName: App.router.get('loginName') }, success: 'loadMineWidgetsSuccessCallback' }); }, /** * success callback of loadMineWidgets * @param {object|null} data */ loadMineWidgetsSuccessCallback: function (data) { var widgetIds = this.get('widgets').mapProperty('id'); if (data.items[0] && data.items.length) { this.set("mineWidgets", data.items.filter(function (widget) { return widget.WidgetInfo.widget_type != "HEATMAP"; }).map(function (widget) { var widgetType = widget.WidgetInfo.widget_type; var widgetName = widget.WidgetInfo.widget_name; var widgetId = widget.WidgetInfo.id; return Em.Object.create({ id: widget.WidgetInfo.id, widgetName: widgetName, description: widget.WidgetInfo.description, widgetType: widgetType, iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png", serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'), added: widgetIds.contains(widgetId), isShared: widget.WidgetInfo.scope == "CLUSTER" }); }) ); } else { this.set("mineWidgets", []); } this.set('isMineWidgetsLoaded', true); }, /** * add widgets, on click handler for "Add" */ addWidget: function (event) { var widgetToAdd = event.context; var activeLayout = this.get('activeWidgetLayout'); var widgetIds = activeLayout.get('widgets').map(function(widget) { return { "id": widget.get("id") } }); widgetIds.pushObject({ "id": widgetToAdd.id }); var data = { "WidgetLayoutInfo": { "display_name": activeLayout.get("displayName"), "id": activeLayout.get("id"), "layout_name": activeLayout.get("layoutName"), "scope": activeLayout.get("scope"), "section_name": activeLayout.get("sectionName"), "widgets": widgetIds } }; widgetToAdd.set('added', !widgetToAdd.added); return App.ajax.send({ name: 'widget.layout.edit', sender: this, data: { layoutId: activeLayout.get("id"), data: data }, success: 'updateActiveLayout' }); }, /** * hide widgets, on click handler for "Added" */ hideWidget: function (event) { var widgetToHide = event.context; var activeLayout = this.get('activeWidgetLayout'); var widgetIds = activeLayout.get('widgets').map(function (widget) { return { "id": widget.get("id") } }); var data = { "WidgetLayoutInfo": { "display_name": activeLayout.get("displayName"), "id": activeLayout.get("id"), "layout_name": activeLayout.get("layoutName"), "scope": activeLayout.get("scope"), "section_name": activeLayout.get("sectionName"), "widgets": widgetIds.filter(function (widget) { return widget.id !== widgetToHide.id; }) } }; widgetToHide.set('added', !widgetToHide.added); return App.ajax.send({ name: 'widget.layout.edit', sender: this, data: { layoutId: activeLayout.get("id"), data: data }, success: 'hideWidgetSuccessCallback' }); }, /** * @param {object|null} data * @param {object} opt * @param {object} params */ hideWidgetSuccessCallback: function (data, opt, params) { params.data.WidgetLayoutInfo.widgets = params.data.WidgetLayoutInfo.widgets.map(function (widget) { return { WidgetInfo: { id: widget.id } } }); App.widgetLayoutMapper.map({items: [params.data]}); this.propertyDidChange('widgets'); }, /** * update current active widget layout */ updateActiveLayout: function () { this.getActiveWidgetLayout(); }, /** * delete widgets, on click handler for "Delete" */ deleteWidget: function (event) { var widget = event.context; var self = this; var confirmMsg = widget.get('isShared') ? Em.I18n.t('dashboard.widgets.browser.action.delete.shared.bodyMsg').format(widget.widgetName) : Em.I18n.t('dashboard.widgets.browser.action.delete.mine.bodyMsg').format(widget.widgetName); var bodyMessage = Em.Object.create({ confirmMsg: confirmMsg, confirmButton: Em.I18n.t('dashboard.widgets.browser.action.delete.btnMsg') }); return App.showConfirmationFeedBackPopup(function (query) { return App.ajax.send({ name: 'widget.action.delete', sender: self, data: { id: widget.id }, success: 'updateWidgetBrowser' }); }, bodyMessage); }, /** * update widget browser content after deleted some widget */ updateWidgetBrowser: function () { this.loadAllSharedWidgets(); this.loadMineWidgets(); }, /** * Share widgets, on click handler for "Share" */ shareWidget: function (event) { var widget = event.context; var self = this; var bodyMessage = Em.Object.create({ confirmMsg: Em.I18n.t('dashboard.widgets.browser.action.share.confirmation'), confirmButton: Em.I18n.t('dashboard.widgets.browser.action.share') }); return App.showConfirmationFeedBackPopup(function (query) { return App.ajax.send({ name: 'widgets.wizard.edit', sender: self, data: { data: { "WidgetInfo": { "widget_name": widget.get("widgetName"), "scope": "CLUSTER" } }, widgetId: widget.get("id") }, success: 'updateWidgetBrowser' }); }, bodyMessage); }, /** * create widget */ createWidget: function () { App.router.send('createServiceWidget', Em.Object.create({ layout: this.get('activeWidgetLayout'), serviceName: this.get('content.serviceName') })); }, /** * edit widget * @param {App.Widget} content */ editWidget: function (content) { content.set('serviceName', this.get('content.serviceName')); App.router.send('editServiceWidget', content); }, /** * launch Widgets Browser popup * @method showPopup * @return {App.ModalPopup} */ goToWidgetsBrowser: function () { var self = this; return App.ModalPopup.show({ header: Em.I18n.t('dashboard.widgets.browser.header'), classNames: ['sixty-percent-width-modal', 'widgets-browser-popup'], onPrimary: function () { this.hide(); self.set('isAllSharedWidgetsLoaded', false); self.set('allSharedWidgets', []); self.set('isMineWidgetsLoaded', false); self.set('mineWidgets', []); }, autoHeight: false, isHideBodyScroll: false, footerClass: Ember.View.extend({ templateName: require('templates/common/modal_popups/widget_browser_footer'), isShowMineOnly: false, onPrimary: function() { this.get('parentView').onPrimary(); } }), isShowMineOnly: false, bodyClass: Ember.View.extend({ templateName: require('templates/common/modal_popups/widget_browser_popup'), controller: self, willInsertElement: function () { this.get('controller').loadAllSharedWidgets(); this.get('controller').loadMineWidgets(); }, isLoaded: Em.computed.and('controller.isAllSharedWidgetsLoaded', 'controller.isMineWidgetsLoaded'), isWidgetEmptyList: Em.computed.empty('filteredContent'), activeService: '', activeStatus: '', content: function () { if (this.get('parentView.isShowMineOnly')) { return this.get('controller.mineWidgets'); } else { // merge my widgets and all shared widgets, no duplicated is allowed var content = []; var widgetMap = {}; var allWidgets = this.get('controller.allSharedWidgets').concat(this.get('controller.mineWidgets')); allWidgets.forEach(function(widget) { if (!widgetMap[widget.get("id")]) { content.pushObject(widget); widgetMap[widget.get("id")] = true; } }); return content; } }.property('controller.allSharedWidgets.length', 'controller.isAllSharedWidgetsLoaded', 'controller.mineWidgets.length', 'controller.isMineWidgetsLoaded', 'parentView.isShowMineOnly'), /** * displaying content filtered by service name and status. */ filteredContent: function () { var activeService = this.get('activeService') ? this.get('activeService') : this.get('controller.content.serviceName'); var result = []; this.get('content').forEach(function (widget) { if (widget.get('serviceName').indexOf(activeService) >= 0) { result.pushObject(widget); } }); return result; }.property('content', 'activeService', 'activeStatus'), /** * service name filter */ services: function () { var view = this; var services = App.Service.find().filter(function(item){ var stackService = App.StackService.find().findProperty('serviceName', item.get('serviceName')); return stackService.get('isServiceWithWidgets'); }); return services.map(function (service) { return Em.Object.create({ value: service.get('serviceName'), label: service.get('displayName'), isActive: function () { var activeService = view.get('activeService') ? view.get('activeService') : view.get('controller.content.serviceName'); return this.get('value') == activeService; }.property('value', 'view.activeService') }) }); }.property('activeService'), filterByService: function (event) { this.set('activeService', event.context); }, createWidget: function () { this.get('parentView').onPrimary(); this.get('controller').createWidget(); }, ensureTooltip: function () { Em.run.later(this, function () { App.tooltip($("[rel='shared-icon-tooltip']")); }, 1000); }.observes('activeService', 'parentView.isShowMineOnly'), didInsertElement: function () { this.ensureTooltip(); } }) }); } });