Project

General

Profile

Download (9.62 KB) Statistics
| Branch: | Revision:
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
});
(16-16/23)