You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2015/08/21 12:10:14 UTC
[1/2] ambari git commit: AMBARI-12845. Alert Instances overriding
issue (onehiporenko)
Repository: ambari
Updated Branches:
refs/heads/trunk 995343371 -> 66d0b6e2c
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alerts/alert_notification.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alerts/alert_notification.js b/ambari-web/app/models/alerts/alert_notification.js
new file mode 100644
index 0000000..c2d7570
--- /dev/null
+++ b/ambari-web/app/models/alerts/alert_notification.js
@@ -0,0 +1,33 @@
+/**
+ * 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.AlertNotification = DS.Model.extend({
+ id: DS.attr('number'),
+ name: DS.attr('string'),
+ type: DS.attr('string'),
+ description: DS.attr('string'),
+ groups: DS.hasMany('App.AlertGroup'),
+ global: DS.attr('boolean'),
+
+ properties: {},
+ alertStates: []
+});
+
+App.AlertNotification.FIXTURES = [];
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/views/main/alerts/definition_details_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/alerts/definition_details_view.js b/ambari-web/app/views/main/alerts/definition_details_view.js
index cbbded9..12b563c 100644
--- a/ambari-web/app/views/main/alerts/definition_details_view.js
+++ b/ambari-web/app/views/main/alerts/definition_details_view.js
@@ -163,7 +163,7 @@ App.AlertInstanceServiceHostView = Em.View.extend({
* Define whether show link for transition to service page
*/
serviceIsLink: function () {
- return App.Service.find().someProperty('serviceName', this.get('instance.service.serviceName'));
+ return App.get('services.all').contains(this.get('instance.service.serviceName'));
}.property('instance.service.serviceName'),
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/views/main/host/host_alerts_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host/host_alerts_view.js b/ambari-web/app/views/main/host/host_alerts_view.js
index 54b4e7c..656787b 100644
--- a/ambari-web/app/views/main/host/host_alerts_view.js
+++ b/ambari-web/app/views/main/host/host_alerts_view.js
@@ -253,10 +253,15 @@ App.MainHostAlertsView = App.TableView.extend({
* @method tooltipsUpdater
*/
tooltipsUpdater: function () {
+ Em.run.once(this,this.tooltipsUpdaterOnce);
+ }.observes('pageContent.@each'),
+
+ tooltipsUpdaterOnce: function() {
+ var self = this;
Em.run.next(this, function () {
- App.tooltip($(".enable-disable-button, .timeago, .alert-text"));
+ App.tooltip(self.$(".enable-disable-button, .timeago, .alert-text"));
});
- }.observes('pageContent.@each'),
+ },
/**
* Run <code>clearFilter</code> in the each child filterView
@@ -268,6 +273,10 @@ App.MainHostAlertsView = App.TableView.extend({
childView.clearFilter();
}
});
+ },
+
+ willDestroyElement: function() {
+ this.$(".enable-disable-button, .timeago, .alert-text").tooltip('destroy');
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/test/models/alert_config_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/alert_config_test.js b/ambari-web/test/models/alert_config_test.js
deleted file mode 100644
index 31f2ce9..0000000
--- a/ambari-web/test/models/alert_config_test.js
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- * 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');
-
-require('models/alert_config');
-
-var model;
-
-describe('App.AlertConfigProperties', function () {
-
- describe('Threshold', function () {
-
- beforeEach(function () {
- model = App.AlertConfigProperties.Threshold.create({});
- });
-
- describe('#apiFormattedValue', function () {
-
- it('should be based on showInputForValue and showInputForText', function () {
-
- model.setProperties({
- value: 'value',
- text: 'text',
- showInputForValue: false,
- showInputForText: false
- });
- expect(model.get('apiFormattedValue')).to.eql([]);
-
- model.set('showInputForValue', true);
- expect(model.get('apiFormattedValue')).to.eql(['value']);
-
- model.set('showInputForText', true);
- expect(model.get('apiFormattedValue')).to.eql(['value', 'text']);
-
- });
-
- });
-
- describe('#valueWasChanged', function () {
-
- it('value change should effect displayValue for AGGREGATE type', function () {
-
- model = App.AlertConfigProperties.Threshold.create(App.AlertConfigProperties.Thresholds.PercentageMixin, {
- value: '0.4',
- valueMetric: '%',
- text: 'text',
- showInputForValue: false,
- showInputForText: false
- });
-
- expect(model.get('displayValue')).to.eql('40');
- });
-
- it('value change should not effect displayValue for not AGGREGATE type', function () {
-
- model = App.AlertConfigProperties.Threshold.create({
- value: '0.4',
- valueMetric: '%',
- text: 'text',
- showInputForValue: false,
- showInputForText: false
- });
-
- expect(model.get('displayValue')).to.eql('0.4');
- });
-
- });
-
- describe('#badgeCssClass', function () {
-
- it ('should be based on badge', function () {
-
- model.set('badge', 'OK');
- expect(model.get('badgeCssClass')).to.equal('alert-state-OK');
-
- });
-
- });
-
- describe('#wasChanged', function () {
-
- Em.A([
- {
- p: {
- previousValue: null,
- previousText: null,
- value: '',
- text: ''
- },
- e: false
- },
- {
- p: {
- previousValue: 'not null',
- previousText: null,
- value: '',
- text: ''
- },
- e: true
- },
- {
- p: {
- previousValue: null,
- previousText: 'not null',
- value: '',
- text: ''
- },
- e: true
- },
- {
- p: {
- previousValue: 'not null',
- previousText: 'not null',
- value: '',
- text: ''
- },
- e: true
- }
- ]).forEach(function (test, i) {
- it('test #' + (i + 1), function () {
- model.setProperties(test.p);
- expect(model.get('wasChanged')).to.equal(test.e);
- });
- });
-
- });
-
- describe('#isValid', function () {
-
- it('should be true if showInputForValue is false', function () {
- model.set('showInputForValue', false);
- expect(model.get('isValid')).to.be.true;
- });
-
- it('should be false if displayValue is null', function () {
- model.set('displayValue', null);
- expect(model.get('isValid')).to.be.false;
-
- model.set('displayValue', undefined);
- expect(model.get('isValid')).to.be.false;
- });
-
- it('should be false if METRIC displayValue is not valid float', function () {
- model.set('displayValue', '$1234.444');
- expect(model.get('isValid')).to.be.false;
-
- model.set('displayValue', 'hello-world!');
- expect(model.get('isValid')).to.be.false;
- });
-
- it('should be true if METRIC displayValue is valid float with at most one decimal', function () {
- model.set('displayValue', '123.4');
- expect(model.get('isValid')).to.be.true;
-
- model.set('displayValue', '123.0');
- expect(model.get('isValid')).to.be.true;
-
- model.set('displayValue', '666');
- expect(model.get('isValid')).to.be.true;
- });
-
- it('should be false if METRIC displayValue is valid float with more than one decimal', function () {
- model.set('displayValue', '123.48');
- expect(model.get('isValid')).to.be.false;
- });
-
- it('should be true for AGGREGATE percentage with precision of 1', function () {
- model = Em.Object.create(App.AlertConfigProperties.Thresholds.PercentageMixin, {
- displayValue: '1',
- showInputForValue: true
- });
-
- expect(model.get('isValid')).to.be.true;
-
- model.set('displayValue', '88');
- expect(model.get('isValid')).to.be.true;
- });
-
- it('should be false for AGGREGATE percentage values with precision smaller than 1', function () {
- model = Em.Object.create(App.AlertConfigProperties.Thresholds.PercentageMixin, {
- displayValue: '70.01',
- showInputForValue: true
- });
-
- expect(model.get('isValid')).to.be.false;
-
- model.set('displayValue', '70.0');
- expect(model.get('isValid')).to.be.false;
-
- model.set('displayValue', '80.000');
- expect(model.get('isValid')).to.be.false;
- });
-
- it('should be true for PORT percentage values with precision of 1/10th', function () {
- model = App.AlertConfigProperties.Threshold.create({
- value: '0.4',
- showInputForValue: true
- })
-
- expect(model.get('isValid')).to.be.true;
-
- model.set('value', '3');
- expect(model.get('isValid')).to.be.true;
-
- model.set('value', '33.0');
- expect(model.get('isValid')).to.be.true;
- });
-
- it('should be false for PORT percentage values with precision greater than 1/10th', function() {
- model = App.AlertConfigProperties.Threshold.create({
- value: '4.234',
- showInputForValue: true
- });
-
- expect(model.get('isValid')).to.be.false;
-
- model.set('value', '44.001');
- expect(model.get('isValid')).to.be.false;
-
- model.set('value', '112.01');
- expect(model.get('isValid')).to.be.false;
- });
-
- });
-
- });
-
- describe('App.AlertConfigProperties.Thresholds', function () {
-
- describe('OkThreshold', function () {
-
- beforeEach(function () {
- model = App.AlertConfigProperties.Thresholds.OkThreshold.create();
- });
-
- describe('#apiProperty', function () {
-
- it('should be based on showInputForValue and showInputForText', function () {
-
- model.setProperties({
- showInputForValue: false,
- showInputForText: false
- });
- expect(model.get('apiProperty')).to.eql([]);
-
- model.set('showInputForValue', true);
- expect(model.get('apiProperty')).to.eql(['source.reporting.ok.value']);
-
- model.set('showInputForText', true);
- expect(model.get('apiProperty')).to.eql(['source.reporting.ok.value', 'source.reporting.ok.text']);
-
- });
-
- });
-
- });
-
- describe('WarningThreshold', function () {
-
- beforeEach(function () {
- model = App.AlertConfigProperties.Thresholds.WarningThreshold.create();
- });
-
- describe('#apiProperty', function () {
-
- it('should be based on showInputForValue and showInputForText', function () {
-
- model.setProperties({
- showInputForValue: false,
- showInputForText: false
- });
- expect(model.get('apiProperty')).to.eql([]);
-
- model.set('showInputForValue', true);
- expect(model.get('apiProperty')).to.eql(['source.reporting.warning.value']);
-
- model.set('showInputForText', true);
- expect(model.get('apiProperty')).to.eql(['source.reporting.warning.value', 'source.reporting.warning.text']);
-
- });
-
- });
-
- });
-
- describe('CriticalThreshold', function () {
-
- beforeEach(function () {
- model = App.AlertConfigProperties.Thresholds.CriticalThreshold.create();
- });
-
- describe('#apiProperty', function () {
-
- it('should be based on showInputForValue and showInputForText', function () {
-
- model.setProperties({
- showInputForValue: false,
- showInputForText: false
- });
- expect(model.get('apiProperty')).to.eql([]);
-
- model.set('showInputForValue', true);
- expect(model.get('apiProperty')).to.eql(['source.reporting.critical.value']);
-
- model.set('showInputForText', true);
- expect(model.get('apiProperty')).to.eql(['source.reporting.critical.value', 'source.reporting.critical.text']);
-
- });
-
- });
-
- });
-
- });
-
-});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/test/models/alert_definition_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/alert_definition_test.js b/ambari-web/test/models/alert_definition_test.js
deleted file mode 100644
index 1e349b1..0000000
--- a/ambari-web/test/models/alert_definition_test.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/**
- * 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');
-
-require('models/alert_definition');
-
-var model;
-
-describe('App.AlertDefinition', function () {
-
- beforeEach(function () {
-
- model = App.AlertDefinition.createRecord();
-
- });
-
- describe('#status', function () {
-
- Em.A([
- {
- summary: {OK: {count: 1, maintenanceCount: 0}, UNKNOWN: {count: 1, maintenanceCount: 0}, WARNING: {count: 2, maintenanceCount: 0}, CRITICAL: {count: 0, maintenanceCount: 0}},
- m: 'No CRITICAL',
- e: '<span class="alert-state-single-host label alert-state-OK">OK (1)</span> ' +
- '<span class="alert-state-single-host label alert-state-WARNING">WARN (2)</span> ' +
- '<span class="alert-state-single-host label alert-state-UNKNOWN">UNKWN (1)</span>'
- },
- {
- summary: {WARNING: {count: 2, maintenanceCount: 0}, CRITICAL: {count: 3, maintenanceCount: 0}, UNKNOWN: {count: 1, maintenanceCount: 0}, OK: {count: 1, maintenanceCount: 0}},
- m: 'All states exists',
- e: '<span class="alert-state-single-host label alert-state-OK">OK (1)</span> ' +
- '<span class="alert-state-single-host label alert-state-WARNING">WARN (2)</span> ' +
- '<span class="alert-state-single-host label alert-state-CRITICAL">CRIT (3)</span> ' +
- '<span class="alert-state-single-host label alert-state-UNKNOWN">UNKWN (1)</span>'
- },
- {
- summary: {OK: {count: 1, maintenanceCount: 0}, UNKNOWN: {count: 0, maintenanceCount: 0}, WARNING: {count: 0, maintenanceCount: 0}, CRITICAL: {count: 0, maintenanceCount: 0}},
- m: 'Single host',
- e: '<span class="alert-state-single-host label alert-state-OK">OK</span>'
- },
- {
- summary: {OK: {count: 0, maintenanceCount: 1}, UNKNOWN: {count: 0, maintenanceCount: 0}, WARNING: {count: 0, maintenanceCount: 0}, CRITICAL: {count: 0, maintenanceCount: 0}},
- m: 'Maintenance OK alert',
- e: '<span class="alert-state-single-host label alert-state-PENDING"><span class="icon-medkit"></span> OK</span>'
- },
- {
- summary: {},
- m: 'Pending',
- e: '<span class="alert-state-single-host label alert-state-PENDING">NONE</span>'
- }
- ]).forEach(function (test) {
- it(test.m, function () {
- model.set('summary', test.summary);
- expect(model.get('status')).to.equal(test.e);
- });
- });
-
- });
-
- describe('#isCriticalOrWarning', function () {
-
- Em.A([
- {summary: {CRITICAL: {count: 1, maintenanceCount: 0}}, e: true},
- {summary: {CRITICAL: {count: 0, maintenanceCount: 1}}, e: false},
- {summary: {CRITICAL: {count: 1, maintenanceCount: 1}}, e: true},
- {summary: {WARNING: {count: 1, maintenanceCount: 0}}, e: true},
- {summary: {OK: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {UNKNOWN: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {}, e: false}
- ]).forEach(function (test, i) {
- it('test ' + (i + 1), function () {
- model.set('summary', test.summary);
- expect(model.get('isCriticalOrWarning')).to.equal(test.e);
- });
- });
-
- });
-
- describe('#isCritical', function () {
-
- Em.A([
- {summary: {CRITICAL: {count: 1, maintenanceCount: 0}}, e: true},
- {summary: {WARNING: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {OK: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {UNKNOWN: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {}, e: false}
- ]).forEach(function (test, i) {
- it('test ' + (i + 1), function () {
- model.set('summary', test.summary);
- expect(model.get('isCritical')).to.equal(test.e);
- });
- });
-
- });
-
- describe('#isWarning', function () {
-
- Em.A([
- {summary: {CRITICAL: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {WARNING: {count: 1, maintenanceCount: 0}}, e: true},
- {summary: {OK: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {UNKNOWN: {count: 1, maintenanceCount: 0}}, e: false},
- {summary: {}, e: false}
- ]).forEach(function (test, i) {
- it('test ' + (i + 1), function () {
- model.set('summary', test.summary);
- expect(model.get('isWarning')).to.equal(test.e);
- });
- });
-
- });
-
- describe('#lastTriggeredAgoFormatted', function () {
-
- it('should be empty', function () {
- model.set('lastTriggered', 0);
- expect(model.get('lastTriggeredAgoFormatted')).to.equal('');
- });
-
- it('should not be empty', function () {
- model.set('lastTriggered', new Date().getTime() - 61000);
- expect(model.get('lastTriggeredAgoFormatted')).to.equal('about a minute ago');
- });
-
- });
-
- describe('#serviceDisplayName', function () {
-
- it('should get name for non-existing service', function () {
- model.set('serviceName', 'FOOBAR');
- expect(model.get('serviceDisplayName')).to.equal('Foobar');
- });
-
- });
-
- describe('#componentNameFormatted', function () {
-
- beforeEach(function () {
- sinon.stub(App.format, 'role', function (a) {
- return 'role ' + a;
- });
- });
-
- it('should wrap component name by App.format.role method', function () {
- model.set('componentName', 'test');
- var result = model.get('componentNameFormatted');
- expect(result).to.equal('role test');
- });
-
- afterEach(function () {
- App.format.role.restore();
- });
-
-
- });
-
- describe('REOPEN', function () {
-
- describe('#getSortDefinitionsByStatus', function () {
-
- Em.A([
- {
- a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
- b: App.AlertDefinition.createRecord({summary: {WARNING: {count: 1, maintenanceCount: 0}}}),
- order: true,
- e: -1
- },
- {
- a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 2, maintenanceCount: 0}}}),
- b: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
- order: true,
- e: -1
- },
- {
- a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
- b: App.AlertDefinition.createRecord({summary: {WARNING: {count: 1, maintenanceCount: 0}}}),
- order: false,
- e: 1
- },
- {
- a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 2, maintenanceCount: 0}}}),
- b: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
- order: false,
- e: 1
- }
- ]).forEach(function(test, i) {
- it('test #' + (i + 1), function () {
- var func = App.AlertDefinition.getSortDefinitionsByStatus(test.order);
- expect(func(test.a, test.b)).to.equal(test.e);
- });
- });
-
- });
-
- });
-
-});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/test/models/alert_instance_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/alert_instance_test.js b/ambari-web/test/models/alert_instance_test.js
deleted file mode 100644
index dc692e2..0000000
--- a/ambari-web/test/models/alert_instance_test.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * 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');
-
-require('models/alert_instance');
-
-var model;
-
-describe('App.AlertInstance', function () {
-
- beforeEach(function () {
-
- model = App.AlertInstance.createRecord();
-
- });
-
- describe('#serviceDisplayName', function () {
-
- it('should get name for non-existing service', function () {
- model.set('serviceName', 'FOOBAR');
- expect(model.get('serviceDisplayName')).to.equal('Foobar');
- });
-
- });
-
- describe('#statusChangedAndLastCheckedFormatted', function () {
-
- it('should Status Changed before Last Checked', function () {
-
- var lastCheckedFormatted = '123',
- lastTriggeredFormatted = '321';
-
- model.reopen({
- lastCheckedFormatted: lastCheckedFormatted,
- lastTriggeredFormatted: lastTriggeredFormatted
- });
- var status = model.get('statusChangedAndLastCheckedFormatted');
- expect(status.indexOf(lastCheckedFormatted) > status.indexOf(lastTriggeredFormatted)).to.be.true;
-
- });
-
- });
-
- describe('#status', function () {
-
- it('should show maint mode icon', function () {
-
- model.set('maintenanceState', 'ON');
- model.set('state', 'OK');
- var status = model.get('status');
-
- expect(status).to.equal('<div class="label alert-state-single-host alert-state-PENDING"><span class="icon-medkit"></span> OK</div>');
-
- });
-
- it('should not show maint mode icon', function () {
-
- model.set('maintenanceState', 'OFF');
- model.set('state', 'OK');
- var status = model.get('status');
-
- expect(status).to.equal('<div class="label alert-state-single-host alert-state-OK">OK</div>');
-
- });
-
- });
-
- describe('#escapeSpecialCharactersFromTooltip', function () {
- it('it Should Display Alert Without special characters "<" and ">"', function () {
-
- model.set('text', '<urlopen error [Errno 111] Connection refused>');
- var resultedText = model.get('escapeSpecialCharactersFromTooltip');
-
- expect(resultedText).to.equal('urlopen error [Errno 111] Connection refused');
- });
- });
-
-});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/test/models/alerts/alert_config_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/alerts/alert_config_test.js b/ambari-web/test/models/alerts/alert_config_test.js
new file mode 100644
index 0000000..ae3f859
--- /dev/null
+++ b/ambari-web/test/models/alerts/alert_config_test.js
@@ -0,0 +1,332 @@
+/**
+ * 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');
+
+require('models/alerts/alert_config');
+
+var model;
+
+describe('App.AlertConfigProperties', function () {
+
+ describe('Threshold', function () {
+
+ beforeEach(function () {
+ model = App.AlertConfigProperties.Threshold.create({});
+ });
+
+ describe('#apiFormattedValue', function () {
+
+ it('should be based on showInputForValue and showInputForText', function () {
+
+ model.setProperties({
+ value: 'value',
+ text: 'text',
+ showInputForValue: false,
+ showInputForText: false
+ });
+ expect(model.get('apiFormattedValue')).to.eql([]);
+
+ model.set('showInputForValue', true);
+ expect(model.get('apiFormattedValue')).to.eql(['value']);
+
+ model.set('showInputForText', true);
+ expect(model.get('apiFormattedValue')).to.eql(['value', 'text']);
+
+ });
+
+ });
+
+ describe('#valueWasChanged', function () {
+
+ it('value change should effect displayValue for AGGREGATE type', function () {
+
+ model = App.AlertConfigProperties.Threshold.create(App.AlertConfigProperties.Thresholds.PercentageMixin, {
+ value: '0.4',
+ valueMetric: '%',
+ text: 'text',
+ showInputForValue: false,
+ showInputForText: false
+ });
+
+ expect(model.get('displayValue')).to.eql('40');
+ });
+
+ it('value change should not effect displayValue for not AGGREGATE type', function () {
+
+ model = App.AlertConfigProperties.Threshold.create({
+ value: '0.4',
+ valueMetric: '%',
+ text: 'text',
+ showInputForValue: false,
+ showInputForText: false
+ });
+
+ expect(model.get('displayValue')).to.eql('0.4');
+ });
+
+ });
+
+ describe('#badgeCssClass', function () {
+
+ it ('should be based on badge', function () {
+
+ model.set('badge', 'OK');
+ expect(model.get('badgeCssClass')).to.equal('alert-state-OK');
+
+ });
+
+ });
+
+ describe('#wasChanged', function () {
+
+ Em.A([
+ {
+ p: {
+ previousValue: null,
+ previousText: null,
+ value: '',
+ text: ''
+ },
+ e: false
+ },
+ {
+ p: {
+ previousValue: 'not null',
+ previousText: null,
+ value: '',
+ text: ''
+ },
+ e: true
+ },
+ {
+ p: {
+ previousValue: null,
+ previousText: 'not null',
+ value: '',
+ text: ''
+ },
+ e: true
+ },
+ {
+ p: {
+ previousValue: 'not null',
+ previousText: 'not null',
+ value: '',
+ text: ''
+ },
+ e: true
+ }
+ ]).forEach(function (test, i) {
+ it('test #' + (i + 1), function () {
+ model.setProperties(test.p);
+ expect(model.get('wasChanged')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isValid', function () {
+
+ it('should be true if showInputForValue is false', function () {
+ model.set('showInputForValue', false);
+ expect(model.get('isValid')).to.be.true;
+ });
+
+ it('should be false if displayValue is null', function () {
+ model.set('displayValue', null);
+ expect(model.get('isValid')).to.be.false;
+
+ model.set('displayValue', undefined);
+ expect(model.get('isValid')).to.be.false;
+ });
+
+ it('should be false if METRIC displayValue is not valid float', function () {
+ model.set('displayValue', '$1234.444');
+ expect(model.get('isValid')).to.be.false;
+
+ model.set('displayValue', 'hello-world!');
+ expect(model.get('isValid')).to.be.false;
+ });
+
+ it('should be true if METRIC displayValue is valid float with at most one decimal', function () {
+ model.set('displayValue', '123.4');
+ expect(model.get('isValid')).to.be.true;
+
+ model.set('displayValue', '123.0');
+ expect(model.get('isValid')).to.be.true;
+
+ model.set('displayValue', '666');
+ expect(model.get('isValid')).to.be.true;
+ });
+
+ it('should be false if METRIC displayValue is valid float with more than one decimal', function () {
+ model.set('displayValue', '123.48');
+ expect(model.get('isValid')).to.be.false;
+ });
+
+ it('should be true for AGGREGATE percentage with precision of 1', function () {
+ model = Em.Object.create(App.AlertConfigProperties.Thresholds.PercentageMixin, {
+ displayValue: '1',
+ showInputForValue: true
+ });
+
+ expect(model.get('isValid')).to.be.true;
+
+ model.set('displayValue', '88');
+ expect(model.get('isValid')).to.be.true;
+ });
+
+ it('should be false for AGGREGATE percentage values with precision smaller than 1', function () {
+ model = Em.Object.create(App.AlertConfigProperties.Thresholds.PercentageMixin, {
+ displayValue: '70.01',
+ showInputForValue: true
+ });
+
+ expect(model.get('isValid')).to.be.false;
+
+ model.set('displayValue', '70.0');
+ expect(model.get('isValid')).to.be.false;
+
+ model.set('displayValue', '80.000');
+ expect(model.get('isValid')).to.be.false;
+ });
+
+ it('should be true for PORT percentage values with precision of 1/10th', function () {
+ model = App.AlertConfigProperties.Threshold.create({
+ value: '0.4',
+ showInputForValue: true
+ })
+
+ expect(model.get('isValid')).to.be.true;
+
+ model.set('value', '3');
+ expect(model.get('isValid')).to.be.true;
+
+ model.set('value', '33.0');
+ expect(model.get('isValid')).to.be.true;
+ });
+
+ it('should be false for PORT percentage values with precision greater than 1/10th', function() {
+ model = App.AlertConfigProperties.Threshold.create({
+ value: '4.234',
+ showInputForValue: true
+ });
+
+ expect(model.get('isValid')).to.be.false;
+
+ model.set('value', '44.001');
+ expect(model.get('isValid')).to.be.false;
+
+ model.set('value', '112.01');
+ expect(model.get('isValid')).to.be.false;
+ });
+
+ });
+
+ });
+
+ describe('App.AlertConfigProperties.Thresholds', function () {
+
+ describe('OkThreshold', function () {
+
+ beforeEach(function () {
+ model = App.AlertConfigProperties.Thresholds.OkThreshold.create();
+ });
+
+ describe('#apiProperty', function () {
+
+ it('should be based on showInputForValue and showInputForText', function () {
+
+ model.setProperties({
+ showInputForValue: false,
+ showInputForText: false
+ });
+ expect(model.get('apiProperty')).to.eql([]);
+
+ model.set('showInputForValue', true);
+ expect(model.get('apiProperty')).to.eql(['source.reporting.ok.value']);
+
+ model.set('showInputForText', true);
+ expect(model.get('apiProperty')).to.eql(['source.reporting.ok.value', 'source.reporting.ok.text']);
+
+ });
+
+ });
+
+ });
+
+ describe('WarningThreshold', function () {
+
+ beforeEach(function () {
+ model = App.AlertConfigProperties.Thresholds.WarningThreshold.create();
+ });
+
+ describe('#apiProperty', function () {
+
+ it('should be based on showInputForValue and showInputForText', function () {
+
+ model.setProperties({
+ showInputForValue: false,
+ showInputForText: false
+ });
+ expect(model.get('apiProperty')).to.eql([]);
+
+ model.set('showInputForValue', true);
+ expect(model.get('apiProperty')).to.eql(['source.reporting.warning.value']);
+
+ model.set('showInputForText', true);
+ expect(model.get('apiProperty')).to.eql(['source.reporting.warning.value', 'source.reporting.warning.text']);
+
+ });
+
+ });
+
+ });
+
+ describe('CriticalThreshold', function () {
+
+ beforeEach(function () {
+ model = App.AlertConfigProperties.Thresholds.CriticalThreshold.create();
+ });
+
+ describe('#apiProperty', function () {
+
+ it('should be based on showInputForValue and showInputForText', function () {
+
+ model.setProperties({
+ showInputForValue: false,
+ showInputForText: false
+ });
+ expect(model.get('apiProperty')).to.eql([]);
+
+ model.set('showInputForValue', true);
+ expect(model.get('apiProperty')).to.eql(['source.reporting.critical.value']);
+
+ model.set('showInputForText', true);
+ expect(model.get('apiProperty')).to.eql(['source.reporting.critical.value', 'source.reporting.critical.text']);
+
+ });
+
+ });
+
+ });
+
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/test/models/alerts/alert_definition_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/alerts/alert_definition_test.js b/ambari-web/test/models/alerts/alert_definition_test.js
new file mode 100644
index 0000000..0e8cc79
--- /dev/null
+++ b/ambari-web/test/models/alerts/alert_definition_test.js
@@ -0,0 +1,212 @@
+/**
+ * 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');
+
+require('models/alerts/alert_definition');
+
+var model;
+
+describe('App.AlertDefinition', function () {
+
+ beforeEach(function () {
+
+ model = App.AlertDefinition.createRecord();
+
+ });
+
+ describe('#status', function () {
+
+ Em.A([
+ {
+ summary: {OK: {count: 1, maintenanceCount: 0}, UNKNOWN: {count: 1, maintenanceCount: 0}, WARNING: {count: 2, maintenanceCount: 0}, CRITICAL: {count: 0, maintenanceCount: 0}},
+ m: 'No CRITICAL',
+ e: '<span class="alert-state-single-host label alert-state-OK">OK (1)</span> ' +
+ '<span class="alert-state-single-host label alert-state-WARNING">WARN (2)</span> ' +
+ '<span class="alert-state-single-host label alert-state-UNKNOWN">UNKWN (1)</span>'
+ },
+ {
+ summary: {WARNING: {count: 2, maintenanceCount: 0}, CRITICAL: {count: 3, maintenanceCount: 0}, UNKNOWN: {count: 1, maintenanceCount: 0}, OK: {count: 1, maintenanceCount: 0}},
+ m: 'All states exists',
+ e: '<span class="alert-state-single-host label alert-state-OK">OK (1)</span> ' +
+ '<span class="alert-state-single-host label alert-state-WARNING">WARN (2)</span> ' +
+ '<span class="alert-state-single-host label alert-state-CRITICAL">CRIT (3)</span> ' +
+ '<span class="alert-state-single-host label alert-state-UNKNOWN">UNKWN (1)</span>'
+ },
+ {
+ summary: {OK: {count: 1, maintenanceCount: 0}, UNKNOWN: {count: 0, maintenanceCount: 0}, WARNING: {count: 0, maintenanceCount: 0}, CRITICAL: {count: 0, maintenanceCount: 0}},
+ m: 'Single host',
+ e: '<span class="alert-state-single-host label alert-state-OK">OK</span>'
+ },
+ {
+ summary: {OK: {count: 0, maintenanceCount: 1}, UNKNOWN: {count: 0, maintenanceCount: 0}, WARNING: {count: 0, maintenanceCount: 0}, CRITICAL: {count: 0, maintenanceCount: 0}},
+ m: 'Maintenance OK alert',
+ e: '<span class="alert-state-single-host label alert-state-PENDING"><span class="icon-medkit"></span> OK</span>'
+ },
+ {
+ summary: {},
+ m: 'Pending',
+ e: '<span class="alert-state-single-host label alert-state-PENDING">NONE</span>'
+ }
+ ]).forEach(function (test) {
+ it(test.m, function () {
+ model.set('summary', test.summary);
+ expect(model.get('status')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isCriticalOrWarning', function () {
+
+ Em.A([
+ {summary: {CRITICAL: {count: 1, maintenanceCount: 0}}, e: true},
+ {summary: {CRITICAL: {count: 0, maintenanceCount: 1}}, e: false},
+ {summary: {CRITICAL: {count: 1, maintenanceCount: 1}}, e: true},
+ {summary: {WARNING: {count: 1, maintenanceCount: 0}}, e: true},
+ {summary: {OK: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {UNKNOWN: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {}, e: false}
+ ]).forEach(function (test, i) {
+ it('test ' + (i + 1), function () {
+ model.set('summary', test.summary);
+ expect(model.get('isCriticalOrWarning')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isCritical', function () {
+
+ Em.A([
+ {summary: {CRITICAL: {count: 1, maintenanceCount: 0}}, e: true},
+ {summary: {WARNING: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {OK: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {UNKNOWN: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {}, e: false}
+ ]).forEach(function (test, i) {
+ it('test ' + (i + 1), function () {
+ model.set('summary', test.summary);
+ expect(model.get('isCritical')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#isWarning', function () {
+
+ Em.A([
+ {summary: {CRITICAL: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {WARNING: {count: 1, maintenanceCount: 0}}, e: true},
+ {summary: {OK: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {UNKNOWN: {count: 1, maintenanceCount: 0}}, e: false},
+ {summary: {}, e: false}
+ ]).forEach(function (test, i) {
+ it('test ' + (i + 1), function () {
+ model.set('summary', test.summary);
+ expect(model.get('isWarning')).to.equal(test.e);
+ });
+ });
+
+ });
+
+ describe('#lastTriggeredAgoFormatted', function () {
+
+ it('should be empty', function () {
+ model.set('lastTriggered', 0);
+ expect(model.get('lastTriggeredAgoFormatted')).to.equal('');
+ });
+
+ it('should not be empty', function () {
+ model.set('lastTriggered', new Date().getTime() - 61000);
+ expect(model.get('lastTriggeredAgoFormatted')).to.equal('about a minute ago');
+ });
+
+ });
+
+ describe('#serviceDisplayName', function () {
+
+ it('should get name for non-existing service', function () {
+ model.set('serviceName', 'FOOBAR');
+ expect(model.get('serviceDisplayName')).to.equal('Foobar');
+ });
+
+ });
+
+ describe('#componentNameFormatted', function () {
+
+ beforeEach(function () {
+ sinon.stub(App.format, 'role', function (a) {
+ return 'role ' + a;
+ });
+ });
+
+ it('should wrap component name by App.format.role method', function () {
+ model.set('componentName', 'test');
+ var result = model.get('componentNameFormatted');
+ expect(result).to.equal('role test');
+ });
+
+ afterEach(function () {
+ App.format.role.restore();
+ });
+
+
+ });
+
+ describe('REOPEN', function () {
+
+ describe('#getSortDefinitionsByStatus', function () {
+
+ Em.A([
+ {
+ a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
+ b: App.AlertDefinition.createRecord({summary: {WARNING: {count: 1, maintenanceCount: 0}}}),
+ order: true,
+ e: -1
+ },
+ {
+ a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 2, maintenanceCount: 0}}}),
+ b: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
+ order: true,
+ e: -1
+ },
+ {
+ a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
+ b: App.AlertDefinition.createRecord({summary: {WARNING: {count: 1, maintenanceCount: 0}}}),
+ order: false,
+ e: 1
+ },
+ {
+ a: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 2, maintenanceCount: 0}}}),
+ b: App.AlertDefinition.createRecord({summary: {OK: {count: 1, maintenanceCount: 0}, WARNING: {count: 1, maintenanceCount: 0}}}),
+ order: false,
+ e: 1
+ }
+ ]).forEach(function(test, i) {
+ it('test #' + (i + 1), function () {
+ var func = App.AlertDefinition.getSortDefinitionsByStatus(test.order);
+ expect(func(test.a, test.b)).to.equal(test.e);
+ });
+ });
+
+ });
+
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/test/models/alerts/alert_instance_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/models/alerts/alert_instance_test.js b/ambari-web/test/models/alerts/alert_instance_test.js
new file mode 100644
index 0000000..19838a4
--- /dev/null
+++ b/ambari-web/test/models/alerts/alert_instance_test.js
@@ -0,0 +1,94 @@
+/**
+ * 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');
+
+require('models/alerts/alert_instance');
+
+var model;
+
+describe('App.AlertInstance', function () {
+
+ beforeEach(function () {
+
+ model = App.AlertInstance.createRecord();
+
+ });
+
+ describe('#serviceDisplayName', function () {
+
+ it('should get name for non-existing service', function () {
+ model.set('serviceName', 'FOOBAR');
+ expect(model.get('serviceDisplayName')).to.equal('Foobar');
+ });
+
+ });
+
+ describe('#statusChangedAndLastCheckedFormatted', function () {
+
+ it('should Status Changed before Last Checked', function () {
+
+ var lastCheckedFormatted = '123',
+ lastTriggeredFormatted = '321';
+
+ model.reopen({
+ lastCheckedFormatted: lastCheckedFormatted,
+ lastTriggeredFormatted: lastTriggeredFormatted
+ });
+ var status = model.get('statusChangedAndLastCheckedFormatted');
+ expect(status.indexOf(lastCheckedFormatted) > status.indexOf(lastTriggeredFormatted)).to.be.true;
+
+ });
+
+ });
+
+ describe('#status', function () {
+
+ it('should show maint mode icon', function () {
+
+ model.set('maintenanceState', 'ON');
+ model.set('state', 'OK');
+ var status = model.get('status');
+
+ expect(status).to.equal('<div class="label alert-state-single-host alert-state-PENDING"><span class="icon-medkit"></span> OK</div>');
+
+ });
+
+ it('should not show maint mode icon', function () {
+
+ model.set('maintenanceState', 'OFF');
+ model.set('state', 'OK');
+ var status = model.get('status');
+
+ expect(status).to.equal('<div class="label alert-state-single-host alert-state-OK">OK</div>');
+
+ });
+
+ });
+
+ describe('#escapeSpecialCharactersFromTooltip', function () {
+ it('it Should Display Alert Without special characters "<" and ">"', function () {
+
+ model.set('text', '<urlopen error [Errno 111] Connection refused>');
+ var resultedText = model.get('escapeSpecialCharactersFromTooltip');
+
+ expect(resultedText).to.equal('urlopen error [Errno 111] Connection refused');
+ });
+ });
+
+});
[2/2] ambari git commit: AMBARI-12845. Alert Instances overriding
issue (onehiporenko)
Posted by on...@apache.org.
AMBARI-12845. Alert Instances overriding issue (onehiporenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/66d0b6e2
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/66d0b6e2
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/66d0b6e2
Branch: refs/heads/trunk
Commit: 66d0b6e2c136335841fb99a47d5122ebde18d046
Parents: 9953433
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Fri Aug 21 13:07:57 2015 +0300
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Fri Aug 21 13:07:57 2015 +0300
----------------------------------------------------------------------
ambari-web/app/assets/test/tests.js | 6 +-
.../main/alerts/alert_instances_controller.js | 13 +-
.../alerts/definition_details_controller.js | 2 +-
.../main/host/host_alerts_controller.js | 2 +-
.../app/mappers/alert_instances_mapper.js | 14 +-
ambari-web/app/models.js | 11 +-
ambari-web/app/models/alert_config.js | 619 -------------------
ambari-web/app/models/alert_definition.js | 344 -----------
ambari-web/app/models/alert_group.js | 79 ---
ambari-web/app/models/alert_instance.js | 165 -----
ambari-web/app/models/alert_notification.js | 33 -
ambari-web/app/models/alerts/alert_config.js | 619 +++++++++++++++++++
.../app/models/alerts/alert_definition.js | 344 +++++++++++
ambari-web/app/models/alerts/alert_group.js | 79 +++
ambari-web/app/models/alerts/alert_instance.js | 165 +++++
.../app/models/alerts/alert_instance_local.js | 30 +
.../app/models/alerts/alert_notification.js | 33 +
.../main/alerts/definition_details_view.js | 2 +-
.../app/views/main/host/host_alerts_view.js | 13 +-
ambari-web/test/models/alert_config_test.js | 332 ----------
ambari-web/test/models/alert_definition_test.js | 212 -------
ambari-web/test/models/alert_instance_test.js | 94 ---
.../test/models/alerts/alert_config_test.js | 332 ++++++++++
.../test/models/alerts/alert_definition_test.js | 212 +++++++
.../test/models/alerts/alert_instance_test.js | 94 +++
25 files changed, 1944 insertions(+), 1905 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 56f9444..1888690 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -275,9 +275,9 @@ var files = ['test/init_model_test',
'test/models/service/flume_test',
'test/models/service/hdfs_test',
'test/models/service/yarn_test',
- 'test/models/alert_config_test',
- 'test/models/alert_definition_test',
- 'test/models/alert_instance_test',
+ 'test/models/alerts/alert_config_test',
+ 'test/models/alerts/alert_definition_test',
+ 'test/models/alerts/alert_instance_test',
'test/models/authentication_test',
'test/models/cluster_states_test',
'test/models/config_group_test',
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alerts/alert_instances_controller.js b/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
index 9e07726..234fba0 100644
--- a/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
+++ b/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
@@ -182,7 +182,7 @@ App.MainAlertInstancesController = Em.Controller.extend({
* @method getAlertInstancesSuccessCallback
*/
getAlertInstancesSuccessCallback: function (json) {
- App.alertInstanceMapper.map(json);
+ App.alertInstanceMapper.mapLocal(json);
this.set('isLoaded', true);
this.toggleProperty('reload');
},
@@ -309,15 +309,6 @@ App.MainAlertInstancesController = Em.Controller.extend({
this.set('filteredContent', this.get('content'));
}.observes('content.length'),
- refreshTooltips: function () {
- this.ensureTooltip();
- }.observes('contents.[]', 'filteringComplete'),
-
- ensureTooltip: function () {
- Em.run.next(this, function () {
- App.tooltip($(".timeago"));
- });
- },
/**
* Router transition to alert definition details page
* @param event
@@ -354,8 +345,6 @@ App.MainAlertInstancesController = Em.Controller.extend({
didInsertElement: function () {
this.filter();
- this.addObserver('filteringComplete', this, this.overlayObserver);
- this.overlayObserver();
return this._super();
}
})
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/controllers/main/alerts/definition_details_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alerts/definition_details_controller.js b/ambari-web/app/controllers/main/alerts/definition_details_controller.js
index 27e6a4a..336033e 100644
--- a/ambari-web/app/controllers/main/alerts/definition_details_controller.js
+++ b/ambari-web/app/controllers/main/alerts/definition_details_controller.js
@@ -21,7 +21,7 @@ App.MainAlertDefinitionDetailsController = Em.Controller.extend({
name: 'mainAlertDefinitionDetailsController',
alerts: function () {
- return App.AlertInstance.find().toArray()
+ return App.AlertInstanceLocal.find().toArray()
.filterProperty('definitionId', this.get('content.id'));
}.property('App.router.mainAlertInstancesController.isLoaded', 'App.router.mainAlertInstancesController.reload'),
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/controllers/main/host/host_alerts_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host/host_alerts_controller.js b/ambari-web/app/controllers/main/host/host_alerts_controller.js
index 2a47914..a44aa26 100644
--- a/ambari-web/app/controllers/main/host/host_alerts_controller.js
+++ b/ambari-web/app/controllers/main/host/host_alerts_controller.js
@@ -30,7 +30,7 @@ App.MainHostAlertsController = Em.ArrayController.extend({
* @type {App.AlertInstance[]}
*/
content: function () {
- return App.AlertInstance.find().toArray().filterProperty('host', this.get('selectedHost'));
+ return App.AlertInstanceLocal.find().toArray().filterProperty('host', this.get('selectedHost'));
}.property('App.router.mainAlertInstancesController.isLoaded', 'selectedHost'),
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/mappers/alert_instances_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/alert_instances_mapper.js b/ambari-web/app/mappers/alert_instances_mapper.js
index af6804f..cd1f8bd 100644
--- a/ambari-web/app/mappers/alert_instances_mapper.js
+++ b/ambari-web/app/mappers/alert_instances_mapper.js
@@ -21,6 +21,8 @@ App.alertInstanceMapper = App.QuickDataMapper.create({
model : App.AlertInstance,
+ modelLocal: App.AlertInstanceLocal,
+
config : {
id: 'Alert.id',
label: 'Alert.label',
@@ -41,10 +43,17 @@ App.alertInstanceMapper = App.QuickDataMapper.create({
},
map: function(json) {
+ return this.parse(json, this.get('model'));
+ },
+
+ mapLocal: function(json) {
+ return this.parse(json, this.get('modelLocal'));
+ },
+
+ parse: function(json, model) {
console.time('App.alertInstanceMapper execution time');
if (json.items) {
var alertInstances = [];
- var model = this.get('model');
var alertsToDelete = model.find().mapProperty('id');
json.items.forEach(function (item) {
@@ -57,8 +66,9 @@ App.alertInstanceMapper = App.QuickDataMapper.create({
model.find().clear();
}
- App.store.loadMany(this.get('model'), alertInstances);
+ App.store.loadMany(model, alertInstances);
console.timeEnd('App.alertInstanceMapper execution time');
}
}
+
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index 64860ea..6bffb95 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -42,11 +42,12 @@ require('models/service/mapreduce2');
require('models/service/hbase');
require('models/service/flume');
require('models/service/storm');
-require('models/alert_definition');
-require('models/alert_instance');
-require('models/alert_notification');
-require('models/alert_config');
-require('models/alert_group');
+require('models/alerts/alert_definition');
+require('models/alerts/alert_instance');
+require('models/alerts/alert_instance_local');
+require('models/alerts/alert_notification');
+require('models/alerts/alert_config');
+require('models/alerts/alert_group');
require('models/user');
require('models/host');
require('models/rack');
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alert_config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alert_config.js b/ambari-web/app/models/alert_config.js
deleted file mode 100644
index df2e579..0000000
--- a/ambari-web/app/models/alert_config.js
+++ /dev/null
@@ -1,619 +0,0 @@
-/**
- * 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: function () {
- var afterLabeledTypes = ['radioButton'];
- return !afterLabeledTypes.contains(this.get('displayType'));
- }.property('displayType'),
-
- /**
- * value converted to appropriate format for sending to server
- * should be computed property
- * should be defined in child class
- * @type {*}
- */
- apiFormattedValue: function () {
- return this.get('value');
- }.property('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;
- default:
- console.error('Unable to find viewClass for displayType ', displayType);
- }
- }.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: function () {
- return 'alert-state-' + this.get('badge');
- }.property('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: function () {
- return this.get('isJMXMetric') ? 'source.jmx.property_list' : 'source.ganglia.property_list'
- }.property('isJMXMetric'),
- 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: function () {
- return this.get('isJMXMetric') ? 'source.jmx.value' : 'source.ganglia.value'
- }.property('isJMXMetric')
- })
-
-};
-
-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 && value <= 100 : true;
- }.property('displayValue', 'showInputForValue'),
-
- /**
- * Return <code>value * 100</code>
- * @returns {string}
- */
- getNewValue: function () {
- var value = this.get('value');
- return (value && !isNaN(value)) ? (Number(value) * 100) + '' : value;
- },
-
- /**
- * Return <code>displayValue / 100</code>
- * @returns {string}
- */
- getNewDisplayValue: function () {
- var displayValue = this.get('displayValue');
- return (displayValue && !isNaN(displayValue)) ? (Number(displayValue) / 100) + '' : displayValue;
- }
-
- }),
-
- /**
- * 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')
-
- })
-
-};
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alert_definition.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alert_definition.js b/ambari-web/app/models/alert_definition.js
deleted file mode 100644
index d1310ff..0000000
--- a/ambari-web/app/models/alert_definition.js
+++ /dev/null
@@ -1,344 +0,0 @@
-/**
- * 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');
-
-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'),
- lastTriggered: 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:
- * <code>
- * {
- * "CRITICAL": {
- * count: 1,
- * maintenanceCount: 0
- * },
- * "OK": {
- * count: 0,
- * maintenanceCount: 1
- * },
- * "UNKNOWN": {
- * count: 0,
- * maintenanceCount: 0
- * },
- * "WARNING": {
- * count: 1,
- * maintenanceCount: 1
- * }
- * }
- * </code>
- * @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 <code>$.timeago</code>
- * @type {string}
- */
- lastTriggeredAgoFormatted: function () {
- var lastTriggered = this.get('lastTriggered');
- return lastTriggered ? $.timeago(new Date(lastTriggered)) : '';
- }.property('lastTriggered'),
-
- 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('lastTriggered');
- 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('lastTriggered'),
-
- /**
- * Formatted displayName for <code>componentName</code>
- * @type {String}
- */
- componentNameFormatted: function () {
- return App.format.role(this.get('componentName'));
- }.property('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 ? '<span class="alert-state-single-host label alert-state-' + state + '">' + shortState + ' (' + summary[state].count + ')</span>' : '';
- // add status with maintenance mode icon
- result += summary[state].maintenanceCount ?
- '<span class="alert-state-single-host label alert-state-PENDING"><span class="icon-medkit"></span> ' + shortState + ' (' + summary[state].maintenanceCount + ')</span>' : '';
- 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 ? '<span class="alert-state-single-host label alert-state-' + state + '">' + shortState + '</span>' : '';
- // add status with maintenance mode icon
- result += summary[state].maintenanceCount ?
- '<span class="alert-state-single-host label alert-state-PENDING"><span class="icon-medkit"></span> ' + shortState + '</span>' : '';
- return result;
- }).without('').join(' ');
- } else if (hostCnt == 0) {
- // none
- return '<span class="alert-state-single-host label alert-state-PENDING">NONE</span>';
- }
- 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'),
-
- isHostAlertDefinition: function () {
- var serviceID = (this.get('service')._id === "AMBARI"),
- component = (this.get('componentName') === "AMBARI_AGENT");
- return serviceID && component;
- }.property('service', 'componentName'),
-
- 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: CRIT
- * @type {boolean}
- */
- isCritical: function () {
- var summary = this.get('summary');
- var state = 'CRITICAL';
- return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
- }.property('summary'),
-
- /**
- * if this definition is in state: WARNING
- * @type {boolean}
- */
- isWarning: function () {
- var summary = this.get('summary');
- var state = 'WARNING';
- return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
- }.property('summary'),
-
- /**
- * if this definition is in state: OK
- * @type {boolean}
- */
- isOK: function () {
- var summary = this.get('summary');
- var state = 'OK';
- return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
- }.property('summary'),
-
- /**
- * if this definition is in state: OK
- * @type {boolean}
- */
- isUnknown: function () {
- var summary = this.get('summary');
- var state = 'UNKNOWN';
- return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
- }.property('summary'),
-
- /**
- * 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'
- },
-
- /**
- * 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 <code>severityOrder</code>
- * @param {boolean} order true - DESC, false - ASC
- * @returns {Function}
- * @method getSortDefinitionsByStatus
- */
- getSortDefinitionsByStatus: function (order) {
- return function (a, b) {
- var a_summary = a.get('summary'),
- b_summary = b.get('summary'),
- st_order = a.get('severityOrder'),
- ret = 0;
- for (var i = 0; i < st_order.length; i++) {
- var a_v = Em.isNone(a_summary[st_order[i]]) ? 0 : a_summary[st_order[i]].count + a_summary[st_order[i]].maintenanceCount,
- b_v = Em.isNone(b_summary[st_order[i]]) ? 0 : b_summary[st_order[i]].count + b_summary[st_order[i]].maintenanceCount;
- ret = b_v - a_v;
- if (ret !== 0) {
- break;
- }
- }
- return order ? ret : -ret;
- };
- }
-
-});
-
-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')
-});
-
-App.AlertDefinition.FIXTURES = [];
-App.AlertReportDefinition.FIXTURES = [];
-App.AlertMetricsSourceDefinition.FIXTURES = [];
-App.AlertMetricsUriDefinition.FIXTURES = [];
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alert_group.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alert_group.js b/ambari-web/app/models/alert_group.js
deleted file mode 100644
index e914ed5..0000000
--- a/ambari-web/app/models/alert_group.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * 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');
-
-/**
- * Represents an alert-group on the cluster.
- * A alert group is a collection of alert definitions
- *
- * Alert group hierarchy is at 2 levels. For
- * each service there is a 'Default' alert group
- * containing all definitions , this group is read-only
- *
- * User can create new alert group containing alert definitions from
- * any service.
- */
-App.AlertGroup = DS.Model.extend({
-
- name: DS.attr('string'),
-
- description: DS.attr('string'),
-
- /**
- * Is this group default for some service
- * @type {boolean}
- */
- default: DS.attr('boolean'),
-
- /**
- * @type {App.AlertDefinition[]}
- */
- definitions: DS.hasMany('App.AlertDefinition'),
-
- /**
- * @type {App.AlertNotification[]}
- */
- targets: DS.hasMany('App.AlertNotification'),
-
- /**
- * @type {string}
- */
- displayName: function () {
- var name = this.get('name');
- if (name && name.length > App.config.CONFIG_GROUP_NAME_MAX_LENGTH) {
- var middle = Math.floor(App.config.CONFIG_GROUP_NAME_MAX_LENGTH / 2);
- name = name.substring(0, middle) + "..." + name.substring(name.length - middle);
- }
- return this.get('default') ? (name + ' Default') : name;
- }.property('name', 'default'),
-
- /**
- * @type {string}
- */
- displayNameDefinitions: function () {
- return this.get('displayName') + ' (' + this.get('definitions.length') + ')';
- }.property('displayName', 'definitions.length'),
-
- isAddDefinitionsDisabled: function () {
- return this.get('default');
- }.property('default')
-});
-App.AlertGroup.FIXTURES = [];
-
-
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alert_instance.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alert_instance.js b/ambari-web/app/models/alert_instance.js
deleted file mode 100644
index 33df293..0000000
--- a/ambari-web/app/models/alert_instance.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * 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');
-
-App.AlertInstance = DS.Model.extend({
- id: DS.attr('number'),
- label: DS.attr('string'),
- definitionName: DS.attr('string'),
- definitionId: DS.attr('number'),
- service: DS.belongsTo('App.Service'),
- serviceName: DS.attr('string'),
- componentName: DS.attr('string'),
- host: DS.belongsTo('App.Host'),
- hostName: DS.attr('string'),
- scope: DS.attr('string'),
- originalTimestamp: DS.attr('number'),
- latestTimestamp: DS.attr('number'),
- maintenanceState: DS.attr('string'),
- instance: DS.attr('string'),
- state: DS.attr('string'),
- text: DS.attr('string'),
- notification: DS.hasMany('App.AlertNotification'),
-
- /**
- * Status icon markup
- * @type {string}
- */
- status: function () {
- var isMaintenanceStateOn = this.get('maintenanceState') === 'ON';
- var state = this.get('state');
- var stateClass = isMaintenanceStateOn ? 'PENDING' : state;
- var shortState = this.get('shortState')[state];
- var maintenanceIcon = isMaintenanceStateOn ? '<span class="icon-medkit"></span> ' : '';
- return '<div class="label alert-state-single-host alert-state-' + stateClass + '">' + maintenanceIcon + shortState + '</div>';
- }.property('state'),
-
- /**
- * 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.AlertDefinition#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'),
-
- /**
- * Formatted timestamp for latest instance triggering
- * @type {string}
- */
- lastCheckedFormatted: function () {
- return dateUtils.dateFormat(this.get('latestTimestamp'));
- }.property('latestTimestamp'),
-
- /**
- * Formatted timestamp for latest instance triggering
- * @type {string}
- */
- lastTriggeredFormatted: function () {
- return dateUtils.dateFormat(this.get('originalTimestamp'));
- }.property('originalTimestamp'),
-
- /**
- * Formatted timestamp with <code>$.timeago</code>
- * @type {string}
- */
- lastTriggeredAgoFormatted: function () {
- var lastTriggered = this.get('originalTimestamp');
- return lastTriggered ? $.timeago(new Date(lastTriggered)) : '';
- }.property('originalTimestamp'),
-
- lastTriggeredVerboseDisplay: function () {
- var originalTimestamp = this.get('originalTimestamp');
- var latestTimestamp = this.get('latestTimestamp');
- return Em.I18n.t('models.alert_instance.tiggered.verbose').format(
- dateUtils.dateFormat(originalTimestamp),
- dateUtils.dateFormat(latestTimestamp));
- }.property('originalTimestamp', 'latestTimestamp'),
-
- /**
- * Formatted timestamp with <code>$.timeago</code>
- * @type {string}
- */
- lastTriggeredForFormatted: function () {
- var lastTriggered = this.get('originalTimestamp');
- 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('originalTimestamp'),
-
- /**
- * escaped '<' and '>' special characters.
- * @type {string}
- */
- escapeSpecialCharactersFromTooltip: function () {
- var displayedText = this.get('text');
- return displayedText.replace(/[<>]/g, '');
- }.property('text'),
-
- /**
- * Formatted lastChecked and lastTriggered timestamp
- * @returns {string}
- */
- statusChangedAndLastCheckedFormatted: function () {
- var lastCheckedFormatted = this.get('lastCheckedFormatted');
- var lastTriggeredFormatted = this.get('lastTriggeredFormatted');
- return Em.I18n.t('models.alert_definition.triggered.checked').format(lastTriggeredFormatted, lastCheckedFormatted);
- }.property('lastCheckedFormatted', 'lastTriggeredFormatted'),
-
- /**
- * List of css-classes for alert instance status
- * @type {object}
- */
- typeIcons: {
- 'DISABLED': 'icon-off'
- },
-
- /**
- * Define if definition serviceName is Ambari
- * Used in some logic in templates to distinguish definitions with Ambari serviceName
- * @returns {boolean}
- */
- isAmbariServiceName: function () {
- return this.get('serviceName') === 'AMBARI';
- }.property('serviceName'),
-
- shortState: {
- 'CRITICAL': 'CRIT',
- 'WARNING': 'WARN',
- 'OK': 'OK',
- 'UNKNOWN': 'UNKWN',
- 'PENDING': 'NONE'
- }
-});
-
-App.AlertInstance.FIXTURES = [];
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alert_notification.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alert_notification.js b/ambari-web/app/models/alert_notification.js
deleted file mode 100644
index c2d7570..0000000
--- a/ambari-web/app/models/alert_notification.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * 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.AlertNotification = DS.Model.extend({
- id: DS.attr('number'),
- name: DS.attr('string'),
- type: DS.attr('string'),
- description: DS.attr('string'),
- groups: DS.hasMany('App.AlertGroup'),
- global: DS.attr('boolean'),
-
- properties: {},
- alertStates: []
-});
-
-App.AlertNotification.FIXTURES = [];
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alerts/alert_config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alerts/alert_config.js b/ambari-web/app/models/alerts/alert_config.js
new file mode 100644
index 0000000..df2e579
--- /dev/null
+++ b/ambari-web/app/models/alerts/alert_config.js
@@ -0,0 +1,619 @@
+/**
+ * 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: function () {
+ var afterLabeledTypes = ['radioButton'];
+ return !afterLabeledTypes.contains(this.get('displayType'));
+ }.property('displayType'),
+
+ /**
+ * value converted to appropriate format for sending to server
+ * should be computed property
+ * should be defined in child class
+ * @type {*}
+ */
+ apiFormattedValue: function () {
+ return this.get('value');
+ }.property('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;
+ default:
+ console.error('Unable to find viewClass for displayType ', displayType);
+ }
+ }.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: function () {
+ return 'alert-state-' + this.get('badge');
+ }.property('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: function () {
+ return this.get('isJMXMetric') ? 'source.jmx.property_list' : 'source.ganglia.property_list'
+ }.property('isJMXMetric'),
+ 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: function () {
+ return this.get('isJMXMetric') ? 'source.jmx.value' : 'source.ganglia.value'
+ }.property('isJMXMetric')
+ })
+
+};
+
+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 && value <= 100 : true;
+ }.property('displayValue', 'showInputForValue'),
+
+ /**
+ * Return <code>value * 100</code>
+ * @returns {string}
+ */
+ getNewValue: function () {
+ var value = this.get('value');
+ return (value && !isNaN(value)) ? (Number(value) * 100) + '' : value;
+ },
+
+ /**
+ * Return <code>displayValue / 100</code>
+ * @returns {string}
+ */
+ getNewDisplayValue: function () {
+ var displayValue = this.get('displayValue');
+ return (displayValue && !isNaN(displayValue)) ? (Number(displayValue) / 100) + '' : displayValue;
+ }
+
+ }),
+
+ /**
+ * 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')
+
+ })
+
+};
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alerts/alert_definition.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alerts/alert_definition.js b/ambari-web/app/models/alerts/alert_definition.js
new file mode 100644
index 0000000..d1310ff
--- /dev/null
+++ b/ambari-web/app/models/alerts/alert_definition.js
@@ -0,0 +1,344 @@
+/**
+ * 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');
+
+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'),
+ lastTriggered: 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:
+ * <code>
+ * {
+ * "CRITICAL": {
+ * count: 1,
+ * maintenanceCount: 0
+ * },
+ * "OK": {
+ * count: 0,
+ * maintenanceCount: 1
+ * },
+ * "UNKNOWN": {
+ * count: 0,
+ * maintenanceCount: 0
+ * },
+ * "WARNING": {
+ * count: 1,
+ * maintenanceCount: 1
+ * }
+ * }
+ * </code>
+ * @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 <code>$.timeago</code>
+ * @type {string}
+ */
+ lastTriggeredAgoFormatted: function () {
+ var lastTriggered = this.get('lastTriggered');
+ return lastTriggered ? $.timeago(new Date(lastTriggered)) : '';
+ }.property('lastTriggered'),
+
+ 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('lastTriggered');
+ 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('lastTriggered'),
+
+ /**
+ * Formatted displayName for <code>componentName</code>
+ * @type {String}
+ */
+ componentNameFormatted: function () {
+ return App.format.role(this.get('componentName'));
+ }.property('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 ? '<span class="alert-state-single-host label alert-state-' + state + '">' + shortState + ' (' + summary[state].count + ')</span>' : '';
+ // add status with maintenance mode icon
+ result += summary[state].maintenanceCount ?
+ '<span class="alert-state-single-host label alert-state-PENDING"><span class="icon-medkit"></span> ' + shortState + ' (' + summary[state].maintenanceCount + ')</span>' : '';
+ 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 ? '<span class="alert-state-single-host label alert-state-' + state + '">' + shortState + '</span>' : '';
+ // add status with maintenance mode icon
+ result += summary[state].maintenanceCount ?
+ '<span class="alert-state-single-host label alert-state-PENDING"><span class="icon-medkit"></span> ' + shortState + '</span>' : '';
+ return result;
+ }).without('').join(' ');
+ } else if (hostCnt == 0) {
+ // none
+ return '<span class="alert-state-single-host label alert-state-PENDING">NONE</span>';
+ }
+ 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'),
+
+ isHostAlertDefinition: function () {
+ var serviceID = (this.get('service')._id === "AMBARI"),
+ component = (this.get('componentName') === "AMBARI_AGENT");
+ return serviceID && component;
+ }.property('service', 'componentName'),
+
+ 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: CRIT
+ * @type {boolean}
+ */
+ isCritical: function () {
+ var summary = this.get('summary');
+ var state = 'CRITICAL';
+ return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
+ }.property('summary'),
+
+ /**
+ * if this definition is in state: WARNING
+ * @type {boolean}
+ */
+ isWarning: function () {
+ var summary = this.get('summary');
+ var state = 'WARNING';
+ return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
+ }.property('summary'),
+
+ /**
+ * if this definition is in state: OK
+ * @type {boolean}
+ */
+ isOK: function () {
+ var summary = this.get('summary');
+ var state = 'OK';
+ return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
+ }.property('summary'),
+
+ /**
+ * if this definition is in state: OK
+ * @type {boolean}
+ */
+ isUnknown: function () {
+ var summary = this.get('summary');
+ var state = 'UNKNOWN';
+ return !!summary[state] && !!(summary[state].count || summary[state].maintenanceCount);
+ }.property('summary'),
+
+ /**
+ * 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'
+ },
+
+ /**
+ * 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 <code>severityOrder</code>
+ * @param {boolean} order true - DESC, false - ASC
+ * @returns {Function}
+ * @method getSortDefinitionsByStatus
+ */
+ getSortDefinitionsByStatus: function (order) {
+ return function (a, b) {
+ var a_summary = a.get('summary'),
+ b_summary = b.get('summary'),
+ st_order = a.get('severityOrder'),
+ ret = 0;
+ for (var i = 0; i < st_order.length; i++) {
+ var a_v = Em.isNone(a_summary[st_order[i]]) ? 0 : a_summary[st_order[i]].count + a_summary[st_order[i]].maintenanceCount,
+ b_v = Em.isNone(b_summary[st_order[i]]) ? 0 : b_summary[st_order[i]].count + b_summary[st_order[i]].maintenanceCount;
+ ret = b_v - a_v;
+ if (ret !== 0) {
+ break;
+ }
+ }
+ return order ? ret : -ret;
+ };
+ }
+
+});
+
+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')
+});
+
+App.AlertDefinition.FIXTURES = [];
+App.AlertReportDefinition.FIXTURES = [];
+App.AlertMetricsSourceDefinition.FIXTURES = [];
+App.AlertMetricsUriDefinition.FIXTURES = [];
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alerts/alert_group.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alerts/alert_group.js b/ambari-web/app/models/alerts/alert_group.js
new file mode 100644
index 0000000..e914ed5
--- /dev/null
+++ b/ambari-web/app/models/alerts/alert_group.js
@@ -0,0 +1,79 @@
+/**
+ * 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');
+
+/**
+ * Represents an alert-group on the cluster.
+ * A alert group is a collection of alert definitions
+ *
+ * Alert group hierarchy is at 2 levels. For
+ * each service there is a 'Default' alert group
+ * containing all definitions , this group is read-only
+ *
+ * User can create new alert group containing alert definitions from
+ * any service.
+ */
+App.AlertGroup = DS.Model.extend({
+
+ name: DS.attr('string'),
+
+ description: DS.attr('string'),
+
+ /**
+ * Is this group default for some service
+ * @type {boolean}
+ */
+ default: DS.attr('boolean'),
+
+ /**
+ * @type {App.AlertDefinition[]}
+ */
+ definitions: DS.hasMany('App.AlertDefinition'),
+
+ /**
+ * @type {App.AlertNotification[]}
+ */
+ targets: DS.hasMany('App.AlertNotification'),
+
+ /**
+ * @type {string}
+ */
+ displayName: function () {
+ var name = this.get('name');
+ if (name && name.length > App.config.CONFIG_GROUP_NAME_MAX_LENGTH) {
+ var middle = Math.floor(App.config.CONFIG_GROUP_NAME_MAX_LENGTH / 2);
+ name = name.substring(0, middle) + "..." + name.substring(name.length - middle);
+ }
+ return this.get('default') ? (name + ' Default') : name;
+ }.property('name', 'default'),
+
+ /**
+ * @type {string}
+ */
+ displayNameDefinitions: function () {
+ return this.get('displayName') + ' (' + this.get('definitions.length') + ')';
+ }.property('displayName', 'definitions.length'),
+
+ isAddDefinitionsDisabled: function () {
+ return this.get('default');
+ }.property('default')
+});
+App.AlertGroup.FIXTURES = [];
+
+
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alerts/alert_instance.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alerts/alert_instance.js b/ambari-web/app/models/alerts/alert_instance.js
new file mode 100644
index 0000000..33df293
--- /dev/null
+++ b/ambari-web/app/models/alerts/alert_instance.js
@@ -0,0 +1,165 @@
+/**
+ * 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');
+
+App.AlertInstance = DS.Model.extend({
+ id: DS.attr('number'),
+ label: DS.attr('string'),
+ definitionName: DS.attr('string'),
+ definitionId: DS.attr('number'),
+ service: DS.belongsTo('App.Service'),
+ serviceName: DS.attr('string'),
+ componentName: DS.attr('string'),
+ host: DS.belongsTo('App.Host'),
+ hostName: DS.attr('string'),
+ scope: DS.attr('string'),
+ originalTimestamp: DS.attr('number'),
+ latestTimestamp: DS.attr('number'),
+ maintenanceState: DS.attr('string'),
+ instance: DS.attr('string'),
+ state: DS.attr('string'),
+ text: DS.attr('string'),
+ notification: DS.hasMany('App.AlertNotification'),
+
+ /**
+ * Status icon markup
+ * @type {string}
+ */
+ status: function () {
+ var isMaintenanceStateOn = this.get('maintenanceState') === 'ON';
+ var state = this.get('state');
+ var stateClass = isMaintenanceStateOn ? 'PENDING' : state;
+ var shortState = this.get('shortState')[state];
+ var maintenanceIcon = isMaintenanceStateOn ? '<span class="icon-medkit"></span> ' : '';
+ return '<div class="label alert-state-single-host alert-state-' + stateClass + '">' + maintenanceIcon + shortState + '</div>';
+ }.property('state'),
+
+ /**
+ * 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.AlertDefinition#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'),
+
+ /**
+ * Formatted timestamp for latest instance triggering
+ * @type {string}
+ */
+ lastCheckedFormatted: function () {
+ return dateUtils.dateFormat(this.get('latestTimestamp'));
+ }.property('latestTimestamp'),
+
+ /**
+ * Formatted timestamp for latest instance triggering
+ * @type {string}
+ */
+ lastTriggeredFormatted: function () {
+ return dateUtils.dateFormat(this.get('originalTimestamp'));
+ }.property('originalTimestamp'),
+
+ /**
+ * Formatted timestamp with <code>$.timeago</code>
+ * @type {string}
+ */
+ lastTriggeredAgoFormatted: function () {
+ var lastTriggered = this.get('originalTimestamp');
+ return lastTriggered ? $.timeago(new Date(lastTriggered)) : '';
+ }.property('originalTimestamp'),
+
+ lastTriggeredVerboseDisplay: function () {
+ var originalTimestamp = this.get('originalTimestamp');
+ var latestTimestamp = this.get('latestTimestamp');
+ return Em.I18n.t('models.alert_instance.tiggered.verbose').format(
+ dateUtils.dateFormat(originalTimestamp),
+ dateUtils.dateFormat(latestTimestamp));
+ }.property('originalTimestamp', 'latestTimestamp'),
+
+ /**
+ * Formatted timestamp with <code>$.timeago</code>
+ * @type {string}
+ */
+ lastTriggeredForFormatted: function () {
+ var lastTriggered = this.get('originalTimestamp');
+ 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('originalTimestamp'),
+
+ /**
+ * escaped '<' and '>' special characters.
+ * @type {string}
+ */
+ escapeSpecialCharactersFromTooltip: function () {
+ var displayedText = this.get('text');
+ return displayedText.replace(/[<>]/g, '');
+ }.property('text'),
+
+ /**
+ * Formatted lastChecked and lastTriggered timestamp
+ * @returns {string}
+ */
+ statusChangedAndLastCheckedFormatted: function () {
+ var lastCheckedFormatted = this.get('lastCheckedFormatted');
+ var lastTriggeredFormatted = this.get('lastTriggeredFormatted');
+ return Em.I18n.t('models.alert_definition.triggered.checked').format(lastTriggeredFormatted, lastCheckedFormatted);
+ }.property('lastCheckedFormatted', 'lastTriggeredFormatted'),
+
+ /**
+ * List of css-classes for alert instance status
+ * @type {object}
+ */
+ typeIcons: {
+ 'DISABLED': 'icon-off'
+ },
+
+ /**
+ * Define if definition serviceName is Ambari
+ * Used in some logic in templates to distinguish definitions with Ambari serviceName
+ * @returns {boolean}
+ */
+ isAmbariServiceName: function () {
+ return this.get('serviceName') === 'AMBARI';
+ }.property('serviceName'),
+
+ shortState: {
+ 'CRITICAL': 'CRIT',
+ 'WARNING': 'WARN',
+ 'OK': 'OK',
+ 'UNKNOWN': 'UNKWN',
+ 'PENDING': 'NONE'
+ }
+});
+
+App.AlertInstance.FIXTURES = [];
http://git-wip-us.apache.org/repos/asf/ambari/blob/66d0b6e2/ambari-web/app/models/alerts/alert_instance_local.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/alerts/alert_instance_local.js b/ambari-web/app/models/alerts/alert_instance_local.js
new file mode 100644
index 0000000..be34565
--- /dev/null
+++ b/ambari-web/app/models/alerts/alert_instance_local.js
@@ -0,0 +1,30 @@
+/**
+ * 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');
+
+/**
+ * Just a copy of <code>App.AlertInstance</code>, used to save separately alert instances for host and definition
+ * Sometimes alert instances can't be stored in the one model. Example: user is in the definition details page and open
+ * alerts popup. Different lists of instances should be shown and loaded. Also it's pretty hard to determine, which
+ * models should be removed from store, when user navigates in the popup or just leave the page
+ * @type {DS.Model}
+ */
+App.AlertInstanceLocal = App.AlertInstance.extend({});
+
+App.AlertInstanceLocal.FIXTURES = [];
\ No newline at end of file