blob: 0c3f4cd9b5672615fc4e426c5f48be166b624400 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var App = require('app');
var misc = require('utils/misc');
DS.attr.transforms.object = {
from: function(serialized) {
return Ember.none(serialized) ? null : Object(serialized);
},
to: function(deserialized) {
return Ember.none(deserialized) ? null : Object(deserialized);
}
};
App.Host = DS.Model.extend({
hostName: DS.attr('string'),
publicHostName: DS.attr('string'),
cluster: DS.belongsTo('App.Cluster'),
hostComponents: DS.hasMany('App.HostComponent'),
cpu: DS.attr('string'),
memory: DS.attr('string'),
diskTotal: DS.attr('number'),
diskFree: DS.attr('number'),
osArch: DS.attr('string'),
ip: DS.attr('string'),
rack: DS.attr('string'),
healthStatus: DS.attr('string'),
cpuUsage: DS.attr('number'),
memoryUsage: DS.attr('number'),
lastHeartBeatTime: DS.attr('number'),
osType: DS.attr("string"),
diskInfo: DS.attr('object'),
loadOne:DS.attr('number'),
loadFive:DS.attr('number'),
loadFifteen:DS.attr('number'),
criticalAlertsCount: function () {
return App.router.get('clusterController.alerts').filterProperty('hostName', this.get('hostName')).filterProperty('isOk', false).filterProperty('ignoredForHosts', false).length;
}.property('App.router.clusterController.alerts.length'),
publicHostNameFormatted: function() {
return this.get('publicHostName').substr(0, 20) + ' ...';
}.property('publicHostName'),
/**
* API return diskTotal and diskFree. Need to save their different
*/
diskUsed: function(){
return this.get('diskTotal') - this.get('diskFree');
}.property('diskFree', 'diskTotal'),
/**
* Format diskUsed value to float with 2 digits (also convert to GB)
*/
diskUsedFormatted: function() {
return Math.round(this.get('diskUsed') * Math.pow(10, 2)) / Math.pow(10, 2) + 'GB';
}.property('diskUsed'),
/**
* Format diskTotal value to float with 2 digits (also convert to GB)
*/
diskTotalFormatted: function() {
return Math.round(this.get('diskTotal') * Math.pow(10, 2)) / Math.pow(10, 2) + 'GB';
}.property('diskTotal'),
/**
* Percent value of used disk space
*/
diskUsage: function() {
return (this.get('diskUsed')) / this.get('diskTotal') * 100;
}.property('diskUsed', 'diskTotal'),
/**
* Format diskUsage to float with 2 digits
*/
diskUsageFormatted: function() {
if (isNaN(this.get('diskUsage')) || this.get('diskUsage') < 0) {
return 'Data Unavailable';
}
var s = Math.round(this.get('diskUsage') * Math.pow(10, 2)) / Math.pow(10, 2);
if (isNaN(s)) {
s = 0;
}
return s + '%';
}.property('diskUsage'),
diskInfoBar: function() {
if (isNaN(this.get('diskUsage')) || this.get('diskUsage') < 0) {
return this.get('diskUsageFormatted');
}
return this.get('diskUsedFormatted') + '/' + this.get('diskTotalFormatted') + ' (' + this.get('diskUsageFormatted') + ' used)';
}.property('diskUsedFormatted', 'diskTotalFormatted'),
/**
* formatted bytes to appropriate value
*/
memoryFormatted: function () {
return misc.formatBandwidth(this.get('memory') * 1000);
}.property('memory'),
/**
* Return true if host not heartbeating last 180 seconds
*/
isNotHeartBeating : function(){
return ((new Date()).getTime() - this.get('lastHeartBeatTime')) > 180 * 1000;
}.property('lastHeartBeatTime'),
loadAvg: function() {
if (this.get('loadOne') != null) return this.get('loadOne').toFixed(2);
if (this.get('loadFive') != null) return this.get('loadFive').toFixed(2);
if (this.get('loadFifteen') != null) return this.get('loadFifteen').toFixed(2);
}.property('loadOne', 'loadFive', 'loadFifteen'),
// Instead of making healthStatus a computed property that listens on hostComponents.@each.workStatus,
// we are creating a separate observer _updateHealthStatus. This is so that healthStatus is updated
// only once after the run loop. This is because Ember invokes the computed property every time
// a property that it depends on changes. For example, App.statusMapper's map function would invoke
// the computed property too many times and freezes the UI without this hack.
// See http://stackoverflow.com/questions/12467345/ember-js-collapsing-deferring-expensive-observers-or-computed-properties
healthClass: '',
_updateHealthClass: function(){
Ember.run.once(this, 'updateHealthClass');
}.observes('healthStatus', 'hostComponents.@each.workStatus'),
updateHealthClass: function(){
var healthStatus = this.get('healthStatus');
/**
* Do nothing until load
*/
if (!this.get('isLoaded') || this.get('isSaving')) {
} else {
var status;
var masterComponents = this.get('hostComponents').filterProperty('isMaster');
var masterComponentsRunning = masterComponents.everyProperty('workStatus', App.HostComponentStatus.started);
var slaveComponents = this.get('hostComponents').filterProperty('isSlave');
var slaveComponentsRunning = slaveComponents.everyProperty('workStatus', App.HostComponentStatus.started);
if (this.get('isNotHeartBeating')) {
status = 'DEAD-YELLOW';
} else if (masterComponentsRunning && slaveComponentsRunning) {
status = 'LIVE';
} else if (masterComponents.length > 0 && !masterComponentsRunning) {
status = 'DEAD';
} else {
status = 'DEAD-ORANGE';
}
if (status) {
healthStatus = status;
}
}
this.set('healthClass', 'health-status-' + healthStatus);
},
healthToolTip: function(){
var hostComponents = this.get('hostComponents').filter(function(item){
if(item.get('workStatus') !== App.HostComponentStatus.started){
return true;
}
});
var output = '';
switch (this.get('healthClass')){
case 'health-status-DEAD':
hostComponents = hostComponents.filterProperty('isMaster', true);
output = Em.I18n.t('hosts.host.healthStatus.mastersDown');
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);
output = Em.I18n.t('hosts.host.healthStatus.slavesDown');
hostComponents.forEach(function(hc, index){
output += (index == (hostComponents.length-1)) ? hc.get('displayName') : (hc.get('displayName')+", ");
}, this);
break;
}
return output;
}.property('healthClass')
});
App.Host.FIXTURES = [];