1
|
define([
|
2
|
"dojox/data/ClientFilter",
|
3
|
"dojox/data/JsonRestStore",
|
4
|
"dojox/charting/Chart2D",
|
5
|
"dojox/charting/Theme"
|
6
|
], function(){
|
7
|
|
8
|
var stats = {
|
9
|
span : 36,
|
10
|
|
11
|
unit : "h",
|
12
|
|
13
|
/** now in seconds since epoch. */
|
14
|
now : function() {
|
15
|
return parseInt(new Date().getTime() * 0.001, 10);
|
16
|
}
|
17
|
|
18
|
};
|
19
|
|
20
|
stats.colors = {
|
21
|
current: 0x18B0000,
|
22
|
|
23
|
id2color: {},
|
24
|
|
25
|
next: function(){
|
26
|
this.current += 0x302010;
|
27
|
return this.current;
|
28
|
},
|
29
|
|
30
|
get: function(id){
|
31
|
var color = this.id2color[id];
|
32
|
if(!color){
|
33
|
color = this.next();
|
34
|
this.id2color[id] = color;
|
35
|
}
|
36
|
return "#" + color.toString(16).substr(1,7);
|
37
|
}
|
38
|
};
|
39
|
|
40
|
stats.theme = new dojox.charting.Theme({
|
41
|
chart:{
|
42
|
stroke:null,
|
43
|
fill: "white"
|
44
|
},
|
45
|
plotarea:{
|
46
|
stroke:null,
|
47
|
fill: "#D4EDF1"
|
48
|
},
|
49
|
axis:{
|
50
|
stroke:{ color:"#fff",width:2 },
|
51
|
line:{ color:"#fff",width:1 },
|
52
|
majorTick:{ color:"#fff", width:2, length:12 },
|
53
|
minorTick:{ color:"#fff", width:1, length:8 },
|
54
|
font:"normal normal normal 8pt Tahoma",
|
55
|
fontColor:"#999"
|
56
|
},
|
57
|
series:{
|
58
|
outline:{ width: 0.1, color:"#fff" },
|
59
|
stroke:{ width: 1, color:"#666" },
|
60
|
fill:new dojo.Color([0x66, 0x66, 0x66, 0.8]),
|
61
|
font:"normal normal normal 7pt Tahoma", // label
|
62
|
fontColor:"#000"
|
63
|
},
|
64
|
marker:{ // any markers on a series.
|
65
|
stroke:{ width:1 },
|
66
|
fill:"#333",
|
67
|
font:"normal normal normal 7pt Tahoma", // label
|
68
|
fontColor:"#000"
|
69
|
},
|
70
|
colors:[]
|
71
|
});
|
72
|
|
73
|
stats.labels = {
|
74
|
|
75
|
/**
|
76
|
* Gets labels for a chart with the given specs.
|
77
|
* Args:
|
78
|
* span {Number}:
|
79
|
* step {Number}:
|
80
|
* unit {String}:
|
81
|
*/
|
82
|
get: function(args){
|
83
|
var labels = [];
|
84
|
for(var i = 0; i < args.span; i = i + args.step){
|
85
|
labels.push({value: i, text: (i - args.span) + args.unit });
|
86
|
}
|
87
|
// the last label is "now"
|
88
|
labels.push({value:args.span, text: "now"});
|
89
|
return labels;
|
90
|
}
|
91
|
};
|
92
|
|
93
|
stats.data = {
|
94
|
|
95
|
secsPerStep: {
|
96
|
min: 60,
|
97
|
h: 3600,
|
98
|
d: 86400,
|
99
|
w: 604800,
|
100
|
m: 2592000 // 30 days
|
101
|
},
|
102
|
|
103
|
/**
|
104
|
* Converts unix timestamps (i.d., secs since epoch) to
|
105
|
* values in [0;...] suitable for charting.
|
106
|
*/
|
107
|
prepare: function(args) {
|
108
|
var item = args.item,
|
109
|
datasetprops = args.datasetprops || [],
|
110
|
span = args.span || stats.span,
|
111
|
unit = args.unit || stats.unit,
|
112
|
maxTime = args.maxTime;
|
113
|
|
114
|
// guard against null from the server
|
115
|
item.timestamps = item.timestamps || [];
|
116
|
item.mem = item.mem || [];
|
117
|
item.diskactivity = item.diskactivity || [];
|
118
|
item.cpuload = item.cpuload || [];
|
119
|
item.networkactivity = item.networkactivity || [];
|
120
|
|
121
|
var self = this;
|
122
|
|
123
|
/* at x = 0 */
|
124
|
var referenceTime = maxTime - (span * self.secsPerStep[unit]);
|
125
|
|
126
|
function normalize(ts){
|
127
|
var val = (ts - referenceTime) / self.secsPerStep[unit];
|
128
|
return val;
|
129
|
}
|
130
|
|
131
|
for(var j = 0; j < datasetprops.length; j++){
|
132
|
var normalized = [];
|
133
|
var min = null, max = null;
|
134
|
|
135
|
for(var i = 0; i < item.timestamps.length; i++){
|
136
|
var val = item[datasetprops[j]][i];
|
137
|
if(!min || val < min){
|
138
|
min = val;
|
139
|
}
|
140
|
if(!max || val > max){
|
141
|
max = val;
|
142
|
}
|
143
|
var x = normalize(item.timestamps[i]);
|
144
|
normalized.push({x:x, y: val});
|
145
|
}
|
146
|
item[datasetprops[j] + "_normalized"] = normalized;
|
147
|
item[datasetprops[j] + "_min"] = min || 0;
|
148
|
item[datasetprops[j] + "_max"] = max || 0;
|
149
|
}
|
150
|
},
|
151
|
|
152
|
load: function(item){
|
153
|
if(!this.store){
|
154
|
// this.store = new dojox.data.JsonRestStore({target:"/stabile/stats"});
|
155
|
this.store = new dojox.data.JsonRestStore({target:"/stabile/systems?action=metrics"});
|
156
|
}
|
157
|
var self = this;
|
158
|
var from = stats.now() - stats.span * stats.data.secsPerStep[stats.unit];
|
159
|
|
160
|
// FIXME: why is the data wrapped in a new array by dojo?
|
161
|
var query = {
|
162
|
uuid: item.uuid,
|
163
|
cpuload: true,
|
164
|
diskactivity: true,
|
165
|
networkactivity: true,
|
166
|
mem: true,
|
167
|
diskspace: true,
|
168
|
from: from,
|
169
|
to: stats.now() // now
|
170
|
};
|
171
|
var queryStr = "?" + dojo.objectToQuery(query);
|
172
|
|
173
|
function loaded(response, request){
|
174
|
if(!response.items){
|
175
|
IRIGO.toaster([
|
176
|
{
|
177
|
message: "No statistics available for server: " + item.name,
|
178
|
type: "message",
|
179
|
duration: 5000
|
180
|
}]);
|
181
|
return;
|
182
|
}
|
183
|
var server = response.items[0];
|
184
|
|
185
|
|
186
|
function prune(prop){
|
187
|
var divisor = 1;
|
188
|
if (prop == "mem") {divisor = 1024;}
|
189
|
else if (prop == "diskactivity") {divisor = 1024;}
|
190
|
else if (prop == "networkactivity") {divisor = 1024;}
|
191
|
else if (prop == "cpuload") {divisor = 1024*1024*100;}
|
192
|
var values = server[prop] || []; // sometimes null
|
193
|
var pruned = [];
|
194
|
var i;
|
195
|
var aber = 0;
|
196
|
for(i = 0; i < values.length; i = i + 1){
|
197
|
if (values[i]==null) { // Ugly hack to correct aberrations...
|
198
|
if (aber<3 && pruned[i-1]!=null) {
|
199
|
pruned.push(pruned[i-1]);
|
200
|
aber++;
|
201
|
} else {
|
202
|
pruned.push(values[i]);
|
203
|
}
|
204
|
} else {
|
205
|
pruned.push(values[i] / divisor);
|
206
|
aber = 0;
|
207
|
}
|
208
|
}
|
209
|
if (pruned[i-2]==0) {pruned[i-2] = pruned[i-3];}
|
210
|
if (pruned[i-1]==0) {pruned[i-1] = pruned[i-2];}
|
211
|
server[prop] = pruned;
|
212
|
}
|
213
|
|
214
|
dojo.forEach(["mem", "cpuload", "diskactivity", "networkactivity"],
|
215
|
function(prop){
|
216
|
prune(prop);
|
217
|
});
|
218
|
|
219
|
|
220
|
self.prepare(
|
221
|
{
|
222
|
maxTime: stats.now(),
|
223
|
span: stats.span,
|
224
|
unit: stats.unit,
|
225
|
item:server,
|
226
|
datasetprops: ["cpuload", "mem", "diskactivity", "networkactivity"]
|
227
|
}
|
228
|
);
|
229
|
self.onLoad(server);
|
230
|
}
|
231
|
|
232
|
self.store.fetch({
|
233
|
query:query,
|
234
|
onComplete: loaded,
|
235
|
onError: function(){
|
236
|
IRIGO.toaster([
|
237
|
{
|
238
|
message: "Error fetching data from server: " + item.name,
|
239
|
type: "message",
|
240
|
duration: 5000
|
241
|
}]);
|
242
|
|
243
|
}
|
244
|
});
|
245
|
},
|
246
|
|
247
|
// event to attach to
|
248
|
onLoad: function(item){}
|
249
|
};
|
250
|
|
251
|
stats.chart = function(domId, args){
|
252
|
domId = dojo.byId(domId);
|
253
|
args = args || {};
|
254
|
args.type = args.type || "";
|
255
|
args.yAxisMax = args.yAxisMax || 100;
|
256
|
|
257
|
var c = {
|
258
|
tickStepXAxis: 4,
|
259
|
chart: null,
|
260
|
type: args.type,
|
261
|
|
262
|
addXAxis: function(){
|
263
|
this.chart.addAxis("x", {
|
264
|
labels: stats.labels.get({span:stats.span, step:this.tickStepXAxis, unit: stats.unit}),
|
265
|
min: 0,
|
266
|
max: stats.span,
|
267
|
includeZero: true,
|
268
|
minorTicks: true,
|
269
|
majorTickStep: this.tickStepXAxis
|
270
|
});
|
271
|
|
272
|
},
|
273
|
|
274
|
addYAxis: function(){
|
275
|
c.chart.addAxis("y", {
|
276
|
min: 0,
|
277
|
max: args.yAxisMax,
|
278
|
vertical: true,
|
279
|
includeZero: true,
|
280
|
minorTicks: false,
|
281
|
majorTickStep: args.yAxisMax / 4
|
282
|
});
|
283
|
},
|
284
|
|
285
|
addBackgroundGrid: function(){
|
286
|
this.chart.addPlot("grid", {type: "Grid", hMinorLines: true});
|
287
|
},
|
288
|
|
289
|
render: function(){
|
290
|
this.refresh();
|
291
|
this.chart.render();
|
292
|
},
|
293
|
|
294
|
refresh: function(){
|
295
|
this.chart.removeAxis("y");
|
296
|
this.addYAxis();
|
297
|
this.chart.removeAxis("x");
|
298
|
this.addXAxis();
|
299
|
},
|
300
|
|
301
|
add: function(item){
|
302
|
|
303
|
var data = item[args.type + "_normalized"] || console.error("couldn't find prepared data");
|
304
|
|
305
|
this.chart.addSeries(item.name, data, {
|
306
|
// legend: item.name,
|
307
|
stroke: {color:stats.colors.get(item.name), width: 1},
|
308
|
line: {width:1}
|
309
|
});
|
310
|
|
311
|
// adjust yAxis
|
312
|
args.yAxisMax = item[args.type + "_max"] * 1.1;
|
313
|
var yMax = item[args.type + "_max"];
|
314
|
if(yMax > args.yAxisMax){
|
315
|
args.yAxisMax = yMax * 1.1;
|
316
|
}
|
317
|
|
318
|
var index = this.chart.runs[item.name];
|
319
|
return this.chart.series[index];
|
320
|
},
|
321
|
|
322
|
remove: function(item){
|
323
|
this.chart.removeSeries(item.name);
|
324
|
},
|
325
|
|
326
|
init: function(domId){
|
327
|
this.chart = new dojox.charting.Chart2D(domId);
|
328
|
this.chart.setTheme(stats.theme);
|
329
|
this.addYAxis();
|
330
|
this.addXAxis();
|
331
|
//this.addBackgroundGrid();
|
332
|
|
333
|
return this;
|
334
|
|
335
|
// _legend = new dojox.charting.widget.Legend({chart: c.chart}, "chartLegend");
|
336
|
}
|
337
|
};
|
338
|
|
339
|
var chart = c.init(domId);
|
340
|
chart.refresh();
|
341
|
return chart;
|
342
|
};
|
343
|
|
344
|
window.stats = stats;
|
345
|
|
346
|
});
|