You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2015/05/26 14:11:30 UTC
ambari git commit: AMBARI-11388 Enhanced Dashboard: Aggregate calls
for widget metrics. (atkach)
Repository: ambari
Updated Branches:
refs/heads/trunk 3410ba4e1 -> 599c1da81
AMBARI-11388 Enhanced Dashboard: Aggregate calls for widget metrics. (atkach)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/599c1da8
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/599c1da8
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/599c1da8
Branch: refs/heads/trunk
Commit: 599c1da8138dd881ed863525cfa56a482677f9b4
Parents: 3410ba4
Author: Andrii Tkach <at...@hortonworks.com>
Authored: Tue May 26 14:26:20 2015 +0300
Committer: Andrii Tkach <at...@hortonworks.com>
Committed: Tue May 26 15:11:08 2015 +0300
----------------------------------------------------------------------
.../app/controllers/global/update_controller.js | 6 +-
.../service/widgets/create/step3_controller.js | 1 -
.../app/mixins/common/widgets/widget_mixin.js | 206 +++++++++++++------
ambari-web/app/utils/ajax/ajax.js | 7 +-
.../views/common/widget/graph_widget_view.js | 33 +--
.../test/mixins/common/widget_mixin_test.js | 157 ++++++++++----
6 files changed, 283 insertions(+), 127 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/599c1da8/ambari-web/app/controllers/global/update_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/update_controller.js b/ambari-web/app/controllers/global/update_controller.js
index f81a079..8e6252f 100644
--- a/ambari-web/app/controllers/global/update_controller.js
+++ b/ambari-web/app/controllers/global/update_controller.js
@@ -480,7 +480,11 @@ App.UpdateController = Em.Controller.extend({
updateUnhealthyAlertInstances: function (callback) {
var testUrl = '/data/alerts/alert_instances.json';
var queryParams = this.get('queryParamsForUnhealthyAlertInstances');
- var realUrl = '/alerts?fields=*&Alert/state.in(CRITICAL,WARNING)&Alert/maintenance_state.in(OFF)&from=' + queryParams.from + '&page_size=' + queryParams.page_size;
+ var realUrl = '/alerts?fields=' +
+ 'Alert/component_name,Alert/definition_id,Alert/definition_name,Alert/host_name,Alert/id,Alert/instance,' +
+ 'Alert/label,Alert/latest_timestamp,Alert/maintenance_state,Alert/original_timestamp,Alert/scope,' +
+ 'Alert/service_name,Alert/state,Alert/text' +
+ '&Alert/state.in(CRITICAL,WARNING)&Alert/maintenance_state.in(OFF)&from=' + queryParams.from + '&page_size=' + queryParams.page_size;
var url = this.getUrl(testUrl, realUrl);
App.HttpClient.get(url, App.alertInstanceMapper, {
http://git-wip-us.apache.org/repos/asf/ambari/blob/599c1da8/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js b/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js
index 8a0eaa3..4c9dc81 100644
--- a/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js
+++ b/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js
@@ -129,7 +129,6 @@ App.WidgetWizardStep3Controller = Em.Controller.extend({
author: this.get('widgetAuthor'),
metrics: this.get('widgetMetrics').map(function (metric) {
delete metric.data;
- delete metric.actual_host_component_criteria;
return metric;
}),
values: this.get('widgetValues').map(function (value) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/599c1da8/ambari-web/app/mixins/common/widgets/widget_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widgets/widget_mixin.js b/ambari-web/app/mixins/common/widgets/widget_mixin.js
index 0220a7e..9e20cf4 100644
--- a/ambari-web/app/mixins/common/widgets/widget_mixin.js
+++ b/ambari-web/app/mixins/common/widgets/widget_mixin.js
@@ -110,14 +110,26 @@ App.WidgetMixin = Ember.Mixin.create({
});
}
} else if (request.host_component_criteria) {
- this.getHostComponentMetrics(request).always(function () {
- requestCounter--;
- if (requestCounter === 0) self.onMetricsLoaded();
+ App.WidgetLoadAggregator.add({
+ data: request,
+ context: this,
+ startCallName: 'getHostComponentMetrics',
+ successCallback: this.getHostComponentMetricsSuccessCallback,
+ completeCallback: function () {
+ requestCounter--;
+ if (requestCounter === 0) this.onMetricsLoaded();
+ }
});
} else {
- this.getServiceComponentMetrics(request).complete(function () {
- requestCounter--;
- if (requestCounter === 0) self.onMetricsLoaded();
+ App.WidgetLoadAggregator.add({
+ data: request,
+ context: this,
+ startCallName: 'getServiceComponentMetrics',
+ successCallback: this.getMetricsSuccessCallback,
+ completeCallback: function () {
+ requestCounter--;
+ if (requestCounter === 0) this.onMetricsLoaded();
+ }
});
}
}
@@ -133,7 +145,6 @@ App.WidgetMixin = Ember.Mixin.create({
metrics.forEach(function (metric, index) {
var key;
if (metric.host_component_criteria) {
- this.createActualHostComponentCriteria(metric);
key = metric.service_name + '_' + metric.component_name + '_' + metric.host_component_criteria;
} else {
key = metric.service_name + '_' + metric.component_name;
@@ -154,32 +165,30 @@ App.WidgetMixin = Ember.Mixin.create({
/**
* Create actual host component criteria from persisted host component criteria
- * NameNode HA and ReourceManager HA host component criteria is applicable only in HA mode
+ * NameNode HA and ResourceManager HA host component criteria is applicable only in HA mode
+ * @param {object} request
*/
- createActualHostComponentCriteria: function (metric) {
- switch (metric.component_name) {
+ computeHostComponentCriteria: function (request) {
+ switch (request.component_name) {
case 'NAMENODE':
- if (metric.host_component_criteria === 'host_components/metrics/dfs/FSNamesystem/HAState=active') {
+ if (request.host_component_criteria === 'host_components/metrics/dfs/FSNamesystem/HAState=active') {
var hdfs = App.HDFSService.find().objectAt(0);
var activeNNHostName = !hdfs.get('snameNode') && hdfs.get('activeNameNode');
if (!activeNNHostName) {
- metric.actual_host_component_criteria = 'host_components/HostRoles/component_name=NAMENODE';
- } else {
- metric.actual_host_component_criteria = metric.host_component_criteria;
+ return '';
}
}
break;
case 'RESOURCEMANAGER':
- if (metric.host_component_criteria === 'host_components/HostRoles/ha_state=ACTIVE') {
+ if (request.host_component_criteria === 'host_components/HostRoles/ha_state=ACTIVE') {
var yarn = App.YARNService.find().objectAt(0);
if (!yarn.get('isRMHaEnabled')) {
- metric.actual_host_component_criteria = 'host_components/HostRoles/component_name=RESOURCEMANAGER';
- } else {
- metric.actual_host_component_criteria = metric.host_component_criteria;
+ return '';
}
}
break;
}
+ return request.host_component_criteria.replace('host_components/', '&');
},
/**
@@ -195,63 +204,32 @@ App.WidgetMixin = Ember.Mixin.create({
serviceName: request.service_name,
componentName: request.component_name,
metricPaths: request.metric_paths.join(',')
- },
- success: 'getMetricsSuccessCallback'
- });
- },
-
- /**
- * make GET call to server in order to fetch specifc host-component metrics
- * @param {object} request
- * @returns {$.Deferred}
- */
- getHostComponentMetrics: function (request) {
- var dfd;
- var self = this;
- dfd = $.Deferred();
- this.getHostComponentName(request).done(function (data) {
- if (data) {
- request.host_name = data.host_components[0].HostRoles.host_name;
- App.ajax.send({
- name: 'widgets.hostComponent.metrics.get',
- sender: self,
- data: {
- componentName: request.component_name,
- hostName: request.host_name,
- metricPaths: request.metric_paths.join(',')
- }
- }).done(function (metricData) {
- self.getMetricsSuccessCallback(metricData);
- dfd.resolve();
- }).fail(function (data) {
- dfd.reject();
- });
}
- }).fail(function (data) {
- dfd.reject();
});
- return dfd.promise();
},
-
/**
- * make GET call to server in order to fetch host-component names
+ * make GET call to server in order to fetch specific host-component metrics
* @param {object} request
* @returns {$.ajax}
*/
- getHostComponentName: function (request) {
+ getHostComponentMetrics: function (request) {
return App.ajax.send({
- name: 'widgets.hostComponent.get.hostName',
+ name: 'widgets.hostComponent.metrics.get',
sender: this,
data: {
- serviceName: request.service_name,
componentName: request.component_name,
metricPaths: request.metric_paths.join(','),
- hostComponentCriteria: request.actual_host_component_criteria || request.host_component_criteria
+ hostComponentCriteria: this.computeHostComponentCriteria(request)
}
});
},
+ getHostComponentMetricsSuccessCallback: function (data) {
+ if (data.items[0]) {
+ this.getMetricsSuccessCallback(data.items[0]);
+ }
+ },
/**
* callback on getting aggregated metrics and host component metrics
@@ -626,4 +604,116 @@ App.WidgetPreviewMixin = Ember.Mixin.create({
onMetricsLoaded: function () {
this.drawWidget();
}
+});
+
+
+/**
+ * aggregate requests to load metrics by component name
+ * requests can be added via add method
+ * input example:
+ * {
+ * data: request,
+ * context: this,
+ * startCallName: this.getServiceComponentMetrics,
+ * successCallback: this.getMetricsSuccessCallback,
+ * completeCallback: function () {
+ * requestCounter--;
+ * if (requestCounter === 0) this.onMetricsLoaded();
+ * }
+ * }
+ * @type {Em.Object}
+ */
+App.WidgetLoadAggregator = Em.Object.create({
+ /**
+ * @type {Array}
+ */
+ requests: [],
+
+ /**
+ * @type {number|null}
+ */
+ timeoutId: null,
+
+ /**
+ * @type {number}
+ * @const
+ */
+ BULK_INTERVAL: 1000,
+
+ /**
+ * add request
+ * every {{BULK_INTERVAL}} requests get collected, aggregated and sent to server
+ *
+ * @param {object} request
+ */
+ add: function (request) {
+ var self = this;
+
+ this.get('requests').push(request);
+ if (Em.isNone(this.get('timeoutId'))) {
+ this.set('timeoutId', window.setTimeout(function () {
+ self.runRequests(self.get('requests'));
+ self.get('requests').clear();
+ clearTimeout(self.get('timeoutId'));
+ self.set('timeoutId', null);
+ }, this.get('BULK_INTERVAL')));
+ }
+ },
+
+ /**
+ * return requests which grouped into bulks
+ * @param {Array} requests
+ * @returns {object} bulks
+ */
+ groupRequests: function (requests) {
+ var bulks = {};
+
+ requests.forEach(function (request) {
+ var id = request.startCallName + "_" + request.data.component_name;
+
+ if (Em.isNone(bulks[id])) {
+ bulks[id] = {
+ data: request.data,
+ context: request.context,
+ startCallName: request.startCallName
+ };
+ bulks[id].subRequests = [{
+ context: request.context,
+ successCallback: request.successCallback,
+ completeCallback: request.completeCallback
+ }];
+ } else {
+ bulks[id].data.metric_paths.pushObjects(request.data.metric_paths);
+ bulks[id].subRequests.push({
+ context: request.context,
+ successCallback: request.successCallback,
+ completeCallback: request.completeCallback
+ });
+ }
+ }, this);
+ return bulks;
+ },
+
+ /**
+ * run aggregated requests
+ * @param {Array} requests
+ */
+ runRequests: function (requests) {
+ var bulks = this.groupRequests(requests);
+
+ for (var id in bulks) {
+ (function (_request) {
+ _request.data.metric_paths = _request.data.metric_paths.uniq();
+ _request.context[_request.startCallName].call(_request.context, _request.data).done(function (response) {
+ _request.subRequests.forEach(function (subRequest) {
+ subRequest.successCallback.call(subRequest.context, response);
+ }, this);
+ }).complete(function () {
+ _request.subRequests.forEach(function (subRequest) {
+ subRequest.completeCallback.call(subRequest.context);
+ }, this);
+ });
+ })(bulks[id]);
+ }
+ }
});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/599c1da8/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 254e2a9..c0a24c1 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -2522,13 +2522,8 @@ var urls = {
mock: '/data/metrics/{serviceName}/Append_num_ops_&_Delete_num_ops.json'
},
- 'widgets.hostComponent.get.hostName': {
- real: '/clusters/{clusterName}/services/{serviceName}/components/{componentName}?{hostComponentCriteria}',
- mock: '/data/metrics/{serviceName}/Append_num_ops.json'
- },
-
'widgets.hostComponent.metrics.get': {
- real: '/clusters/{clusterName}/hosts/{hostName}/host_components/{componentName}?fields={metricPaths}&format=null_padding',
+ real: '/clusters/{clusterName}/host_components?HostRoles/component_name={componentName}{hostComponentCriteria}&fields={metricPaths}&format=null_padding',
mock: '/data/metrics/{serviceName}/Append_num_ops.json'
},
http://git-wip-us.apache.org/repos/asf/ambari/blob/599c1da8/ambari-web/app/views/common/widget/graph_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/graph_widget_view.js b/ambari-web/app/views/common/widget/graph_widget_view.js
index 321cb91..acdb401 100644
--- a/ambari-web/app/views/common/widget/graph_widget_view.js
+++ b/ambari-web/app/views/common/widget/graph_widget_view.js
@@ -208,31 +208,16 @@ App.GraphWidgetView = Em.View.extend(App.WidgetMixin, {
* @returns {$.ajax}
*/
getHostComponentMetrics: function (request) {
- var dfd;
- var self = this;
- dfd = $.Deferred();
- this.getHostComponentName(request).done(function (data) {
- if (data) {
- request.host_name = data.host_components[0].HostRoles.host_name;
- App.ajax.send({
- name: 'widgets.hostComponent.metrics.get',
- sender: self,
- data: {
- componentName: request.component_name,
- hostName: request.host_name,
- metricPaths: self.addTimeProperties(request.metric_paths).join(',')
- }
- }).done(function(metricData) {
- self.getMetricsSuccessCallback(metricData);
- dfd.resolve();
- }).fail(function(data){
- dfd.reject();
- });
- }
- }).fail(function(data){
- dfd.reject();
+ return App.ajax.send({
+ name: 'widgets.hostComponent.metrics.get',
+ sender: this,
+ data: {
+ componentName: request.component_name,
+ metricPaths: this.addTimeProperties(request.metric_paths).join(','),
+ hostComponentCriteria: this.computeHostComponentCriteria(request)
+ },
+ success: 'getHostComponentMetricsSuccessCallback'
});
- return dfd.promise();
},
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/599c1da8/ambari-web/test/mixins/common/widget_mixin_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mixins/common/widget_mixin_test.js b/ambari-web/test/mixins/common/widget_mixin_test.js
index c9ec6fb..01fb277 100644
--- a/ambari-web/test/mixins/common/widget_mixin_test.js
+++ b/ambari-web/test/mixins/common/widget_mixin_test.js
@@ -25,35 +25,25 @@ describe('App.WidgetMixin', function() {
var mixinObject = mixinClass.create();
beforeEach(function () {
this.mock = sinon.stub(mixinObject, 'getRequestData');
- sinon.stub(mixinObject, 'getHostComponentMetrics').returns({always: function(callback){
- callback();
- }});
- sinon.stub(mixinObject, 'getServiceComponentMetrics').returns({complete: function(callback){
- callback();
- }});
- sinon.stub(mixinObject, 'onMetricsLoaded');
+ sinon.stub(App.WidgetLoadAggregator, 'add');
});
afterEach(function () {
this.mock.restore();
- mixinObject.getHostComponentMetrics.restore();
- mixinObject.getServiceComponentMetrics.restore();
- mixinObject.onMetricsLoaded.restore();
+ App.WidgetLoadAggregator.add.restore();
});
it('has host_component_criteria', function () {
this.mock.returns({'key1': {host_component_criteria: 'criteria'}});
mixinObject.set('isLoaded', false);
mixinObject.loadMetrics();
- expect(mixinObject.getHostComponentMetrics.calledWith({host_component_criteria: 'criteria'})).to.be.true;
- expect(mixinObject.onMetricsLoaded.calledOnce).to.be.true;
+ expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
});
it('host_component_criteria is absent', function () {
this.mock.returns({'key1': {}});
mixinObject.set('isLoaded', false);
mixinObject.loadMetrics();
- expect(mixinObject.getServiceComponentMetrics.calledWith({})).to.be.true;
- expect(mixinObject.onMetricsLoaded.calledOnce).to.be.true;
+ expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
});
});
@@ -175,8 +165,7 @@ describe('App.WidgetMixin', function() {
serviceName: 'S1',
componentName: 'C1',
metricPaths: 'w1,w2'
- },
- success: 'getMetricsSuccessCallback'
+ }
})
});
});
@@ -208,28 +197,12 @@ describe('App.WidgetMixin', function() {
describe("#getHostComponentMetrics()", function () {
var mixinObject = mixinClass.create();
before(function () {
- sinon.stub(App.ajax, 'send').returns({done: function(callback){
- callback();
- return this;
- },fail: function(callback){
- callback();
- return this;
- }});
- sinon.stub(mixinObject, 'getHostComponentName').returns({done: function(callback){
- var data = {host_components: [{HostRoles:{host_name:"c6401"}}]};
- callback(data);
- return this;
- },fail: function(callback){
- callback();
- return this;
- }});
-
- sinon.stub(mixinObject, 'getMetricsSuccessCallback')
+ sinon.stub(App.ajax, 'send');
+ sinon.stub(mixinObject, 'computeHostComponentCriteria').returns('criteria')
});
after(function () {
App.ajax.send.restore();
- mixinObject.getHostComponentName.restore();
- mixinObject.getMetricsSuccessCallback.restore();
+ mixinObject.computeHostComponentCriteria.restore();
});
it("", function () {
var request = {
@@ -243,8 +216,8 @@ describe('App.WidgetMixin', function() {
sender: mixinObject,
data: {
componentName: 'C1',
- hostName: "c6401",
- metricPaths: 'w1,w2'
+ metricPaths: 'w1,w2',
+ hostComponentCriteria: 'criteria'
}
})
});
@@ -362,3 +335,113 @@ describe('App.WidgetMixin', function() {
});
});
});
+
+
+describe('App.WidgetLoadAggregator', function() {
+ var aggregator = App.WidgetLoadAggregator;
+
+ describe("#add()", function () {
+ beforeEach(function () {
+ sinon.stub(window, 'setTimeout').returns('timeId');
+ });
+ afterEach(function () {
+ window.setTimeout.restore();
+ });
+ it("timeout started", function () {
+ aggregator.set('timeoutId', 'timeId');
+ aggregator.get('requests').clear();
+ aggregator.add({});
+ expect(aggregator.get('requests')).to.not.be.empty;
+ expect(window.setTimeout.called).to.be.false;
+ });
+ it("timeout started", function () {
+ aggregator.set('timeoutId', null);
+ aggregator.get('requests').clear();
+ aggregator.add({});
+ expect(aggregator.get('requests')).to.not.be.empty;
+ expect(window.setTimeout.calledOnce).to.be.true;
+ expect(aggregator.get('timeoutId')).to.equal('timeId');
+ });
+ });
+
+ describe("#groupRequests()", function () {
+ it("", function () {
+ var requests = [
+ {
+ startCallName: 'n1',
+ data: {
+ component_name: 'C1',
+ metric_paths: ['m1']
+ },
+ context: 'c1'
+ },
+ {
+ startCallName: 'n1',
+ data: {
+ component_name: 'C1',
+ metric_paths: ['m2']
+ },
+ context: 'c2'
+ },
+ {
+ startCallName: 'n2',
+ data: {
+ component_name: 'C1',
+ metric_paths: ['m3']
+ },
+ context: 'c3'
+ },
+ {
+ startCallName: 'n1',
+ data: {
+ component_name: 'C2',
+ metric_paths: ['m4']
+ },
+ context: 'c4'
+ }
+ ];
+ var result = aggregator.groupRequests(requests);
+
+ expect(result['n1_C1'].subRequests.length).to.equal(2);
+ expect(result['n1_C1'].data.metric_paths.length).to.equal(2);
+ expect(result['n2_C1'].subRequests.length).to.equal(1);
+ expect(result['n2_C1'].data.metric_paths.length).to.equal(1);
+ expect(result['n1_C2'].subRequests.length).to.equal(1);
+ expect(result['n1_C2'].data.metric_paths.length).to.equal(1);
+ });
+ });
+
+ describe("#runRequests()", function() {
+ var mock = {
+ f1: function () {
+ return {
+ done: Em.K,
+ complete: Em.K
+ }
+ }
+ };
+ beforeEach(function () {
+ sinon.stub(aggregator, 'groupRequests', function(requests){
+ return requests;
+ });
+ sinon.spy(mock, 'f1');
+ });
+ afterEach(function () {
+ aggregator.groupRequests.restore();
+ mock.f1.restore();
+ });
+ it("", function() {
+ var requests = {
+ 'r1': {
+ data: {
+ metric_paths: ['m1', 'm1', 'm2']
+ },
+ context: mock,
+ startCallName: 'f1'
+ }
+ };
+ aggregator.runRequests(requests);
+ expect(mock.f1.calledWith(requests['r1'].data)).to.be.true;
+ });
+ });
+});