blob: efbfebff5840dcdafea7657ff44ba0c7206b05bb [file] [log] [blame]
/**
* 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 validator = require('utils/validator');
var numericUtils = require('utils/number_utils');
App.AlertConfigProperty = Ember.Object.extend({
/**
* label to be shown for config property
* @type {String}
*/
label: '',
/**
* PORT|METRIC|AGGREGATE
* @type {String}
*/
type: '',
/**
* config property value
* @type {*}
*/
value: null,
/**
* property value cache to realise undo function
* @type {*}
*/
previousValue: null,
/**
* define either input is disabled or enabled
* @type {Boolean}
*/
isDisabled: false,
/**
* options that Select list will have
* @type {Array}
*/
options: [],
/**
* input displayType
* one of 'textFields', 'textArea', 'select' or 'threshold'
* @type {String}
*/
displayType: '',
/**
* unit to be shown with value
* @type {String}
*/
unit: null,
/**
* space separated list of css class names to use
* @type {String}
*/
classNames: '',
/**
* define whether row with property should be shifted right
* @type {Boolean}
*/
isShifted: false,
/**
* name or names of properties related to config
* may be either string for one property or array of strings for multiple properties
* if this property is array, then <code>apiFormattedValue</code> should also be an array
* example: <code>apiProperty[0]</code> relates to <code>apiFormattedValue[0]</code>
* @type {String|Array}
*/
apiProperty: '',
/**
* for some metrics properties may be set true or false
* depending on what property is related to (JMX or Ganglia)
*/
isJMXMetric: null,
/**
* define place to show label
* if true - label is shown before input
* if false - label is shown after input
* @type {Boolean}
*/
isPreLabeled: Em.computed.notExistsIn('displayType', ['radioButton']),
/**
* value converted to appropriate format for sending to server
* should be computed property
* should be defined in child class
* @type {*}
*/
apiFormattedValue: Em.computed.alias('value'),
/**
* define if property was changed by user
* @type {Boolean}
*/
wasChanged: function () {
return this.get('previousValue') !== null && this.get('value') !== this.get('previousValue');
}.property('value', 'previousValue'),
/**
* view class according to <code>displayType</code>
* @type {Em.View}
*/
viewClass: function () {
var displayType = this.get('displayType');
switch (displayType) {
case 'textField':
return App.AlertConfigTextFieldView;
case 'textArea':
return App.AlertConfigTextAreaView;
case 'select':
return App.AlertConfigSelectView;
case 'threshold':
return App.AlertConfigThresholdView;
case 'radioButton':
return App.AlertConfigRadioButtonView;
case 'parameter':
return App.AlertConfigParameterView;
default:
}
}.property('displayType'),
/**
* Define whether property is valid
* Computed property
* Should be defined in child class
* @type {Boolean}
*/
isValid: function () {
return true;
}.property()
});
App.AlertConfigProperties = {
AlertName: App.AlertConfigProperty.extend({
name: 'alert_name',
label: 'Alert Name',
displayType: 'textField',
classNames: 'alert-text-input',
apiProperty: 'name'
}),
AlertNameSelected: App.AlertConfigProperty.extend({
name: 'alert_name',
label: 'Alert Name',
displayType: 'select',
apiProperty: 'name'
}),
ServiceAlertType: App.AlertConfigProperty.extend({
name: 'alert_type_service',
label: 'Service Alert Definition',
displayType: 'radioButton',
group: 'alert_type'
}),
HostAlertType: App.AlertConfigProperty.extend({
name: 'alert_type_host',
label: 'Host Alert Definition',
displayType: 'radioButton',
group: 'alert_type'
}),
Service: App.AlertConfigProperty.extend({
name: 'service',
label: 'Service',
displayType: 'select',
apiProperty: 'service_name',
apiFormattedValue: function () {
return App.StackService.find().findProperty('displayName', this.get('value')).get('serviceName');
}.property('value')
}),
Component: App.AlertConfigProperty.extend({
name: 'component',
label: 'Component',
displayType: 'select',
apiProperty: 'component_name',
apiFormattedValue: function () {
return App.StackServiceComponent.find().findProperty('displayName', this.get('value')).get('componentName');
}.property('value')
}),
Scope: App.AlertConfigProperty.extend({
name: 'scope',
label: 'Scope',
displayType: 'select',
apiProperty: 'scope',
apiFormattedValue: function () {
return this.get('value').toUpperCase();
}.property('value')
}),
Description: App.AlertConfigProperty.extend({
name: 'description',
label: 'Description',
displayType: 'textArea',
classNames: 'alert-config-text-area',
// todo: check value after API will be provided
apiProperty: 'description'
}),
Interval: App.AlertConfigProperty.extend({
name: 'interval',
label: 'Check Interval',
displayType: 'textField',
unit: 'Minute',
classNames: 'alert-interval-input',
apiProperty: 'interval',
isValid: function () {
var value = this.get('value');
if (!value) return false;
return String(value) === String(parseInt(value, 10)) && value >= 1;
}.property('value')
}),
/**
* Implements threshold
* Main difference from other alertConfigProperties:
* it has two editable parts - <code>value</code> and <code>text</code>
* User may configure it to edit only one of them (use flags <code>showInputForValue</code> and <code>showInputForText</code>)
* This flags also determines update value and text in the API-request or not (see <code>App.AlertConfigProperties.Thresholds</code> for more examples)
*
* @type {App.AlertConfigProperty.Threshold}
*/
Threshold: App.AlertConfigProperty.extend({
name: 'threshold',
/**
* Property text cache to realise undo function
* @type {*}
*/
previousText: null,
label: '',
/**
* OK|WARNING|CRITICAL
* @type {string}
*/
badge: '',
/**
* threshold-value
* @type {string}
*/
value: '',
/**
* Type of value. This will be a fixed set of types (like %).
*/
valueMetric: null,
/**
* Value actually displayed to the user. This value is transformed
* based on the limited types of 'valueMetric's. Mappings from
* 'value' to 'displayValue' is handled by observers.
*/
displayValue: '',
/**
* threshold-text
* @type {string}
*/
text: '',
displayType: 'threshold',
classNames: 'alert-thresholds-input',
apiProperty: [],
init: function () {
this.set('displayValue', this.getNewValue());
this._super();
},
/**
* @type {string[]}
*/
apiFormattedValue: function () {
var ret = [];
if (this.get('showInputForValue')) {
ret.push(this.get('value'));
}
if (this.get('showInputForText')) {
ret.push(this.get('text'));
}
return ret;
}.property('value', 'text', 'showInputForValue', 'showInputForText'),
/**
* Determines if <code>value</code> should be visible and editable (if not - won't update API-value)
* @type {bool}
*/
showInputForValue: true,
/**
* Determines if <code>text</code> should be visible and editable (if not - won't update API-text)
* @type {bool}
*/
showInputForText: true,
/**
* Custom css-class for different badges
* type {string}
*/
badgeCssClass: Em.computed.format('alert-state-{0}', 'badge'),
/**
* Determines if <code>value</code> or <code>text</code> were changed
* @type {bool}
*/
wasChanged: function () {
return this.get('previousValue') !== null && this.get('value') !== this.get('previousValue') ||
this.get('previousText') !== null && this.get('text') !== this.get('previousText');
}.property('value', 'text', 'previousValue', 'previousText'),
/**
* May be redefined in child-models, mixins etc
* @method getValue
* @returns {string}
*/
getNewValue: function () {
return this.get('value');
},
valueWasChanged: function () {
var displayValue = this.get('displayValue');
var newDisplayValue = this.getNewValue();
if (newDisplayValue !== displayValue && !(isNaN(newDisplayValue) ||isNaN(displayValue))) {
this.set('displayValue', newDisplayValue);
}
}.observes('value'),
/**
* May be redefined in child-models, mixins etc
* @method getDisplayValue
* @returns {string}
*/
getNewDisplayValue: function () {
return this.get('displayValue');
},
displayValueWasChanged: function () {
var value = this.get('value');
var newValue = this.getNewDisplayValue();
if (newValue !== value && !(isNaN(newValue) ||isNaN(value))) {
this.set('value', newValue);
}
}.observes('displayValue'),
/**
* Check if <code>displayValue</code> is valid float number
* If this value isn't shown (see <code>showInputForValue</code>), result is always true
* @return {boolean}
*/
isValid: function () {
if (!this.get('showInputForValue')) {
return true;
}
var value = this.get('displayValue');
if (Em.isNone(value)) {
return false;
}
value = ('' + value).trim();
//only allow 1/10th of a second
if (numericUtils.getFloatDecimals(value) > 1) {
return false;
}
return validator.isValidFloat(value);
}.property('displayValue', 'showInputForValue')
}),
URI: App.AlertConfigProperty.extend({
name: 'uri',
label: 'URI',
displayType: 'textField',
classNames: 'alert-text-input',
apiProperty: 'source.uri'
}),
URIExtended: App.AlertConfigProperty.extend({
name: 'uri',
label: 'URI',
displayType: 'textArea',
classNames: 'alert-config-text-area',
apiProperty: 'source.uri',
apiFormattedValue: function () {
var result = {};
try {
result = JSON.parse(this.get('value'));
} catch (e) {
console.error('Wrong format of URI');
}
return result;
}.property('value')
}),
DefaultPort: App.AlertConfigProperty.extend({
name: 'default_port',
label: 'Default Port',
displayType: 'textField',
classNames: 'alert-port-input',
apiProperty: 'source.default_port'
}),
Path: App.AlertConfigProperty.extend({
name: 'path',
label: 'Path',
displayType: 'textField',
classNames: 'alert-text-input',
apiProperty: 'source.path'
}),
Metrics: App.AlertConfigProperty.extend({
name: 'metrics',
label: 'JMX/Ganglia Metrics',
displayType: 'textArea',
classNames: 'alert-config-text-area',
apiProperty: Em.computed.ifThenElse('isJMXMetric', 'source.jmx.property_list', 'source.ganglia.property_list'),
apiFormattedValue: function () {
return this.get('value').split(',\n');
}.property('value')
}),
FormatString: App.AlertConfigProperty.extend({
name: 'metrics_string',
label: 'Format String',
displayType: 'textArea',
classNames: 'alert-config-text-area',
apiProperty: Em.computed.ifThenElse('isJMXMetric', 'source.jmx.value', 'source.ganglia.value')
}),
Parameter: App.AlertConfigProperty.extend({
name: 'parameter',
displayType: 'parameter',
badge: Em.computed.alias('threshold'),
thresholdExists: Em.computed.bool('threshold'),
thresholdNotExists: Em.computed.empty('threshold'),
/**
* Custom css-class for different badges
* type {string}
*/
badgeCssClass: Em.computed.format('alert-state-{0}', 'badge'),
})
};
App.AlertConfigProperties.Parameters = {
StringMixin: Em.Mixin.create({
isValid: function () {
var value = this.get('value');
return String(value).trim() !== '';
}.property('value')
}),
NumericMixin: Em.Mixin.create({
isValid: function () {
var value = this.get('value');
if (!value) {
return false;
}
value = ('' + value).trim();
if (!numericUtils.isPositiveNumber(value)) {
return false;
}
value = parseFloat(value);
return !isNaN(value);
}.property('value')
}),
PercentageMixin: Em.Mixin.create({
isValid: function () {
var value = this.get('value');
if (!value) {
return false;
}
if (!validator.isValidFloat(value) || !numericUtils.isPositiveNumber(value)) {
return false;
}
value = String(value).trim();
value = parseFloat(value);
return !isNaN(value) && value > 0;
}.property('value')
})
};
App.AlertConfigProperties.Thresholds = {
OkThreshold: App.AlertConfigProperties.Threshold.extend({
badge: 'OK',
name: 'ok_threshold',
apiProperty: function () {
var ret = [];
if (this.get('showInputForValue')) {
ret.push('source.reporting.ok.value');
}
if (this.get('showInputForText')) {
ret.push('source.reporting.ok.text');
}
return ret;
}.property('showInputForValue', 'showInputForText')
}),
WarningThreshold: App.AlertConfigProperties.Threshold.extend({
badge: 'WARNING',
name: 'warning_threshold',
apiProperty: function () {
var ret = [];
if (this.get('showInputForValue')) {
ret.push('source.reporting.warning.value');
}
if (this.get('showInputForText')) {
ret.push('source.reporting.warning.text');
}
return ret;
}.property('showInputForValue', 'showInputForText')
}),
CriticalThreshold: App.AlertConfigProperties.Threshold.extend({
badge: 'CRITICAL',
name: 'critical_threshold',
apiProperty: function () {
var ret = [];
if (this.get('showInputForValue')) {
ret.push('source.reporting.critical.value');
}
if (this.get('showInputForText')) {
ret.push('source.reporting.critical.text');
}
return ret;
}.property('showInputForValue', 'showInputForText')
}),
/**
* Mixin for <code>App.AlertConfigProperties.Threshold</code>
* Used to validate values in percentage range (0..1]
* @type {Em.Mixin}
*/
PercentageMixin: Em.Mixin.create({
isValid: function () {
var value = this.get('displayValue');
if (!value) {
return false;
}
value = ('' + value).trim();
if (numericUtils.getFloatDecimals(value)) {
return false;
}
value = parseFloat(value);
//do not allow float values
if (parseInt(value, 10) !== value) {
return false;
}
return this.get('showInputForValue') ? !isNaN(value) && value > 0 : true;
}.property('displayValue', 'showInputForValue')
}),
/**
* Mixin for <code>App.AlertConfigProperties.Threshold</code>
* Used to validate values that should be greater than 0
* @type {Em.Mixin}
*/
PositiveMixin: Em.Mixin.create({
isValid: function () {
if (!this.get('showInputForValue')) {
return true;
}
var value = this.get('displayValue');
if (!value) {
return false;
}
//only allow 1/10th of a second
if (numericUtils.getFloatDecimals(value) > 1) {
return false;
}
value = ('' + value).trim();
value = parseFloat(value);
return !isNaN(value) && value > 0;
}.property('displayValue', 'showInputForValue')
})
};