mirror of https://github.com/rancher/ui.git
250 lines
6.9 KiB
JavaScript
250 lines
6.9 KiB
JavaScript
import { and } from '@ember/object/computed';
|
|
import EmberObject, { computed, observer } from '@ember/object';
|
|
import Evented from '@ember/object/evented';
|
|
import { run } from '@ember/runloop';
|
|
import Socket from 'shared/utils/socket';
|
|
import C from 'shared/utils/constants';
|
|
|
|
/*
|
|
Usage:
|
|
|
|
import MultiStatsSocket from "./multi-stats"
|
|
var sock = StatsSocket.create({resource: a_resource_with_stats, linkName: 'containerStats or hostStats'});
|
|
sock.on('dataPoint', function(point) {
|
|
// Do something with data
|
|
});
|
|
|
|
// When done
|
|
sock.close();
|
|
*/
|
|
export default EmberObject.extend(Evented, {
|
|
resource: null,
|
|
linkName: 'containerStats',
|
|
|
|
connected: false,
|
|
prev: null,
|
|
closed: false,
|
|
|
|
init() {
|
|
this._super();
|
|
this.onAvailableChanged();
|
|
},
|
|
|
|
available: computed('resource.{state,healthState}', function() {
|
|
return C.ACTIVEISH_STATES.indexOf(this.get('resource.state')) >= 0 && this.get('resource.healthState') !== 'started-once';
|
|
}),
|
|
|
|
active: and('available', 'connected'),
|
|
|
|
loading: computed('available', 'connected', function() {
|
|
return this.get('available') && !this.get('connected');
|
|
}),
|
|
|
|
onAvailableChanged: observer('available', function() {
|
|
if ( this.get('available') ) {
|
|
this.connect();
|
|
} else {
|
|
this.disconnect();
|
|
}
|
|
}),
|
|
|
|
connect() {
|
|
if ( this.get('socket') || this.get('closed') ) {
|
|
return;
|
|
}
|
|
|
|
this.set('prev', {});
|
|
if ( this.get('resource').hasLink(this.get('linkName')) ) {
|
|
this.get('resource').followLink(this.get('linkName')).then((response) => {
|
|
let token = response.get('authToken') || response.get('token');
|
|
|
|
if ( response.get('url') && token ) {
|
|
// Send the fixed-size auth token in query string if present
|
|
var url = `${ response.get('url') }?token=${ encodeURIComponent(token) }`;
|
|
|
|
var socket = Socket.create({
|
|
url,
|
|
autoReconnect: false
|
|
});
|
|
|
|
socket.on('message', (event) => {
|
|
if ( this.get('connected') ) {
|
|
// console.log('message', event);
|
|
JSON.parse(event.data).forEach((row) => {
|
|
// console.log('row', row);
|
|
this.process(row);
|
|
});
|
|
}
|
|
});
|
|
|
|
socket.on('connected', (/* tries, after*/) => {
|
|
// Send the big token
|
|
if ( response.get('token') && response.get('authToken') ) {
|
|
socket.send(response.get('token'));
|
|
}
|
|
|
|
this.set('connected', true);
|
|
this.trigger('connected');
|
|
});
|
|
|
|
socket.on('disconnected', (/* tries*/) => {
|
|
this.set('connected', false);
|
|
this.trigger('disconnected');
|
|
this.set('socket', null);
|
|
run.next(this, 'connect');
|
|
});
|
|
|
|
this.set('socket', socket);
|
|
socket.connect();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
disconnect() {
|
|
this.set('connected', false);
|
|
|
|
var socket = this.get('socket');
|
|
|
|
if ( socket ) {
|
|
socket.disconnect();
|
|
this.set('socket', null);
|
|
}
|
|
},
|
|
|
|
close() {
|
|
this.set('closed', true);
|
|
this.disconnect();
|
|
},
|
|
|
|
process(data) {
|
|
// console.log('process', data.id, data);
|
|
var key = `${ data.resourceType }/${ data.id }`;
|
|
var prev = this.get('prev')[key];
|
|
|
|
var ts = roundTsFromString(data.timestamp);
|
|
|
|
var out = {
|
|
key,
|
|
id: data.id,
|
|
resourceType: data.resourceType,
|
|
ts,
|
|
};
|
|
|
|
if ( prev ) {
|
|
// Don't use `ts` here, need the unrounded time to get accurate CPU usage
|
|
var time_diff_ms = (tsFromString(data.timestamp) - tsFromString(prev.timestamp));
|
|
|
|
// Duplicate data point, ignore
|
|
if ( time_diff_ms === 0 ) {
|
|
return;
|
|
}
|
|
|
|
var time_diff_ns = time_diff_ms * 1e6;
|
|
var time_diff_s = time_diff_ms / 1000;
|
|
|
|
out.time_diff_ms = time_diff_ms;
|
|
|
|
// CPU
|
|
var count = 1;
|
|
|
|
if ( data.cpu.usage.per_cpu_usage ) {
|
|
count = data.cpu.usage.per_cpu_usage.length;
|
|
}
|
|
|
|
out.cpu_user = toPercent((data.cpu.usage.user - prev.cpu.usage.user ) / time_diff_ns, count * 100);
|
|
out.cpu_system = toPercent((data.cpu.usage.system - prev.cpu.usage.system ) / time_diff_ns, count * 100);
|
|
out.cpu_total = toPercent((data.cpu.usage.total - prev.cpu.usage.total ) / time_diff_ns, count * 100);
|
|
out.cpu_count = count;
|
|
|
|
var read = 0;
|
|
var write = 0;
|
|
|
|
if ( data.diskio && data.diskio.io_service_bytes && prev.diskio && prev.diskio.io_service_bytes) {
|
|
// Minus the last point
|
|
prev.diskio.io_service_bytes.forEach((io) => {
|
|
if ( io && io.stats ) {
|
|
read -= io.stats.Read || 0;
|
|
write -= io.stats.Write || 0;
|
|
}
|
|
});
|
|
|
|
// Plus the current point
|
|
data.diskio.io_service_bytes.forEach((io) => {
|
|
if ( io && io.stats ) {
|
|
read += io.stats.Read || 0;
|
|
write += io.stats.Write || 0;
|
|
}
|
|
});
|
|
}
|
|
out.disk_read_kb = Math.max(0, read / (time_diff_s * 1024));
|
|
out.disk_write_kb = Math.max(0, write / (time_diff_s * 1024));
|
|
|
|
// Network
|
|
if ( data.network ) {
|
|
if ( data.network.interfaces && prev.network.interfaces ) {
|
|
data.network.interfaces.forEach((iface) => {
|
|
var prev_iface = prev.network.interfaces.filterBy('name', iface.name)[0];
|
|
|
|
if ( prev_iface ) {
|
|
out.net_rx_kb = (out.net_rx_kb || 0) + Math.max(0, (iface.rx_bytes - prev_iface.rx_bytes) / (time_diff_s * 1024));
|
|
out.net_tx_kb = (out.net_tx_kb || 0) + Math.max(0, (iface.tx_bytes - prev_iface.tx_bytes) / (time_diff_s * 1024));
|
|
}
|
|
});
|
|
} else if ( data.network && prev.network ) {
|
|
out.net_rx_kb = Math.max(0, (data.network.rx_bytes - prev.network.rx_bytes) / (time_diff_s * 1024));
|
|
out.net_tx_kb = Math.max(0, (data.network.tx_bytes - prev.network.tx_bytes) / (time_diff_s * 1024));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Memory
|
|
if ( data.memory ) {
|
|
if ( data.memory.usage ) {
|
|
out.mem_used_mb = Math.round(data.memory.usage / 1048576);
|
|
} else {
|
|
out.mem_used_mb = 0;
|
|
}
|
|
|
|
if ( data.memory.limit ) {
|
|
out.mem_total_mb = Math.round(data.memory.limit / 1048576);
|
|
}
|
|
}
|
|
|
|
// Convert any NaNs to 0 in case time_diff is 0
|
|
Object.keys(out).forEach((key) => {
|
|
if ( typeof out[key] === 'number' && isNaN(out[key]) ) {
|
|
out[key] = 0;
|
|
}
|
|
});
|
|
|
|
this.get('prev')[key] = data;
|
|
this.trigger('dataPoint', out);
|
|
},
|
|
});
|
|
|
|
function tsFromString(str) {
|
|
return new Date(str).getTime();
|
|
}
|
|
|
|
function roundTsFromString(str) {
|
|
var ts = tsFromString(str);
|
|
var ms = ts % 1000;
|
|
|
|
if ( ms >= 500 ) {
|
|
return ts - ms + 1000;
|
|
} else {
|
|
return ts - ms;
|
|
}
|
|
}
|
|
|
|
function toPercent(decimal, max = 100) {
|
|
var percent = Math.max(0, Math.round(decimal * 10000) / 100);
|
|
|
|
if ( max ) {
|
|
return Math.min(percent, max);
|
|
} else {
|
|
return percent;
|
|
}
|
|
}
|