Repository: ambari Updated Branches: refs/heads/trunk 52806bd7f -> 5c84352f5
AMBARI-5897 Convert Hosts page to use aggregate counts provided by the API, rather than using client-computed info. (atkach) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/5c84352f Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/5c84352f Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/5c84352f Branch: refs/heads/trunk Commit: 5c84352f51b19c28c745beb5cc5ac9f7ba36bd23 Parents: 52806bd Author: atkach <[email protected]> Authored: Tue May 27 12:41:09 2014 +0300 Committer: atkach <[email protected]> Committed: Tue May 27 12:41:09 2014 +0300 ---------------------------------------------------------------------- .../data/hosts/HDP2/host_status_counters.json | 27 +++ .../data/services/HDP2/components_state.json | 175 +++++++++++++++++++ .../app/controllers/global/update_controller.js | 2 +- ambari-web/app/data/host/categories.js | 24 +-- .../app/mappers/components_state_mapper.js | 2 +- .../app/mixins/common/tableServerProvider.js | 12 ++ ambari-web/app/templates/main/host.hbs | 10 +- ambari-web/app/utils/ajax/ajax.js | 4 + ambari-web/app/views/main/host.js | 133 +++++++++----- .../global/update_controller_test.js | 6 +- .../widgets/node_managers_live_test.js | 14 +- 11 files changed, 328 insertions(+), 81 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/assets/data/hosts/HDP2/host_status_counters.json ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/data/hosts/HDP2/host_status_counters.json b/ambari-web/app/assets/data/hosts/HDP2/host_status_counters.json new file mode 100644 index 0000000..1e544e0 --- /dev/null +++ b/ambari-web/app/assets/data/hosts/HDP2/host_status_counters.json @@ -0,0 +1,27 @@ +{ + "Clusters" : { + "cluster_name" : "c", + "health_report" : { + "Host/stale_config" : 1, + "Host/maintenance_state" : 0, + "Host/host_state/HEALTHY" : 1, + "Host/host_state/UNHEALTHY" : 0, + "Host/host_state/HEARTBEAT_LOST" : 0, + "Host/host_state/INIT" : 0, + "Host/host_status/HEALTHY" : 1, + "Host/host_status/UNHEALTHY" : 0, + "Host/host_status/UNKNOWN" : 0, + "Host/host_status/ALERT" : 0 + }, + "total_hosts" : 1, + "version" : "HDP-2.1" + }, + "alerts" : { + "summary" : { + "CRITICAL" : 0, + "OK" : 1, + "PASSIVE" : 0, + "WARNING" : 0 + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/assets/data/services/HDP2/components_state.json ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/data/services/HDP2/components_state.json b/ambari-web/app/assets/data/services/HDP2/components_state.json new file mode 100644 index 0000000..900737f --- /dev/null +++ b/ambari-web/app/assets/data/services/HDP2/components_state.json @@ -0,0 +1,175 @@ +{ + "items" : [ + { + "ServiceComponentInfo" : { + "component_name" : "FALCON_CLIENT", + "installed_count" : 1, + "service_name" : "FALCON", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "GANGLIA_MONITOR", + "installed_count" : 0, + "service_name" : "GANGLIA", + "started_count" : 1, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "HBASE_CLIENT", + "installed_count" : 1, + "service_name" : "HBASE", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "HBASE_REGIONSERVER", + "installed_count" : 0, + "service_name" : "HBASE", + "started_count" : 1, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "HCAT", + "installed_count" : 1, + "service_name" : "HCATALOG", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "DATANODE", + "installed_count" : 0, + "service_name" : "HDFS", + "started_count" : 1, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "HDFS_CLIENT", + "installed_count" : 1, + "service_name" : "HDFS", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "ZKFC", + "installed_count" : 0, + "service_name" : "HDFS", + "started_count" : 0, + "total_count" : 0 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "HIVE_CLIENT", + "installed_count" : 1, + "service_name" : "HIVE", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "MAPREDUCE2_CLIENT", + "installed_count" : 1, + "service_name" : "MAPREDUCE2", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "OOZIE_CLIENT", + "installed_count" : 1, + "service_name" : "OOZIE", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "PIG", + "installed_count" : 1, + "service_name" : "PIG", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "SQOOP", + "installed_count" : 1, + "service_name" : "SQOOP", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "SUPERVISOR", + "installed_count" : 0, + "service_name" : "STORM", + "started_count" : 1, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "TEZ_CLIENT", + "installed_count" : 1, + "service_name" : "TEZ", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "APP_TIMELINE_SERVER", + "installed_count" : 0, + "service_name" : "YARN", + "started_count" : 1, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "NODEMANAGER", + "installed_count" : 0, + "service_name" : "YARN", + "started_count" : 1, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "YARN_CLIENT", + "installed_count" : 1, + "service_name" : "YARN", + "started_count" : 0, + "total_count" : 1 + } + }, + { + "ServiceComponentInfo" : { + "component_name" : "ZOOKEEPER_CLIENT", + "installed_count" : 1, + "service_name" : "ZOOKEEPER", + "started_count" : 0, + "total_count" : 1 + } + } + ] +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/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 c7fa022..dca6f0d 100644 --- a/ambari-web/app/controllers/global/update_controller.js +++ b/ambari-web/app/controllers/global/update_controller.js @@ -249,7 +249,7 @@ App.UpdateController = Em.Controller.extend({ }); }, updateComponentsState: function (callback) { - var testUrl = ''; + var testUrl = '/data/services/HDP2/components_state.json'; var realUrl = '/components/?ServiceComponentInfo/category.in(SLAVE,CLIENT)&fields=ServiceComponentInfo/service_name,' + 'ServiceComponentInfo/installed_count,ServiceComponentInfo/started_count,ServiceComponentInfo/total_count&minimal_response=true'; var url = this.getUrl(testUrl, realUrl); http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/data/host/categories.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/data/host/categories.js b/ambari-web/app/data/host/categories.js index 1c8bcb7..8e8ef13 100644 --- a/ambari-web/app/data/host/categories.js +++ b/ambari-web/app/data/host/categories.js @@ -28,29 +28,25 @@ module.exports = [ value: Em.I18n.t('hosts.host.healthStatusCategory.green'), isHealthStatus: true, class: App.healthIconClassGreen, - healthStatusValue: 'health-status-LIVE', - observes: '[email protected]' + healthStatusValue: 'health-status-LIVE' }, { value: Em.I18n.t('hosts.host.healthStatusCategory.red'), isHealthStatus: true, class: App.healthIconClassRed, - healthStatusValue: 'health-status-DEAD-RED', - observes: '[email protected]' + healthStatusValue: 'health-status-DEAD-RED' }, { value: Em.I18n.t('hosts.host.healthStatusCategory.orange'), isHealthStatus: true, class: App.healthIconClassOrange, - healthStatusValue: 'health-status-DEAD-ORANGE', - observes: '[email protected]' + healthStatusValue: 'health-status-DEAD-ORANGE' }, { value: Em.I18n.t('hosts.host.healthStatusCategory.yellow'), isHealthStatus: true, class: App.healthIconClassYellow, - healthStatusValue: 'health-status-DEAD-YELLOW', - observes: '[email protected]' + healthStatusValue: 'health-status-DEAD-YELLOW' }, { value: Em.I18n.t('hosts.host.alerts.label'), @@ -60,8 +56,7 @@ module.exports = [ healthStatusValue: 'health-status-WITH-ALERTS', column: 7, type: 'number', - filterValue: '>0', - observes: '[email protected]' + filterValue: '>0' }, { value: Em.I18n.t('common.restart'), @@ -71,8 +66,7 @@ module.exports = [ healthStatusValue: 'health-status-RESTART', column: 8, type: 'number', - filterValue: '>0', - observes: '[email protected]' + filterValue: '>0' }, { value: Em.I18n.t('common.selected'), @@ -84,8 +78,7 @@ module.exports = [ column: 10, type: 'boolean', filterValue: true, - isVisible: false, - observes: '[email protected]' + isVisible: false }, { value: Em.I18n.t('common.passive_state'), @@ -95,7 +88,6 @@ module.exports = [ healthStatusValue: 'health-status-PASSIVE_STATE', column: 9, type: 'number', - filterValue: '>0', - observes: '[email protected]' + filterValue: '>0' } ]; http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/mappers/components_state_mapper.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mappers/components_state_mapper.js b/ambari-web/app/mappers/components_state_mapper.js index b29f882..7561d3a 100644 --- a/ambari-web/app/mappers/components_state_mapper.js +++ b/ambari-web/app/mappers/components_state_mapper.js @@ -129,8 +129,8 @@ App.componentsStateMapper = App.QuickDataMapper.create({ var cacheService = App.cache['services'].findProperty('ServiceInfo.service_name', item.ServiceComponentInfo.service_name); for (var i in parsedItem) { - cacheService[i] = parsedItem[i]; if (service.get('isLoaded')) { + cacheService[i] = parsedItem[i]; service.set(stringUtils.underScoreToCamelCase(i), parsedItem[i]); if (extendedModel) { extendedModel.set(stringUtils.underScoreToCamelCase(i), parsedItem[i]); http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/mixins/common/tableServerProvider.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/tableServerProvider.js b/ambari-web/app/mixins/common/tableServerProvider.js index 725469e..8c8391b 100644 --- a/ambari-web/app/mixins/common/tableServerProvider.js +++ b/ambari-web/app/mixins/common/tableServerProvider.js @@ -31,6 +31,18 @@ App.TableServerProvider = Em.Mixin.create({ */ refreshTriggers: [], refreshCompleted: true, + /** + * total number of entities in table + */ + totalCount: 0, + + /** + * Return pagination information displayed on the page + * @type {String} + */ + paginationInfo: function () { + return this.t('tableView.filters.paginationInfo').format(this.get('startIndex'), this.get('endIndex'), this.get('totalCount')); + }.property('totalCount', 'endIndex'), /** * add observers to trigger properties http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/templates/main/host.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/host.hbs b/ambari-web/app/templates/main/host.hbs index 36c5eae..b5deaa0 100644 --- a/ambari-web/app/templates/main/host.hbs +++ b/ambari-web/app/templates/main/host.hbs @@ -37,17 +37,13 @@ </a> <ul class="dropdown-menu"> <li {{bindAttr class=":category-item view.filtersUsed::active"}}> - <a {{action clearFilter target="view"}} href="#">{{t common.all}} ({{content.length}})</a> + <a {{action clearFilter target="view"}} href="#">{{t common.all}} ({{view.parentView.totalCount}})</a> </li> {{#each category in view.categories}} {{#if category.isVisible}} - <li {{bindAttr class="aaa :category-item category.itemClass"}}> + <li {{bindAttr class=":category-item category.itemClass"}}> <a {{action selectCategory category target="view"}} href="#"> - {{#if category.isHealthStatus}} - <span {{bindAttr class=":health-status category.healthStatusValue category.class"}}></span> - {{else}} - <span {{bindAttr class="category.class"}}></span> - {{/if}} + <span {{bindAttr class="category.isHealthStatus:health-status category.healthStatusValue category.class"}}></span> {{category.label}} </a> </li> http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/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 0e228d9..1b11ee1 100644 --- a/ambari-web/app/utils/ajax/ajax.js +++ b/ambari-web/app/utils/ajax/ajax.js @@ -2085,6 +2085,10 @@ var urls = { url: opt.url + data.urlParams } } + }, + 'host.status.counters': { + 'real': '/clusters/{clusterName}?fields=alerts,Clusters/health_report,Clusters/total_hosts&minimal_response=true', + 'mock': '/data/hosts/HDP2/host_status_counters.json' } }; /** http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/app/views/main/host.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/host.js b/ambari-web/app/views/main/host.js index ee3510b..5970212 100644 --- a/ambari-web/app/views/main/host.js +++ b/ambari-web/app/views/main/host.js @@ -39,7 +39,12 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, { refreshTriggers: ['serverStartIndex', 'displayLength'], /** - * startIndex as query parameter have first index - "0" + * flag responsible for updating status counters of hosts + */ + isCountersUpdating: false, + + /** + * startIndex as query parameter have first index - 0 */ serverStartIndex: function() { return this.get('startIndex') - 1; @@ -64,14 +69,12 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, { this.addObserver('controller.clearFilters', this, this.clearFiltersObs); this.clearFiltersObs(); this.addObserver('selectAllHosts', this, this.toggleAllHosts); + this.set('isCountersUpdating', true); + this.updateStatusCounters(); }, willDestroyElement: function() { - this.get('categories').forEach(function(c) { - if (c.get('observes')) { - c.removeObserver(c.get('observes'), c, c.updateHostsCount); - } - }); + this.set('isCountersUpdating', false); }, /** @@ -297,6 +300,80 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, { }), /** + * update hosts count of selected hosts category + */ + updateSelectedCategory: function () { + var hostsCountMap = { + 'health-status-SELECTED': this.get('content').filterProperty('selected').length + }; + + this.updateHostsCount(hostsCountMap); + }.observes('[email protected]'), + + /** + * update status counters of hosts + */ + updateStatusCounters: function () { + var self = this; + + if (this.get('isCountersUpdating')) { + App.ajax.send({ + name: 'host.status.counters', + sender: this, + data: {}, + success: 'updateStatusCountersSuccessCallback', + error: 'updateStatusCountersErrorCallback' + }); + + setTimeout(function () { + self.updateStatusCounters(); + }, App.get('componentsUpdateInterval')); + } + }, + + /** + * success callback on <code>updateStatusCounters()</code> + * map counters' value to categories + * @param data + */ + updateStatusCountersSuccessCallback: function (data) { + var hostsCountMap = { + 'health-status-LIVE': data.Clusters.health_report['Host/host_status/HEALTHY'], + 'health-status-DEAD-RED': data.Clusters.health_report['Host/host_status/UNHEALTHY'], + 'health-status-DEAD-ORANGE': data.Clusters.health_report['Host/host_status/ALERT'], + 'health-status-DEAD-YELLOW': data.Clusters.health_report['Host/host_status/UNKNOWN'], + 'health-status-WITH-ALERTS': data.alerts.summary.CRITICAL + data.alerts.summary.WARNING, + 'health-status-RESTART': data.Clusters.health_report['Host/stale_config'], + 'health-status-PASSIVE_STATE': data.Clusters.health_report['Host/maintenance_state'], + 'TOTAL': data.Clusters.total_hosts + }; + + this.set('totalCount', data.Clusters.total_hosts); + this.updateHostsCount(hostsCountMap); + }, + + /** + * success callback on <code>updateStatusCounters()</code> + */ + updateStatusCountersErrorCallback: function() { + console.warn('ERROR: updateStatusCounters failed') + }, + + /** + * Update <code>hostsCount</code> in every category + */ + updateHostsCount: function(hostsCountMap) { + this.get('categories').forEach(function(category) { + var hostsCount = (category.get('healthStatusValue').trim() === "") ? hostsCountMap['TOTAL'] : hostsCountMap[category.get('healthStatusValue')]; + + if (!Em.isNone(hostsCount)) { + category.set('hostsCount', hostsCount); + category.set('hasHosts', (hostsCount > 0)); + } + }, this); + }, + + /** * Category view for all hosts * @type {Object} */ @@ -379,33 +456,6 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, { }.property('isActive'), /** - * Trigger updating <code>hostsCount</code> only 1 time - */ - updateHostsCount: function() { - Em.run.once(this, 'updateOnce'); - }, - /** - * Update <code>hostsCount</code> in current category - */ - updateOnce: function() { - //skip update when view is destroyed - if(!this.get('view.content')) return; - var statusString = this.get('healthStatusValue'); - if (this.get('isHealthStatus')) { - if (statusString == "") { - this.set('hostsCount', this.get('view.content').get('length')); - } - else { - this.set('hostsCount', this.get('view.content').filterProperty('healthClass', statusString).get('length')); - } - } - else { - this.set('hostsCount', this.get('view.content').filterProperty(this.get('hostProperty')).get('length')); - } - this.set('hasHosts', !!this.get('hostsCount')); - }, - - /** * Text shown on the right of category icon * @type {String} */ @@ -420,19 +470,10 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, { */ categories: function () { var self = this; - self.categoryObject.reopen({ - view: self - }); - var category_mocks = require('data/host/categories'); return category_mocks.map(function(category_mock) { - var c = self.categoryObject.create(category_mock); - if (c.get('observes')) { - c.addObserver(c.get('observes'), c, c.updateHostsCount); - c.updateHostsCount(); - } - return c; + return self.categoryObject.create(category_mock); }); }.property(), @@ -449,13 +490,13 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, { value: null, class: "", comboBoxLabel: function(){ - var selected = this.get('categories').findProperty('isActive',true); + var selected = this.get('categories').findProperty('isActive'); if (!this.get('value') || !selected) { - return "%@ (%@)".fmt(Em.I18n.t('common.all'), this.get('parentView.content.length')); + return "%@ (%@)".fmt(Em.I18n.t('common.all'), this.get('parentView.totalCount')); } else { return "%@ (%@)".fmt(selected.get('value'), selected.get('hostsCount')) } - }.property('value'), + }.property('value', 'parentView.totalCount'), /** * switch active category label */ http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/test/controllers/global/update_controller_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/global/update_controller_test.js b/ambari-web/test/controllers/global/update_controller_test.js index 3f4119e..b272cf9 100644 --- a/ambari-web/test/controllers/global/update_controller_test.js +++ b/ambari-web/test/controllers/global/update_controller_test.js @@ -50,7 +50,7 @@ describe('App.UpdateController', function () { describe('#updateAll()', function () { beforeEach(function () { - sinon.spy(App.updater, 'run'); + sinon.stub(App.updater, 'run', Em.K); }); afterEach(function () { App.updater.run.restore(); @@ -63,14 +63,14 @@ describe('App.UpdateController', function () { it('isWorking = true, App.supports.hostOverrides = false', function () { App.supports.hostOverrides = false; controller.set('isWorking', true); - expect(App.updater.run.callCount).to.equal(4); + expect(App.updater.run.callCount).to.equal(5); controller.set('isWorking', false); }); it('isWorking = true, App.supports.hostOverrides = true', function () { App.supports.hostOverrides = true; controller.set('isWorking', true); - expect(App.updater.run.callCount).to.equal(5); + expect(App.updater.run.callCount).to.equal(6); }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/5c84352f/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js index 2edfe8e..3f7a4ac 100644 --- a/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js +++ b/ambari-web/test/views/main/dashboard/widgets/node_managers_live_test.js @@ -28,8 +28,8 @@ describe('App.NodeManagersLiveView', function() { var tests = [ { model: { - nodeManagerNodes: [{}, {}, {}], - nodeManagerLiveNodes: [{}, {}] + nodeManagersTotal: 3, + nodeManagerLiveNodes: 2 }, e: { isRed: false, @@ -42,8 +42,8 @@ describe('App.NodeManagersLiveView', function() { }, { model: { - nodeManagerNodes: [{},{}], - nodeManagerLiveNodes: [{},{}] + nodeManagersTotal: 2, + nodeManagerLiveNodes: 2 }, e: { isRed: false, @@ -56,8 +56,8 @@ describe('App.NodeManagersLiveView', function() { }, { model: { - nodeManagerNodes: [{}, {}], - nodeManagerLiveNodes: [] + nodeManagersTotal: 2, + nodeManagerLiveNodes: 0 }, e: { isRed: true, @@ -71,7 +71,7 @@ describe('App.NodeManagersLiveView', function() { ]; tests.forEach(function(test) { - describe('nodeManagerNodes length - ' + test.model.nodeManagerNodes.length + ' | nodeManagerLiveNodes length - ' + test.model.nodeManagerLiveNodes.length, function() { + describe('nodeManagerNodes length - ' + test.model.nodeManagersTotal + ' | nodeManagerLiveNodes length - ' + test.model.nodeManagerLiveNodes, function() { var AppNodeManagersLiveView = App.NodeManagersLiveView.extend({nodeManagersLive: test.model.nodeManagerLiveNodes}); var nodeManagersLiveView = AppNodeManagersLiveView.create({model_type:null, model: test.model}); it('content', function() {
