Stats improvements

This commit is contained in:
Vincent Fiduccia 2015-09-14 17:13:57 -07:00
parent c3182bfdad
commit c5c1ae27dd
9 changed files with 135 additions and 83 deletions

View File

@ -4,6 +4,8 @@ import { formatPercent, formatMib, formatKbps } from 'ui/utils/util';
const MAX_POINTS = 60; const MAX_POINTS = 60;
const TICK_COUNT = 6; const TICK_COUNT = 6;
const COLORS = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"];
const ALT_COLORS = ["#ff7f0e", "#1f77b4", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"];
export default Ember.Component.extend({ export default Ember.Component.extend({
model: null, model: null,
@ -119,10 +121,10 @@ export default Ember.Component.extend({
{ {
if ( this.get('single') ) if ( this.get('single') )
{ {
row = getOrCreateDataRow(graph, data, 'User');
row.push(point.cpu_user);
row = getOrCreateDataRow(graph, data, 'System'); row = getOrCreateDataRow(graph, data, 'System');
row.push(point.cpu_system); row.push(point.cpu_system);
row = getOrCreateDataRow(graph, data, 'User');
row.push(point.cpu_user);
} }
else else
{ {
@ -166,10 +168,10 @@ export default Ember.Component.extend({
{ {
if ( this.get('single') ) if ( this.get('single') )
{ {
row = getOrCreateDataRow(graph, data, 'Receive');
row.push(point.net_rx_kb);
row = getOrCreateDataRow(graph, data, 'Transmit'); row = getOrCreateDataRow(graph, data, 'Transmit');
row.push(point.net_tx_kb); row.push(point.net_tx_kb);
row = getOrCreateDataRow(graph, data, 'Receive');
row.push(point.net_rx_kb);
} }
else else
{ {
@ -185,10 +187,10 @@ export default Ember.Component.extend({
{ {
if ( this.get('single') ) if ( this.get('single') )
{ {
row = getOrCreateDataRow(graph, data, 'Read');
row.push(point.disk_read_kb);
row = getOrCreateDataRow(graph, data, 'Write'); row = getOrCreateDataRow(graph, data, 'Write');
row.push(point.disk_write_kb); row.push(point.disk_write_kb);
row = getOrCreateDataRow(graph, data, 'Read');
row.push(point.disk_read_kb);
} }
else else
{ {
@ -222,11 +224,13 @@ export default Ember.Component.extend({
size: { size: {
height: 110, height: 110,
}, },
color: { pattern: ALT_COLORS.slice() },
data: { data: {
type: 'area-step', type: 'area-step',
x: 'x', x: 'x',
columns: this.get('cpuData'), columns: this.get('cpuData'),
groups: [[]], // Stacked graph, populated by getOrCreateDataRow... groups: [[]], // Stacked graph, populated by getOrCreateDataRow...
order: null,
}, },
transition: { duration: 0 }, transition: { duration: 0 },
legend: { show: false }, legend: { show: false },
@ -280,6 +284,7 @@ export default Ember.Component.extend({
size: { size: {
height: 110, height: 110,
}, },
color: { pattern: COLORS.slice() },
data: { data: {
type: 'area-step', type: 'area-step',
x: 'x', x: 'x',
@ -337,11 +342,13 @@ export default Ember.Component.extend({
size: { size: {
height: 110, height: 110,
}, },
color: { pattern: ALT_COLORS.slice() },
data: { data: {
type: 'area-step', type: 'area-step',
x: 'x', x: 'x',
columns: this.get('storageData'), columns: this.get('storageData'),
groups: [[]], // Stacked graph, populated by getOrCreateDataRow... groups: [[]], // Stacked graph, populated by getOrCreateDataRow...
order: null,
}, },
transition: { duration: 0 }, transition: { duration: 0 },
legend: { show: false }, legend: { show: false },
@ -393,11 +400,13 @@ export default Ember.Component.extend({
size: { size: {
height: 110, height: 110,
}, },
color: { pattern: ALT_COLORS.slice() },
data: { data: {
type: 'area-step', type: 'area-step',
x: 'x', x: 'x',
columns: this.get('networkData'), columns: this.get('networkData'),
groups: [[]], // Stacked graph, populated by getOrCreateDataRow... groups: [[]], // Stacked graph, populated by getOrCreateDataRow...
order: null,
}, },
transition: { duration: 0 }, transition: { duration: 0 },
legend: { show: false }, legend: { show: false },

View File

@ -95,7 +95,7 @@ export default Ember.Component.extend({
update: function() { update: function() {
var svg = this.get('svg'); var svg = this.get('svg');
var data = this.get('data').slice(); var data = (this.get('data')||[]).slice();
var x = this.get('x'); var x = this.get('x');
var y = this.get('y'); var y = this.get('y');
var line = this.get('line'); var line = this.get('line');

View File

@ -3,6 +3,8 @@ import Sortable from 'ui/mixins/sortable';
import ContainerSparkStats from 'ui/mixins/container-spark-stats'; import ContainerSparkStats from 'ui/mixins/container-spark-stats';
export default Ember.Controller.extend(Sortable, ContainerSparkStats, { export default Ember.Controller.extend(Sortable, ContainerSparkStats, {
statsSocket: null,
sortableContent: Ember.computed.alias('model.instances'), sortableContent: Ember.computed.alias('model.instances'),
sortBy: 'name', sortBy: 'name',
sorts: { sorts: {

View File

@ -1,4 +1,6 @@
import Ember from 'ember';
import Resource from 'ember-api-store/models/resource'; import Resource from 'ember-api-store/models/resource';
import { formatMib } from 'ui/utils/util';
var Host = Resource.extend({ var Host = Resource.extend({
type: 'host', type: 'host',
@ -53,12 +55,13 @@ var Host = Resource.extend({
}.property('physicalHostId'), }.property('physicalHostId'),
osBlurb: function() { osBlurb: function() {
// @TODO this always sends back Ubuntu if ( this.get('info.osInfo.operatingSystem') )
if ( false && this.get('info.osInfo') )
{ {
return this.get('info.osInfo.distribution') + ' ' + this.get('info.osInfo.version'); return this.get('info.osInfo.operatingSystem').replace(/\s+\(.*?\)/,'');
} }
}.property('info.osInfo.{distribution,version}'), }.property('info.osInfo.operatingSystem'),
osDetail: Ember.computed.alias('info.osInfo.operatingSystem'),
dockerBlurb: function() { dockerBlurb: function() {
// @TODO this always sends back Ubuntu // @TODO this always sends back Ubuntu
@ -84,28 +87,53 @@ var Host = Resource.extend({
} }
}.property('info.cpuInfo.{count,mhz}'), }.property('info.cpuInfo.{count,mhz}'),
cpuTooltip: Ember.computed.alias('info.cpuInfo.modelName'),
memoryBlurb: function() { memoryBlurb: function() {
if ( this.get('info.memoryInfo') ) if ( this.get('info.memoryInfo') )
{ {
var gb = Math.round((this.get('info.memoryInfo.memTotal')/1024)*100)/100; return formatMib(this.get('info.memoryInfo.memTotal'));
return gb + ' GiB';
} }
}.property('info.memoryInfo.memTotal'), }.property('info.memoryInfo.memTotal'),
diskBlurb: function() { diskBlurb: function() {
if ( this.get('info.diskInfo.mountPoints') ) var totalMb = 0;
// New hotness
if ( this.get('info.diskInfo.fileSystems') )
{ {
var totalMb = 0; var fses = this.get('info.diskInfo.fileSystems')||[];
Object.keys(fses).forEach((fs) => {
totalMb += fses[fs].capacity;
});
return formatMib(totalMb);
}
else if ( this.get('info.diskInfo.mountPoints') )
{
// Old & busted
var mounts = this.get('info.diskInfo.mountPoints')||[]; var mounts = this.get('info.diskInfo.mountPoints')||[];
Object.keys(mounts).forEach((mountPoint) => { Object.keys(mounts).forEach((mountPoint) => {
totalMb += mounts[mountPoint].total; totalMb += mounts[mountPoint].total;
}); });
var gb = Math.round((totalMb/1024)*10)/10; return formatMib(totalMb);
return gb + ' GiB';
} }
}.property('info.diskInfo.mountPoints.@each.total'), }.property('info.diskInfo.mountPoints.@each.total','info.diskInfo.fileSystems.@each.capacity'),
diskDetail: function() {
// New hotness
if ( this.get('info.diskInfo.fileSystems') )
{
var out = [];
var fses = this.get('info.diskInfo.fileSystems')||[];
Object.keys(fses).forEach((fs) => {
out.pushObject(Ember.Object.create({label: fs, value: formatMib(fses[fs].capacity)}));
});
return out;
}
}.property('info.diskInfo.fileSystems.@each.capacity'),
}); });
Host.reopenClass({ Host.reopenClass({

View File

@ -39,7 +39,7 @@
{{#if model.cpuBlurb}} {{#if model.cpuBlurb}}
<li> <li>
<label>CPU</label> <label>CPU</label>
{{model.cpuBlurb}} {{model.cpuBlurb}}{{#if model.cpuTooltip}} <i class="icon icon-info" tooltip="{{model.cpuTooltip}}"></i>{{/if}}
</li> </li>
{{/if}} {{/if}}
@ -53,14 +53,20 @@
{{#if model.diskBlurb}} {{#if model.diskBlurb}}
<li> <li>
<label>Storage</label> <label>Storage</label>
{{model.diskBlurb}} {{#if model.diskDetail}}
{{#each model.diskDetail as |disk|}}
<span style="display: inline-block; padding-right: 10px;">{{disk.value}} <i class="icon icon-info" tooltip="{{disk.label}}"></i></span>
{{/each}}
{{else}}
{{model.diskBlurb}} (total)
{{/if}}
</li> </li>
{{/if}} {{/if}}
{{#if model.osBlurb}} {{#if model.osDetail}}
<li> <li>
<label>OS</label> <label>OS</label>
{{model.osBlurb}} {{model.osDetail}}
</li> </li>
{{/if}} {{/if}}

View File

@ -1,17 +1,8 @@
import Ember from 'ember'; import Ember from 'ember';
const MAX_POINTS = 60; const MAX_POINTS = 60;
const keys = ['cpu','memory','network','storage'];
export default Ember.Mixin.create({ export default Ember.Mixin.create({
init() {
this._super();
keys.forEach((key) => {
this.set(key+'Data', Ember.Object.create());
this.set(key+'Max', 0);
});
},
cpuData: null, cpuData: null,
memoryData: null, memoryData: null,
networkData: null, networkData: null,
@ -85,13 +76,20 @@ export default Ember.Mixin.create({
getOrCreateDataRow(key, id) { getOrCreateDataRow(key, id) {
var data = this.get(key+'Data'); var data = this.get(key+'Data');
var row = data[id]; if ( !data )
{
data = Ember.Object.create();
this.set(key+'Max', 0);
this.set(key+'Data', data);
}
var row = data.get(id);
if ( !row ) if ( !row )
{ {
row = []; row = [];
for ( var i = 0 ; i < MAX_POINTS ; i++ ) for ( var i = 0 ; i < MAX_POINTS ; i++ )
{ {
row.push(0); row.pushObject(0);
} }
data.set(id,row); data.set(id,row);
} }

View File

@ -117,7 +117,9 @@ export default Ember.Object.extend(Ember.Evented, {
if ( prev ) if ( prev )
{ {
// Don't use `ts` here, need the unrounded time to get accurate CPU usage // Don't use `ts` here, need the unrounded time to get accurate CPU usage
var time_diff_ns = (tsFromString(data.timestamp) - tsFromString(prev.timestamp))*1e6; var time_diff_ms = (tsFromString(data.timestamp) - tsFromString(prev.timestamp));
var time_diff_ns = time_diff_ms*1e6;
var time_diff_s = time_diff_ms/1000;
var count = 1; var count = 1;
// CPU // CPU
@ -135,6 +137,37 @@ export default Ember.Object.extend(Ember.Evented, {
out.cpu_total = toPercent((data.cpu.usage.total - prev.cpu.usage.total )/time_diff_ns); out.cpu_total = toPercent((data.cpu.usage.total - prev.cpu.usage.total )/time_diff_ns);
out.cpu_count = count; out.cpu_count = count;
} }
if ( data.diskio && data.diskio.io_service_bytes )
{
var read = 0;
var write = 0;
data.diskio.io_service_bytes.forEach((io) => {
if ( io && io.stats )
{
read += io.stats.Read || 0;
write += io.stats.Write || 0;
}
});
prev.diskio.io_service_bytes.forEach((io) => {
if ( io && io.stats )
{
read -= io.stats.Read || 0;
write -= io.stats.Write || 0;
}
});
out.disk_read_kb = read/(time_diff_s*1024);
out.disk_write_kb = write/(time_diff_s*1024);
}
// network
if ( data.network )
{
out.net_rx_kb = (data.network.rx_bytes - prev.network.rx_bytes)/(time_diff_s*1024);
out.net_tx_kb = (data.network.tx_bytes - prev.network.tx_bytes)/(time_diff_s*1024);
}
} }
// Memory // Memory
@ -155,40 +188,6 @@ export default Ember.Object.extend(Ember.Evented, {
} }
} }
// Storage
if ( prev )
{
if ( data.diskio && data.diskio.io_serviced )
{
var read = 0;
var write = 0;
data.diskio.io_serviced.forEach((io) => {
if ( io && io.stats )
{
read += io.stats.Read || 0;
write += io.stats.Write || 0;
}
});
prev.diskio.io_serviced.forEach((io) => {
if ( io && io.stats )
{
read -= io.stats.Read || 0;
write -= io.stats.Write || 0;
}
});
out.disk_read_kb = read;
out.disk_write_kb = write;
}
}
// network
if ( data.network )
{
out.net_rx_kb = Math.round(data.network.rx_bytes/1000);
out.net_tx_kb = Math.round(data.network.tx_bytes/1000);
}
this.get('prev')[key] = data; this.get('prev')[key] = data;
this.trigger('dataPoint', out); this.trigger('dataPoint', out);

View File

@ -89,11 +89,20 @@ export default Ember.Object.extend(Ember.Evented, {
this.setProperties({ this.setProperties({
connected: true, connected: true,
tries: 0,
disconnectedAt: null, disconnectedAt: null,
}); });
this.trigger('connected', this.get('tries'), after); this.trigger('connected', this.get('tries'), after);
// Don't reset tries for a little bit, in case the socket immediately closes again.
// This prevents open/imediate close loops from hammering the server because the tries count is never incrementing.
Ember.run.later(this, '_resetTries', 1000);
},
_resetTries: function() {
if ( this.get('connected') ) {
this.set('tries', 0);
}
}, },
_message: function(event) { _message: function(event) {

View File

@ -1,27 +1,28 @@
{ {
"name": "ui", "name": "ui",
"dependencies": { "dependencies": {
"async": "~0.9.2",
"bootstrap-multiselect": "~0.9.10",
"bootstrap-sass-official": "~3.3.1",
"c3": "~0.4.10",
"dagre": "~0.7.1",
"dagre-d3": "~0.4.3",
"ember": "1.13.3", "ember": "1.13.3",
"jquery": "^2.1.4",
"ember-data": "1.13.5",
"ember-resolver": "~0.1.18",
"loader.js": "ember-cli/loader.js#3.2.0",
"ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3", "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
"ember-cli-test-loader": "ember-cli-test-loader#0.1.3", "ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
"ember-data": "1.13.5",
"ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5", "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
"ember-qunit": "0.4.1", "ember-qunit": "0.4.1",
"ember-qunit-notifications": "0.0.7", "ember-qunit-notifications": "0.0.7",
"qunit": "~1.17.1", "ember-resolver": "~0.1.18",
"font-awesome": "~4.4.0",
"jgrowl": "~1.4.2", "jgrowl": "~1.4.2",
"c3": "~0.4.10", "jquery": "^2.1.4",
"jquery.cookie": "~1.4.1", "jquery.cookie": "~1.4.1",
"bootstrap-sass-official": "~3.3.1", "loader.js": "ember-cli/loader.js#3.2.0",
"bootstrap-multiselect": "~0.9.10", "position-calculator": "~1.1.2",
"zeroclipboard": "~2.2.0",
"prism": "gh-pages", "prism": "gh-pages",
"dagre": "~0.7.1", "qunit": "~1.17.1",
"dagre-d3": "~0.4.3", "zeroclipboard": "~2.2.0"
"async": "~0.9.2",
"position-calculator": "~1.1.2"
} }
} }