Browse Source

AMBARI-5897 Convert Hosts page to use aggregate counts provided by the API, rather than using client-computed info. (atkach)

atkach 11 years ago
parent
commit
5c84352f51

+ 27 - 0
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
+    }
+  }
+}

+ 175 - 0
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
+      }
+    }
+  ]
+}

+ 1 - 1
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);

+ 8 - 16
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: 'view.content.@each.healthClass'
+    healthStatusValue: 'health-status-LIVE'
   },
   {
     value: Em.I18n.t('hosts.host.healthStatusCategory.red'),
     isHealthStatus: true,
     class: App.healthIconClassRed,
-    healthStatusValue: 'health-status-DEAD-RED',
-    observes: 'view.content.@each.healthClass'
+    healthStatusValue: 'health-status-DEAD-RED'
   },
   {
     value: Em.I18n.t('hosts.host.healthStatusCategory.orange'),
     isHealthStatus: true,
     class: App.healthIconClassOrange,
-    healthStatusValue: 'health-status-DEAD-ORANGE',
-    observes: 'view.content.@each.healthClass'
+    healthStatusValue: 'health-status-DEAD-ORANGE'
   },
   {
     value: Em.I18n.t('hosts.host.healthStatusCategory.yellow'),
     isHealthStatus: true,
     class: App.healthIconClassYellow,
-    healthStatusValue: 'health-status-DEAD-YELLOW',
-    observes: 'view.content.@each.healthClass'
+    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: 'view.content.@each.criticalAlertsCount'
+    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: 'view.content.@each.componentsWithStaleConfigsCount'
+    filterValue: '>0'
   },
   {
     value: Em.I18n.t('common.selected'),
@@ -84,8 +78,7 @@ module.exports = [
     column: 10,
     type: 'boolean',
     filterValue: true,
-    isVisible: false,
-    observes: 'view.content.@each.selected'
+    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: 'view.content.@each.componentsInPassiveStateCount'
+    filterValue: '>0'
   }
 ];

+ 1 - 1
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]);

+ 12 - 0
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

+ 3 - 7
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>

+ 4 - 0
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'
   }
 };
 /**

+ 87 - 46
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);
   },
 
   /**
@@ -296,6 +299,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('content.@each.selected'),
+
+  /**
+   * 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}
@@ -378,33 +455,6 @@ App.MainHostView = App.TableView.extend(App.TableServerProvider, {
       return this.get('isActive') ? 'active' : '';
     }.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
      */

+ 3 - 3
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);
     });
   });
 

+ 7 - 7
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() {