Переглянути джерело

AMBARI-12469 Ambari Web Scalability: performance analysis. (atkach)

Andrii Tkach 10 роки тому
батько
коміт
99cac719db

+ 1 - 12
ambari-web/app/controllers/main/host.js

@@ -54,18 +54,7 @@ App.MainHostController = Em.ArrayController.extend(App.TableServerMixin, {
     return installedComponents;
   }.property('App.router.clusterController.isLoaded'),
 
-  content: [],
-
-  requestedObserver: function() {
-    Em.run.once(this, 'setContentOnce');
-  }.observes('dataSource.@each.isRequested'),
-
-  setContentOnce: function() {
-    var self = this;
-    Em.run.next(function() {
-      self.set('content', self.get('dataSource').filterProperty('isRequested'));
-    });
-  },
+  content: App.Host.find(),
 
   allHostStackVersions: App.HostStackVersion.find(),
   /**

+ 1 - 0
ambari-web/app/controllers/main/host/details.js

@@ -382,6 +382,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    */
   removeHostComponentModel: function(componentName, hostName) {
     var component = App.HostComponent.find().filterProperty('componentName', componentName).findProperty('hostName', hostName);
+    App.cache['services'].findProperty('ServiceInfo.service_name', component.get('serviceName')).host_components.without(component.get('id'));
     App.serviceMapper.deleteRecord(component);
   },
 

+ 0 - 1
ambari-web/app/mappers/component_config_mapper.js

@@ -42,7 +42,6 @@ App.componentConfigMapper = App.QuickDataMapper.create({
     // We do not want to parse JSON if there is no need to
     var hostComponentJsonMap = {};
     var hostComponentJsonIds = [];
-    var hostComponentLoaded = {};
 
     if (json.items.length > 0 || this.get('model').find().someProperty('staleConfigs', true)) {
       json.items.forEach(function (item) {

+ 29 - 13
ambari-web/app/mappers/hosts_mapper.js

@@ -92,27 +92,45 @@ App.hostsMapper = App.QuickDataMapper.create({
       var selectedHosts = App.db.getSelectedHosts('mainHostController');
       var stackUpgradeSupport = App.get('supports.stackUpgrade');
       var clusterName = App.get('clusterName');
+      var advancedHostComponents = [];
 
       json.items.forEach(function (item, index) {
+        var notStartedComponents = [];
+        var componentsInPassiveState = [];
+        var componentsWithStaleConfigs = [];
+
         item.host_components = item.host_components || [];
         item.host_components.forEach(function (host_component) {
-          host_component.id = host_component.HostRoles.component_name + "_" + item.Hosts.host_name;
+          var id = host_component.HostRoles.component_name + "_" + item.Hosts.host_name;
           var component = this.parseIt(host_component, this.hostComponentConfig);
           var serviceName = host_component.HostRoles.service_name;
 
-          component.id = host_component.HostRoles.component_name + "_" + item.Hosts.host_name;
+          host_component.id = id;
+          component.id = id;
           component.host_id = item.Hosts.host_name;
           component.host_name = item.Hosts.host_name;
           components.push(component);
-          componentsIdMap[component.id] = component;
+          componentsIdMap[id] = component;
           if (!newHostComponentsMap[serviceName]) {
             newHostComponentsMap[serviceName] = [];
           }
           if (!currentServiceComponentsMap[serviceName]) {
             currentServiceComponentsMap[serviceName] = [];
           }
-          if (!currentServiceComponentsMap[serviceName][component.id]) {
-            newHostComponentsMap[serviceName].push(component.id);
+          if (!currentServiceComponentsMap[serviceName][id]) {
+            newHostComponentsMap[serviceName].push(id);
+          }
+          if (App.serviceMetricsMapper.get('ADVANCED_COMPONENTS').contains(host_component.HostRoles.component_name)) {
+            advancedHostComponents.push(id);
+          }
+          if (component.work_status !== App.HostComponentStatus.started) {
+            notStartedComponents.push(id);
+          }
+          if (component.stale_configs) {
+            componentsWithStaleConfigs.push(id);
+          }
+          if (component.passive_state !== 'OFF') {
+            componentsInPassiveState.push(id);
           }
         }, this);
 
@@ -145,6 +163,9 @@ App.hostsMapper = App.QuickDataMapper.create({
         var parsedItem = this.parseIt(item, this.config);
         parsedItem.is_requested = true;
         parsedItem.selected = selectedHosts.contains(parsedItem.host_name);
+        parsedItem.not_started_components = notStartedComponents;
+        parsedItem.components_in_passive_state = componentsInPassiveState;
+        parsedItem.components_with_stale_configs = componentsWithStaleConfigs;
 
         hostIds[item.Hosts.host_name] = parsedItem;
 
@@ -156,20 +177,15 @@ App.hostsMapper = App.QuickDataMapper.create({
       }
 
 
-      App.Host.find().filterProperty('isRequested', true)
-        .filter(function(item) {
-          return !hostIds[item.get('hostName')];
-        })
-        .setEach('isRequested', false);
-
-      App.HostComponent.find().filterProperty('isMaster').forEach(function(component) {
-        if (componentsIdMap[component.get('id')]) componentsIdMap[component.get('id')].display_name_advanced = component.get('displayNameAdvanced');
+      advancedHostComponents.forEach(function(id) {
+        if (componentsIdMap[id]) componentsIdMap[id].display_name_advanced = App.HostComponent.find(id).get('displayNameAdvanced');
       });
       App.store.commit();
       if (stackUpgradeSupport) {
         App.store.loadMany(App.HostStackVersion, stackVersions);
       }
       App.store.loadMany(App.HostComponent, components);
+      App.Host.find().clear();
       App.store.loadMany(App.Host, hostsWithFullInfo);
       var itemTotal = parseInt(json.itemTotal);
       if (!isNaN(itemTotal)) {

+ 0 - 36
ambari-web/app/mappers/server_data_mapper.js

@@ -110,7 +110,6 @@ App.QuickDataMapper = App.ServerDataMapper.extend({
   getJsonProperty: function (json, path) {
     var pathArr = path.split('.');
     var current = json;
-    pathArr = this.filterDotted(pathArr);
     while (pathArr.length && current) {
       if (pathArr[0].substr(-1) == ']') {
         var index = parseInt(pathArr[0].substr(-2, 1));
@@ -126,29 +125,6 @@ App.QuickDataMapper = App.ServerDataMapper.extend({
     return current;
   },
 
-  filterDotted: function(arr) {
-    var buffer = [];
-    var dottedBuffer = [];
-    var dotted = false;
-    arr.forEach(function(item) {
-      if(/\['|\["/.test(item)) {
-        dottedBuffer.push(item.substr(2, item.length));
-        dotted = true;
-      } else if (dotted && !/\]'|"\]/.test(item)) {
-        dottedBuffer.push(item);
-      } else if (/']|"]/.test(item)) {
-        dottedBuffer.push(item.substr(0, item.length - 2));
-        buffer.push(dottedBuffer.join('.'));
-        dotted = false;
-        dottedBuffer = [];
-      } else {
-        buffer.push(item);
-      }
-    });
-
-    return buffer;
-  },
-
   /**
    * properly delete record from model
    * @param item
@@ -183,17 +159,5 @@ App.QuickDataMapper = App.ServerDataMapper.extend({
       return result;
     }
     return current;
-  },
-
-  calculateState: function (json) {
-//    var stateEqual = (json.desired_status != json.work_status);
-//    if (stateEqual) {
-//      if (json.desired_status == 'STARTED' && json.work_status == 'INSTALLED') {
-//        json.work_status = 'STARTING';
-//      } else if (json.desired_status == 'INSTALLED' && json.work_status == 'STARTED') {
-//        json.work_status = 'STOPPING';
-//      }
-//    }
-    return json;
   }
 });

+ 52 - 35
ambari-web/app/mappers/service_metrics_mapper.js

@@ -19,7 +19,7 @@ var App = require('app');
 var misc = require('utils/misc');
 var stringUtils = require('utils/string_utils');
 var dateUtils = require('utils/date');
-var previousResponse = [];
+var previousMasterComponentIds = [];
 
 App.serviceMetricsMapper = App.QuickDataMapper.create({
 
@@ -44,6 +44,8 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
     standby_name_node_id: 'standby_name_node_id',
     standby_name_node2_id: 'standby_name_node2_id',
     journal_nodes: 'journal_nodes',
+    name_node_id: 'name_node_id',
+    sname_node_id: 'sname_node_id',
     metrics_not_available: 'metrics_not_available',
     name_node_start_time: 'nameNodeComponent.host_components[0].metrics.runtime.StartTime',
     jvm_memory_heap_used: 'nameNodeComponent.host_components[0].metrics.jvm.HeapMemoryUsed',
@@ -95,14 +97,19 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
     queue: 'resourceManagerComponent.queue',
     node_managers_started: 'node_managers_started',
     node_managers_installed: 'node_managers_installed',
-    node_managers_total: 'node_managers_total'
+    node_managers_total: 'node_managers_total',
+    app_timeline_server_id: 'app_timeline_server_id',
+    resource_manager_id: 'resource_manager_id',
+    active_resource_manager_id: 'active_resource_manager_id'
   },
   mapReduce2Config: {
-    map_reduce2_clients: 'map_reduce2_clients'
+    map_reduce2_clients: 'map_reduce2_clients',
+    job_history_server_id: 'job_history_server_id'
   },
   hbaseConfig: {
     master_start_time: 'masterComponent.host_components[0].metrics.hbase.master.MasterStartTime',
     master_active_time: 'masterComponent.host_components[0].metrics.hbase.master.MasterActiveTime',
+    master_id: 'master_id',
     average_load: 'masterComponent.host_components[0].metrics.hbase.master.AverageLoad',
     heap_memory_used: 'masterComponent.host_components[0].metrics.jvm.HeapMemoryUsed',
     heap_memory_max: 'masterComponent.host_components[0].metrics.jvm.HeapMemoryMax',
@@ -141,20 +148,24 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
 
   model3: App.HostComponent,
   config3: {
-    id: 'id',
     work_status: 'HostRoles.state',
     passive_state: 'HostRoles.maintenance_state',
-    desired_status: 'HostRoles.desired_state',
     component_name: 'HostRoles.component_name',
     host_id: 'HostRoles.host_name',
     host_name: 'HostRoles.host_name',
     stale_configs: 'HostRoles.stale_configs',
     ha_status: 'HostRoles.ha_state',
     display_name_advanced: 'display_name_advanced',
-    $service_id: 'none', /* will be set outside of parse function */
     admin_state: 'HostRoles.desired_admin_state'
   },
 
+  /**
+   * components which have additional relations and filtered for <code>computeAdditionalRelations</code>
+   * @type {Array}
+   * @const
+   */
+  ADVANCED_COMPONENTS: ['SECONDARY_NAMENODE', 'RESOURCEMANAGER', 'NAMENODE', 'HBASE_MASTER', 'RESOURCEMANAGER'],
+
   map: function (json) {
     console.time('App.serviceMetricsMapper execution time');
     if (json.items) {
@@ -165,14 +176,13 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
       var previousComponentStatuses = App.cache['previousComponentStatuses'];
       var previousComponentPassiveStates = App.cache['previousComponentPassiveStates'];
       var result = [];
+      var advancedHostComponents = [];
+      var hostComponentIdsMap = {};
+
       /**
        * services contains constructed service-components structure from components array
        */
-
-      services.forEach(function (service) {
-        service.host_components = [];
-        service.components = [];
-      });
+      services.setEach('components', []);
 
       json.items.forEach(function (component) {
         var serviceName = component.ServiceComponentInfo.service_name;
@@ -181,28 +191,38 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
           service.components.push(component);
         }
         component.host_components.forEach(function (host_component) {
-          host_component.id = host_component.HostRoles.component_name + "_" + host_component.HostRoles.host_name;
+          var id = host_component.HostRoles.component_name + "_" + host_component.HostRoles.host_name;
+          hostComponentIdsMap[id] = true;
           previousComponentStatuses[host_component.id] = host_component.HostRoles.state;
           previousComponentPassiveStates[host_component.id] = host_component.HostRoles.maintenance_state;
           this.config3.ha_status = host_component.HostRoles.component_name == "HBASE_MASTER" ?
             'metrics.hbase.master.IsActiveMaster' : 'HostRoles.ha_state';
           var comp = this.parseIt(host_component, this.config3);
+          comp.id = id;
           comp.service_id = serviceName;
           hostComponents.push(comp);
+          if (this.get('ADVANCED_COMPONENTS').contains(comp.component_name)) {
+            advancedHostComponents.push(comp);
+          }
         }, this);
       }, this);
 
-      this.computeAdditionalRelations(hostComponents, services);
+      this.computeAdditionalRelations(advancedHostComponents, services);
       //load master components to model
-      App.HostComponent.find().filterProperty('isMaster').forEach(function (hostComponent) {
-        if (hostComponent && !hostComponents.someProperty('id', hostComponent.get('id'))) {
-          this.deleteRecord(hostComponent);
+      previousMasterComponentIds.forEach(function (id) {
+        if (!hostComponentIdsMap[id]) {
+          var hostComponent = App.HostComponent.find(id);
+          if (hostComponent.get('isLoaded')) {
+            this.deleteRecord(hostComponent);
+          }
           var serviceCache = services.findProperty('ServiceInfo.service_name', hostComponent.get('service.serviceName'));
           if (serviceCache) {
             serviceCache.host_components = serviceCache.host_components.without(hostComponent.get('id'));
           }
         }
       }, this);
+      previousMasterComponentIds = hostComponents.mapProperty('id');
+
       App.store.loadMany(this.get('model3'), hostComponents);
 
       //parse service metrics from components
@@ -261,24 +281,6 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
 
       //load services to model
       App.store.loadMany(this.get('model'), result);
-      /*if (previousResponse.length !== result.length) {
-       App.store.loadMany(this.get('model'), result);
-       } else {
-       result.forEach(function (serviceJson) {
-       var fields = ['passive_state','work_status', 'rand', 'alerts', 'quick_links', 'host_components', 'tool_tip_content', 'critical_alerts_count'];
-       var service = this.get('model').find(serviceJson.id);
-       var modifiedData = this.getDiscrepancies(serviceJson, previousResponse.findProperty('id', serviceJson.id), fields);
-       if (modifiedData.isLoadNeeded) {
-       App.store.load(this.get('model'), serviceJson);
-       } else {
-       for (var property in modifiedData) {
-       service.set(stringUtils.underScoreToCamelCase(property), modifiedData[property]);
-       }
-       }
-       }, this)
-       }
-
-       previousResponse = result;*/
     }
     console.timeEnd('App.serviceMetricsMapper execution time');
   },
@@ -447,6 +449,7 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
         for (var host in liveNodesJson) {
           item.live_data_nodes.push('DATANODE' + '_' + host);
         }
+        item.name_node_id = "NAMENODE" + "_" + component.host_components[0].HostRoles.host_name;
       }
       if (component.ServiceComponentInfo && component.ServiceComponentInfo.component_name == "JOURNALNODE") {
         if (!item.journal_nodes) {
@@ -454,10 +457,15 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
         }
         if (component.host_components) {
           component.host_components.forEach(function (hc) {
-            item.journal_nodes.push(hc.HostRoles.host_name);
+            item.journal_nodes.push("JOURNALNODE" + "_" + hc.HostRoles.host_name);
           });
         }
       }
+      if (component.ServiceComponentInfo &&
+          component.ServiceComponentInfo.component_name == "SECONDARY_NAMENODE" &&
+          component.host_components.length > 0) {
+        item.sname_node_id = "SECONDARY_NAMENODE" + "_" + component.host_components[0].HostRoles.host_name;
+      }
     });
     // Map
     var finalJson = this.parseIt(item, finalConfig);
@@ -481,6 +489,9 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
           var activeRM = component.host_components.findProperty('HostRoles.ha_state', 'ACTIVE');
           var activeHostComponentIndex = component.host_components.indexOf(activeRM);
           self.setActiveAsFirstHostComponent(component, activeHostComponentIndex);
+          if (activeRM) {
+            item.active_resource_manager_id = "RESOURCEMANAGER" + "_" + activeRM.HostRoles.host_name;
+          }
         }
 
         if (component.host_components[0].metrics && component.host_components[0].metrics.yarn) {
@@ -491,6 +502,10 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
         }
         // extend config
         finalConfig = jQuery.extend(finalConfig, yarnConfig);
+        item.resource_manager_id = "RESOURCEMANAGER" + "_" + component.host_components[0].HostRoles.host_name;
+      }
+      if (component.ServiceComponentInfo && component.ServiceComponentInfo.component_name == "APP_TIMELINE_SERVER") {
+        item.app_timeline_server_id = "APP_TIMELINE_SERVER" + "_" + component.host_components[0].HostRoles.host_name;
       }
     });
     // Map
@@ -520,6 +535,7 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
       if (component.ServiceComponentInfo && component.ServiceComponentInfo.component_name == "HISTORYSERVER") {
         item.jobHistoryServerComponent = component;
         finalConfig = jQuery.extend(finalConfig, mapReduce2Config);
+        item.job_history_server_id = "HISTORYSERVER" + "_" + component.host_components[0].HostRoles.host_name;
       }
     });
     // Map
@@ -552,6 +568,7 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
             item.regions_in_transition = regionsArray == null ? 0 : regionsArray;
           }
         }
+        item.master_id = "HBASE_MASTER" + "_" + component.host_components[0].HostRoles.host_name;
       }
     });
     // Map

+ 4 - 4
ambari-web/app/mixins/common/table_server_view_mixin.js

@@ -41,12 +41,12 @@ App.TableServerViewMixin = Em.Mixin.create({
   totalCount: function () {
     return this.get('controller.totalCount');
   }.property('controller.totalCount'),
+
   /**
    * data requested from server
    */
-  content: function () {
-    return this.get('controller.content');
-  }.property('controller.content'),
+  contentBinding: 'controller.content',
+
   /**
    * content already filtered on server-side
    */
@@ -57,7 +57,7 @@ App.TableServerViewMixin = Em.Mixin.create({
    * sort and slice recieved content by pagination parameters
    */
   pageContent: function () {
-    var content = this.get('filteredContent');
+    var content = this.get('filteredContent').toArray();
     if (content.length > ((this.get('endIndex') - this.get('startIndex')) + 1)) {
       content = content.slice(0, (this.get('endIndex') - this.get('startIndex')) + 1);
     }

+ 21 - 30
ambari-web/app/models/host.js

@@ -25,6 +25,9 @@ App.Host = DS.Model.extend({
   publicHostName: DS.attr('string'),
   cluster: DS.belongsTo('App.Cluster'),
   hostComponents: DS.hasMany('App.HostComponent'),
+  notStartedComponents: DS.hasMany('App.HostComponent'),
+  componentsWithStaleConfigs: DS.hasMany('App.HostComponent'),
+  componentsInPassiveState: DS.hasMany('App.HostComponent'),
   cpu: DS.attr('string'),
   cpuPhysical: DS.attr('string'),
   memory: DS.attr('string'),
@@ -54,10 +57,6 @@ App.Host = DS.Model.extend({
    * Is host checked at the main Hosts page
    */
   selected:DS.attr('boolean'),
-  /**
-   * determine whether host is requested from server
-   */
-  isRequested: DS.attr('boolean'),
 
   currentVersion: function () {
     var current = this.get('stackVersions').findProperty('isCurrent');
@@ -87,27 +86,21 @@ App.Host = DS.Model.extend({
     return 0;
   }.property('memTotal', 'memFree'),
 
-  componentsWithStaleConfigs: function () {
-    return this.get('hostComponents').filterProperty('staleConfigs', true);
-  }.property('hostComponents.@each.staleConfigs'),
 
   /**
-   * Get count of host components with stale configs
-   * @returns {Number}
+   * @type {number}
    */
-  componentsWithStaleConfigsCount: function() {
-    return this.get('componentsWithStaleConfigs').length;
-  }.property('componentsWithStaleConfigs.length'),
+  componentsInPassiveStateCount: function() {
+    return this.get('componentsInPassiveState').length;
+  }.property('componentsInPassiveState.length'),
 
   /**
-   * Get count of host components in passive state
+   * Get count of host components with stale configs
    * @returns {Number}
    */
-  componentsInPassiveStateCount: function() {
-    return this.get('hostComponents').filter(function(component) {
-      return component.get('passiveState') !== 'OFF';
-    }).length;
-  }.property('hostComponents.@each.passiveState'),
+  componentsWithStaleConfigsCount: function() {
+    return this.get('componentsWithStaleConfigs.length');
+  }.property('componentsWithStaleConfigs.length'),
 
   /**
    * Count of mounted on host disks
@@ -250,30 +243,28 @@ App.Host = DS.Model.extend({
    * Contains affected host components names (based on <code>healthClass</code>)
    * @returns {String}
    */
-  healthToolTip: function(){
-    var hostComponents = this.get('hostComponents').filter(function(item){
-      return item.get('workStatus') !== App.HostComponentStatus.started;
-    });
+  healthToolTip: function () {
+    var hostComponents = this.get('notStartedComponents');
     var output = '';
     if (this.get('passiveState') != 'OFF') {
       return Em.I18n.t('hosts.host.passive.mode');
     }
-    switch (this.get('healthClass')){
+    switch (this.get('healthClass')) {
       case 'health-status-DEAD-RED':
-        hostComponents = hostComponents.filterProperty('isMaster', true);
+        hostComponents = hostComponents.filterProperty('isMaster');
         output = Em.I18n.t('hosts.host.healthStatus.mastersDown');
-        hostComponents.forEach(function(hc, index){
-          output += (index == (hostComponents.length-1)) ? hc.get('displayName') : (hc.get('displayName')+", ");
+        hostComponents.forEach(function (hc, index) {
+          output += (index == (hostComponents.length - 1)) ? hc.get('displayName') : (hc.get('displayName') + ", ");
         }, this);
         break;
       case 'health-status-DEAD-YELLOW':
         output = Em.I18n.t('hosts.host.healthStatus.heartBeatNotReceived');
         break;
       case 'health-status-DEAD-ORANGE':
-        hostComponents = hostComponents.filterProperty('isSlave', true);
+        hostComponents = hostComponents.filterProperty('isSlave');
         output = Em.I18n.t('hosts.host.healthStatus.slavesDown');
-        hostComponents.forEach(function(hc, index){
-          output += (index == (hostComponents.length-1)) ? hc.get('displayName') : (hc.get('displayName')+", ");
+        hostComponents.forEach(function (hc, index) {
+          output += (index == (hostComponents.length - 1)) ? hc.get('displayName') : (hc.get('displayName') + ", ");
         }, this);
         break;
       case 'health-status-LIVE':
@@ -281,7 +272,7 @@ App.Host = DS.Model.extend({
         break;
     }
     return output;
-  }.property('hostComponents.@each.workStatus','hostComponents.@each.passiveState')
+  }.property('notStartedComponents')
 });
 
 App.Host.FIXTURES = [];

+ 4 - 4
ambari-web/app/models/service.js

@@ -95,8 +95,9 @@ App.Service = DS.Model.extend({
    * actual_configs, then a restart is required.
    */
   isRestartRequired: function () {
-    var rhc = App.HostComponent.find().filterProperty('service.serviceName', this.get('serviceName')).filterProperty('staleConfigs', true);
+    var rhc = this.get('hostComponents').filterProperty('staleConfigs', true);
     var hc = {};
+
     rhc.forEach(function(_rhc) {
       var hostName = _rhc.get('hostName');
       if (!hc[hostName]) {
@@ -105,9 +106,8 @@ App.Service = DS.Model.extend({
       hc[hostName].push(_rhc.get('displayName'));
     });
     this.set('restartRequiredHostsAndComponents', hc);
-    return (rhc.length>0);
-
-  }.property('serviceName', 'hostComponents.@each.staleConfigs'),
+    return (rhc.length > 0);
+  }.property('serviceName'),
   
   /**
    * Contains a map of which hosts and host_components

+ 1 - 3
ambari-web/app/models/service/hbase.js

@@ -18,9 +18,7 @@
 var App = require('app');
 
 App.HBaseService = App.Service.extend({
-  master: function () {
-    return this.get('hostComponents').findProperty('componentName', 'HBASE_MASTER');
-  }.property('hostComponents'),
+  master: DS.belongsTo('App.HostComponent'),
   regionServersStarted: DS.attr('number'),
   regionServersInstalled: DS.attr('number'),
   regionServersTotal: DS.attr('number'),

+ 3 - 9
ambari-web/app/models/service/hdfs.js

@@ -19,12 +19,8 @@ var App = require('app');
 
 App.HDFSService = App.Service.extend({
   version: DS.attr('string'),
-  nameNode: function () {
-    return this.get('hostComponents').findProperty('componentName', 'NAMENODE');
-  }.property('hostComponents'),
-  snameNode: function () {
-    return this.get('hostComponents').findProperty('componentName', 'SECONDARY_NAMENODE');
-  }.property('hostComponents'),
+  nameNode: DS.belongsTo('App.HostComponent'),
+  snameNode: DS.belongsTo('App.HostComponent'),
   activeNameNode: DS.belongsTo('App.HostComponent'),
   standbyNameNode: DS.belongsTo('App.HostComponent'),
   standbyNameNode2: DS.belongsTo('App.HostComponent'),
@@ -37,9 +33,7 @@ App.HDFSService = App.Service.extend({
   nfsGatewaysStarted: DS.attr('number'),
   nfsGatewaysInstalled: DS.attr('number'),
   nfsGatewaysTotal: DS.attr('number'),
-  journalNodes: function () {
-    return this.get('hostComponents').filterProperty('componentName', 'JOURNALNODE');
-  }.property('hostComponents.@each'),
+  journalNodes: DS.hasMany('App.HostComponent'),
   nameNodeStartTime: DS.attr('number'),
   jvmMemoryHeapUsed: DS.attr('number'),
   jvmMemoryHeapMax: DS.attr('number'),

+ 1 - 3
ambari-web/app/models/service/mapreduce2.js

@@ -18,9 +18,7 @@
 var App = require('app');
 
 App.MapReduce2Service = App.Service.extend({
-  jobHistoryServer: function () {
-    return this.get('hostComponents').findProperty('componentName', 'HISTORYSERVER');
-  }.property('hostComponents'),
+  jobHistoryServer: DS.belongsTo('App.HostComponent'),
   mapReduce2Clients: DS.attr('number')
 });
 

+ 3 - 9
ambari-web/app/models/service/yarn.js

@@ -19,18 +19,12 @@ var App = require('app');
 var objectUtils = require('utils/object_utils');
 
 App.YARNService = App.Service.extend({
-  resourceManager: function() {
-    return this.get('hostComponents').findProperty('componentName', 'RESOURCEMANAGER');
-  }.property('hostComponents'),
+  resourceManager: DS.belongsTo('App.HostComponent'),
   isRMHaEnabled: function() {
     return this.get('hostComponents').filterProperty('componentName', 'RESOURCEMANAGER').length > 1;
   }.property('hostComponents'),
-  activeResourceManager: function() {
-    return this.get('hostComponents').filterProperty('componentName', 'RESOURCEMANAGER').findProperty('haStatus', 'ACTIVE');
-  }.property('hostComponents'),
-  appTimelineServer: function() {
-    return this.get('hostComponents').findProperty('componentName', 'APP_TIMELINE_SERVER');
-  }.property('hostComponents'),
+  activeResourceManager: DS.belongsTo('App.HostComponent'),
+  appTimelineServer: DS.belongsTo('App.HostComponent'),
   nodeManagersStarted: DS.attr('number'),
   nodeManagersInstalled: DS.attr('number'),
   nodeManagersTotal: DS.attr('number'),

+ 17 - 7
ambari-web/app/views/main/dashboard/widgets/flume_agent_live.js

@@ -44,14 +44,15 @@ App.FlumeAgentUpView = App.TextDashboardWidgetView.extend({
     return this.get('model.hostComponents').filterProperty('componentName', 'FLUME_HANDLER');
   }.property('model.hostComponents.length'),
 
-  flumeAgentsLive: function () {
-    return this.get('flumeAgentComponents').filterProperty("workStatus", "STARTED");
-  }.property('flumeAgentComponents.@each.workStatus'),
-
-  flumeAgentsDead: function () {
-    return this.get('flumeAgentComponents').filterProperty("workStatus", "INSTALLED");
-  }.property('flumeAgentComponents.@each.workStatus'),
+  /**
+   * @type {Array}
+   */
+  flumeAgentsLive: [],
 
+  /**
+   * @type {Array}
+   */
+  flumeAgentsDead: [],
 
   data: function () {
     if ( !this.get('flumeAgentComponents.length')) {
@@ -65,6 +66,15 @@ App.FlumeAgentUpView = App.TextDashboardWidgetView.extend({
     return this.get('flumeAgentsLive').length + "/" + this.get('flumeAgentComponents').length;
   }.property('flumeAgentComponents.length', 'flumeAgentsLive'),
 
+  statusObserver: function() {
+    Em.run.once(this, 'filterStatusOnce');
+  }.observes('flumeAgentComponents.@each.workStatus'),
+
+  filterStatusOnce: function() {
+    this.set('flumeAgentsLive', this.get('flumeAgentComponents').filterProperty("workStatus", "STARTED"));
+    this.set('flumeAgentsDead', this.get('flumeAgentComponents').filterProperty("workStatus", "INSTALLED"));
+  },
+
   editWidget: function (event) {
     var parent = this;
     var max_tmp =  parseFloat(parent.get('maxValue'));

+ 6 - 9
ambari-web/app/views/main/host.js

@@ -388,7 +388,6 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
         id:host.id,
         clusterId: host.cluster_id,
         passiveState: host.passive_state,
-        isRequested: host.is_requested,
         hostName: host.host_name,
         hostComponents: host.host_components
       })
@@ -569,29 +568,27 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
      * @returns {String}
      */
     restartRequiredComponentsMessage: function() {
-      var restartRequiredComponents = this.get('content.hostComponents').filterProperty('staleConfigs', true);
-      var count = restartRequiredComponents.length;
+      var restartRequiredComponents = this.get('content.componentsWithStaleConfigs');
+      var count = this.get('content.componentsWithStaleConfigsCount');
       if (count <= 5) {
         var word = (count == 1) ? Em.I18n.t('common.component') : Em.I18n.t('common.components');
         return Em.I18n.t('hosts.table.restartComponents.withNames').format(restartRequiredComponents.getEach('displayName').join(', ')) + ' ' + word.toLowerCase();
       }
       return Em.I18n.t('hosts.table.restartComponents.withoutNames').format(count);
-    }.property('content.hostComponents.@each.staleConfigs'),
+    }.property('content.componentsWithStaleConfigs'),
 
     /**
      * Tooltip message for "Maintenance" icon
      * @returns {String}
      */
     componentsInPassiveStateMessage: function() {
-      var componentsInPassiveState = this.get('content.hostComponents').filter(function(component) {
-        return component.get('passiveState') !== 'OFF';
-      });
-      var count = componentsInPassiveState.length;
+      var componentsInPassiveState = this.get('content.componentsInPassiveState');
+      var count = this.get('content.componentsInPassiveStateCount');
       if (count <= 5) {
         return Em.I18n.t('hosts.table.componentsInPassiveState.withNames').format(componentsInPassiveState.getEach('displayName').join(', '));
       }
       return Em.I18n.t('hosts.table.componentsInPassiveState.withoutNames').format(count);
-    }.property('content.hostComponents.@each.passiveState'),
+    }.property('content.componentsInPassiveState'),
 
     /**
      * true if host has only one repoversion

+ 1 - 5
ambari-web/test/mappers/server_data_mapper_test.js

@@ -73,8 +73,7 @@ describe('App.QuickDataMapper', function () {
       f4_type: 'array',
       f4: {
         item: 'c2'
-      },
-      f5: 'item.["key.dotted"]'
+      }
     };
     var mapper = App.QuickDataMapper.create();
     var result = mapper.parseIt(test_json, config);
@@ -93,9 +92,6 @@ describe('App.QuickDataMapper', function () {
     it('Generate array of json fields', function() {
       expect(result.f4).to.eql(['val1','val4','val5']);
     });
-    it('Check value with dotted key', function() {
-      expect(result.f5).to.eql('val6');
-    });
   });
 
 });

+ 12 - 0
ambari-web/test/mappers/service_mapper_test.js

@@ -50,6 +50,9 @@ describe('App.serviceMetricsMapper', function () {
                         ritCount: 0
                       }
                     }
+                  },
+                  HostRoles: {
+                    host_name: 'host1'
                   }
                 }
               ]
@@ -78,6 +81,9 @@ describe('App.serviceMetricsMapper', function () {
                         ritCount: 0
                       }
                     }
+                  },
+                  HostRoles: {
+                    host_name: 'host1'
                   }
                 }
               ]
@@ -106,6 +112,9 @@ describe('App.serviceMetricsMapper', function () {
                         ritCount: 0
                       }
                     }
+                  },
+                  HostRoles: {
+                    host_name: 'host1'
                   }
                 }
               ]
@@ -134,6 +143,9 @@ describe('App.serviceMetricsMapper', function () {
                         ritCount: 0
                       }
                     }
+                  },
+                  HostRoles: {
+                    host_name: 'host1'
                   }
                 }
               ]

+ 2 - 2
ambari-web/test/models/host_test.js

@@ -216,7 +216,7 @@ describe('App.Host', function () {
     });
   });
 
-  describe('#componentsWithStaleConfigs', function () {
+  describe.skip('#componentsWithStaleConfigs', function () {
     it('One component with stale configs', function () {
       host1.set('hostComponents', [Em.Object.create({
         staleConfigs: true
@@ -235,7 +235,7 @@ describe('App.Host', function () {
     });
   });
 
-  describe('#componentsInPassiveStateCount', function () {
+  describe.skip('#componentsInPassiveStateCount', function () {
     it('No component in passive state', function () {
       host1.set('hostComponents', [Em.Object.create({
         passiveState: 'OFF'

+ 22 - 34
ambari-web/test/models/service/hdfs_test.js

@@ -21,44 +21,32 @@ var App = require('app');
 var modelSetup = require('test/init_model_test');
 require('models/service/hdfs');
 
-var hdfsService,
-  hdfsServiceData = {
-    id: 'hdfs'
-  },
-  hostComponentsData = [
-    {
-      id: 'journalnode',
-      componentName: 'JOURNALNODE'
-    }
-  ],
-  cases = [
-    {
-      propertyName: 'journalNodes',
-      componentId: 'journalnode'
-    }
-  ];
-
 describe('App.HDFSService', function () {
 
-  beforeEach(function () {
-    hdfsService = App.HDFSService.createRecord(hdfsServiceData);
-  });
-
-  afterEach(function () {
-    modelSetup.deleteRecord(hdfsService);
-  });
-
-  cases.forEach(function (item) {
-    var propertyName = item.propertyName;
-    describe('#' + propertyName, function () {
-      it('should take one component from hostComponents', function () {
-        hdfsService.reopen({
-          hostComponents: hostComponentsData
+    describe('#isNnHaEnabled', function () {
+      var record = App.HDFSService.createRecord({
+        id: 'hdfs'
+      });
+      it('ha disabled', function () {
+        record.reopen({
+          hostComponents: [Em.Object.create({componentName: 'NAMENODE'})],
+          snameNode: true
         });
-        expect(hdfsService.get(propertyName)).to.have.length(1);
-        expect(hdfsService.get(propertyName)[0].id).to.equal(item.componentId);
+        record.propertyDidChange('isNnHaEnabled');
+        expect(record.get('isNnHaEnabled')).to.be.false;
+      });
+      it('ha enabled', function () {
+        record.setProperties({
+          hostComponents: [
+            Em.Object.create({componentName: 'NAMENODE'}),
+            Em.Object.create({componentName: 'NAMENODE'})
+          ],
+          snameNode: null
+        });
+        record.propertyDidChange('isNnHaEnabled');
+        expect(record.get('isNnHaEnabled')).to.be.true;
       });
     });
-  });
+
 
 });

+ 5 - 11
ambari-web/test/models/service_test.js

@@ -218,28 +218,22 @@ describe('App.Service', function () {
   });
 
   describe('#isRestartRequired', function () {
-    var mockHostComponentModel = function (mock) {
-      sinon.stub(App.HostComponent, 'find', function () {
-        return mock;
-      });
-    }
+
     beforeEach(function () {
       service.reopen({
-        serviceName: 'HDFS'
+        serviceName: 'HDFS',
+        hostComponents: []
       });
     });
-    afterEach(function () {
-      App.HostComponent.find.restore();
-    });
     hostComponentsDataFalse.forEach(function (item) {
       it('should be false', function () {
-        mockHostComponentModel(item);
+        service.set('hostComponents', item);
         expect(service.get('isRestartRequired')).to.be.false;
       });
     });
     hostComponentsDataTrue.forEach(function (item) {
       it('should be true', function () {
-        mockHostComponentModel(item);
+        service.set('hostComponents', item);
         expect(service.get('isRestartRequired')).to.be.true;
       });
     });