/** * 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'); var dateUtils = require('utils/date/date'); function computedOnSummaryState(state) { return Em.computed('summary.' + state, 'summary.' + state + '.count', 'summary.' + state + '.maintenanceCount', function () { var summary = Em.get(this, 'summary'); return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount); }); } App.AlertDefinition = DS.Model.extend({ name: DS.attr('string'), label: DS.attr('string'), description: DS.attr('string'), service: DS.belongsTo('App.Service'), serviceName: DS.attr('string'), componentName: DS.attr('string'), enabled: DS.attr('boolean'), scope: DS.attr('string'), interval: DS.attr('number'), type: DS.attr('string'), groups: DS.hasMany('App.AlertGroup'), reporting: DS.hasMany('App.AlertReportDefinition'), parameters: DS.hasMany('App.AlertDefinitionParameter'), lastTriggered: DS.attr('number'), lastTriggeredRaw: DS.attr('number'), //relates only to SCRIPT-type alert definition location: DS.attr('string'), //relates only to AGGREGATE-type alert definition alertName: DS.attr('string'), //relates only to WEB and METRIC types of alert definition uri: DS.belongsTo('App.AlertMetricsUriDefinition'), //relates only METRIC-type alert definition jmx: DS.belongsTo('App.AlertMetricsSourceDefinition'), ganglia: DS.belongsTo('App.AlertMetricsSourceDefinition'), //relates only PORT-type alert definition defaultPort: DS.attr('number'), portUri: DS.attr('string'), /** * Raw data from AlertDefinition/source * used to format request content for updating alert definition * @type {Object} */ rawSourceData: {}, /** * Counts of alert grouped by their status * Format: * * { * "CRITICAL": { * count: 1, * maintenanceCount: 0 * }, * "OK": { * count: 0, * maintenanceCount: 1 * }, * "UNKNOWN": { * count: 0, * maintenanceCount: 0 * }, * "WARNING": { * count: 1, * maintenanceCount: 1 * } * } * * @type {object} */ summary: {}, /** * Formatted timestamp for latest alert triggering for current alertDefinition * @type {string} */ lastTriggeredFormatted: function () { return dateUtils.dateFormat(this.get('lastTriggered')); }.property('lastTriggered'), /** * Formatted timestamp with $.timeago * @type {string} */ lastTriggeredAgoFormatted: function () { var lastTriggered = this.get('lastTriggeredRaw'); return lastTriggered ? $.timeago(new Date(lastTriggered)) : ''; }.property('lastTriggeredRaw'), lastTriggeredVerboseDisplay: function () { var lastTriggered = this.get('lastTriggered'); return Em.I18n.t('models.alert_definition.triggered.verbose').format(dateUtils.dateFormat(lastTriggered)); }.property('lastTriggered'), /** * Formatted timestamp in format: for 4 days * @type {string} */ lastTriggeredForFormatted: function () { var lastTriggered = this.get('lastTriggeredRaw'); var previousSuffixAgo = $.timeago.settings.strings.suffixAgo; var previousPrefixAgo = $.timeago.settings.strings.prefixAgo; $.timeago.settings.strings.suffixAgo = null; $.timeago.settings.strings.prefixAgo = 'for'; var triggeredFor = lastTriggered ? $.timeago(new Date(lastTriggered)) : ''; $.timeago.settings.strings.suffixAgo = previousSuffixAgo; $.timeago.settings.strings.prefixAgo = previousPrefixAgo; return triggeredFor; }.property('lastTriggeredRaw'), /** * Formatted displayName for componentName * @type {String} */ componentNameFormatted: Em.computed.formatRole('componentName'), /** * Status generates from child-alerts * Format: OK(1) WARN(2) CRIT(1) UNKN(1) * If single host: show: OK/WARNING/CRITICAL/UNKNOWN * If some there are no alerts with some state, this state isn't shown * If no OK/WARN/CRIT/UNKN state, then show PENDING * Order is equal to example * @type {string} */ status: function () { var order = this.get('order'), summary = this.get('summary'), hostCnt = 0, self = this; order.forEach(function (state) { hostCnt += summary[state] ? summary[state].count + summary[state].maintenanceCount : 0; }); if (hostCnt > 1) { // multiple hosts return order.map(function (state) { var shortState = self.get('shortState')[state]; var result = ''; result += summary[state].count ? '' + shortState + ' (' + summary[state].count + ')' : ''; // add status with maintenance mode icon result += summary[state].maintenanceCount ? ' ' + shortState + ' (' + summary[state].maintenanceCount + ')' : ''; return result; }).without('').join(' '); } else if (hostCnt === 1) { // single host, single status return order.map(function (state) { var shortState = self.get('shortState')[state]; var result = ''; result += summary[state].count ? '' + shortState + '' : ''; // add status with maintenance mode icon result += summary[state].maintenanceCount ? ' ' + shortState + '' : ''; return result; }).without('').join(' '); } else if (!hostCnt) { // none return 'NONE'; } return ''; }.property('summary'), latestText: function () { var order = this.get('order'), summary = this.get('summary'), text = ''; order.forEach(function (state) { var cnt = summary[state] ? summary[state].count + summary[state].maintenanceCount : 0; if (cnt > 0) { text = summary[state].latestText; } }); return text; }.property('summary'), isAmbariService: Em.computed.equal('service._id', 'AMBARI'), isAmbariAgentComponent: Em.computed.equal('componentName', 'AMBARI_AGENT'), isHostAlertDefinition: Em.computed.and('isAmbariService', 'isAmbariAgentComponent'), typeIconClass: function () { var typeIcons = this.get('typeIcons'), type = this.get('type'); return typeIcons[type]; }.property('type'), /** * if this definition is in state: CRITICAL / WARNING, if true, will show up in alerts fast access popup * instances with maintenance mode ON are ignored * @type {boolean} */ isCriticalOrWarning: function () { return !!(this.get('summary.CRITICAL.count') || this.get('summary.WARNING.count')); }.property('summary'), /** * if this definition is in state: CRITICAL * @type {boolean} */ isCritical: computedOnSummaryState('CRITICAL'), /** * if this definition is in state: WARNING * @type {boolean} */ isWarning: computedOnSummaryState('WARNING'), /** * if this definition is in state: OK * @type {boolean} */ isOK: computedOnSummaryState('OK'), /** * if this definition is in state: UNKNOWN * @type {boolean} */ isUnknown: computedOnSummaryState('UNKNOWN'), /** * For alerts we will have processes which are not typical * cluster services - like Ambari-Server. This method unifies * cluster services and other services into a common display-name. * @see App.AlertInstance#serviceDisplayName() */ serviceDisplayName: function () { var serviceName = this.get('service.displayName'); if (!serviceName) { serviceName = this.get('serviceName'); if (serviceName) { serviceName = serviceName.toCapital(); } } return serviceName; }.property('serviceName', 'service.displayName'), /** * List of css-classes for alert types * @type {object} */ typeIcons: { 'METRIC': 'icon-bolt', 'SCRIPT': 'icon-file-text', 'WEB': 'icon-globe', 'PORT': 'icon-signin', 'AGGREGATE': 'icon-plus', 'SERVER': 'icon-desktop', 'RECOVERY': 'icon-desktop' }, /** * Sort on load definitions by this severity order */ severityOrder: ['CRITICAL', 'WARNING', 'OK', 'UNKNOWN', 'PENDING'], order: ['OK', 'WARNING', 'CRITICAL', 'UNKNOWN'], shortState: { 'CRITICAL': 'CRIT', 'WARNING': 'WARN', 'OK': 'OK', 'UNKNOWN': 'UNKWN', 'PENDING': 'NONE' } }); App.AlertDefinition.reopenClass({ /** * Return function to sort list of AlertDefinitions by their status * It sorts according to severityOrder * @param {boolean} order true - DESC, false - ASC * @returns {Function} * @method getSortDefinitionsByStatus */ getSortDefinitionsByStatus: function (order) { return function (a, b) { var aSummary = a.get('summary'), bSummary = b.get('summary'), stOrder = a.get('severityOrder'), ret = 0; for (var i = 0; i < stOrder.length; i++) { var aV = Em.isNone(aSummary[stOrder[i]]) ? 0 : aSummary[stOrder[i]].count + aSummary[stOrder[i]].maintenanceCount, bV = Em.isNone(bSummary[stOrder[i]]) ? 0 : bSummary[stOrder[i]].count + bSummary[stOrder[i]].maintenanceCount; ret = bV - aV; if (ret !== 0) { break; } } return order ? ret : -ret; }; } }); App.AlertDefinitionParameter = DS.Model.extend({ name: DS.attr('string'), displayName: DS.attr('string'), units: DS.attr('string'), value: DS.attr('string'), description: DS.attr('string'), type: DS.attr('string'), threshold: DS.attr('string') }); App.AlertReportDefinition = DS.Model.extend({ type: DS.attr('string'), text: DS.attr('string'), value: DS.attr('number') }); App.AlertMetricsSourceDefinition = DS.Model.extend({ propertyList: [], value: DS.attr('string') }); App.AlertMetricsUriDefinition = DS.Model.extend({ http: DS.attr('string'), https: DS.attr('string'), httpsProperty: DS.attr('string'), httpsPropertyValue: DS.attr('string'), connectionTimeout: DS.attr('number') }); App.AlertDefinition.FIXTURES = []; App.AlertReportDefinition.FIXTURES = []; App.AlertMetricsSourceDefinition.FIXTURES = []; App.AlertMetricsUriDefinition.FIXTURES = []; App.AlertDefinitionParameter.FIXTURES = [];