2 * This js establishes a websockets connection with the API Server.
5 /* Subscribe to websockets event log. Do nothing if already connected. */
6 function subscribeToEventLog () {
7 // if websockets are not supported by browser, do not subscribe for events
8 websocketsSupported = ('WebSocket' in window);
9 if (websocketsSupported == false) {
13 // check if websocket connection is already stored on the window
14 event_log_disp = $(window).data("arv-websocket");
15 if (event_log_disp == null) {
16 // need to create new websocket and event log dispatcher
17 websocket_url = $('meta[name=arv-websocket-url]').attr("content");
18 if (websocket_url == null)
21 event_log_disp = new WebSocket(websocket_url);
23 event_log_disp.onopen = onEventLogDispatcherOpen;
24 event_log_disp.onmessage = onEventLogDispatcherMessage;
26 // store websocket in window to allow reuse when multiple divs subscribe for events
27 $(window).data("arv-websocket", event_log_disp);
31 /* Send subscribe message to the websockets server. Without any filters
32 arguments, this subscribes to all events */
33 function onEventLogDispatcherOpen(event) {
34 this.send('{"method":"subscribe"}');
37 /* Trigger event for all applicable elements waiting for this event */
38 function onEventLogDispatcherMessage(event) {
39 parsedData = JSON.parse(event.data);
40 object_uuid = parsedData.object_uuid;
46 // if there are any listeners for this object uuid or "all", trigger the event
47 matches = ".arv-log-event-listener[data-object-uuid=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuids~=\"" + object_uuid + "\"],.arv-log-event-listener[data-object-uuid=\"all\"],.arv-log-event-listener[data-object-kind=\"" + parsedData.object_kind + "\"]";
48 $(matches).trigger('arv-log-event', parsedData);
51 /* Automatically connect if there are any elements on the page that want to
52 receive event log events. */
53 $(document).on('ajax:complete ready', function() {
54 var a = $('.arv-log-event-listener');
56 subscribeToEventLog();
60 /* Assumes existence of:
61 window.jobGraphData = [];
62 window.jobGraphSeries = [];
63 window.jobGraphMaxima = {};
65 function processLogLineForChart( logLine ) {
68 // TODO: make this more robust: anything could go wrong in here
69 var match = logLine.match(/(.*)crunchstat:(.*)-- interval(.*)/);
71 var series = match[2].trim().split(' ')[0];
72 if( $.inArray( series, jobGraphSeries) < 0 ) {
73 jobGraphSeries.push(series);
74 jobGraphMaxima[series] = null;
77 var intervalData = match[3].trim().split(' ');
78 var dt = parseFloat(intervalData[0]);
80 for(var i=2; i < intervalData.length; i += 2 ) {
81 dsum += parseFloat(intervalData[i]);
84 if( datum !== 0 && ( jobGraphMaxima[series] === null || jobGraphMaxima[series] < datum ) ) {
85 if( isJobSeriesRescalable(series) ) {
86 // use old maximum to get a scale conversion
87 var scaleConversion = jobGraphMaxima[series]/datum;
88 // set new maximum and rescale the series
89 jobGraphMaxima[series] = datum;
90 rescaleJobGraphSeries( series, scaleConversion );
92 // and special calculation for cpus
93 if( series === 'cpu' ) {
94 var cpuCountMatch = match[2].match(/(\d+) cpus/);
96 datum = datum / cpuCountMatch[1];
101 // FIXME: what about negative numbers?
102 var scaledDatum = null;
103 if( isJobSeriesRescalable(series) && jobGraphMaxima[series] !== null && jobGraphMaxima[series] !== 0 ) {
104 scaledDatum = datum/jobGraphMaxima[series]
109 var preamble = match[1].trim().split(' ');
110 var timestamp = preamble[0].replace('_','T');
111 // identify x axis point
113 for( var i = jobGraphData.length - 1; i >= 0; i-- ) {
114 if( jobGraphData[i]['t'] === timestamp ) {
116 jobGraphData[i][series] = scaledDatum;
118 } else if( jobGraphData[i]['t'] < timestamp ) {
119 // we've gone far enough back in time and this data is supposed to be sorted
123 // index counter from previous loop will have gone one too far, so add one
126 // create a new x point for this previously unrecorded timestamp
127 var entry = { 't': timestamp };
128 entry[series] = scaledDatum;
129 jobGraphData.splice( insertAt, 0, entry );
131 // now let's see about "scrolling" the graph, dropping entries that are too old (>3 minutes)
132 while( jobGraphData.length > 0
133 && (Date.parse( jobGraphData[0]['t'] ).valueOf() + 3*60000 < Date.parse( jobGraphData[jobGraphData.length-1]['t'] ).valueOf()) ) {
134 shifted.push(jobGraphData.shift());
136 if( shifted.length > 0 ) {
137 // from those that we dropped, are any of them maxima? if so we need to rescale
138 jobGraphSeries.forEach( function(series) {
139 // test that every shifted entry in this series was either not a number (in which case we don't care)
140 // or else smaller than the scaled maximum (i.e. 1), because otherwise we just scrolled off something that was a maximum point
141 // and so we need to recalculate a new maximum point by looking at all remaining displayed points in the series
142 if( isJobSeriesRescalable(series) && jobGraphMaxima[series] !== null
143 && !shifted.every( function(e) { return( !($.isNumeric(e[series])) || e[series] < 1 ) } ) ) {
144 // check the remaining displayed points and find the new (scaled) maximum
145 var seriesMax = null;
146 jobGraphData.forEach( function(entry) {
147 if( $.isNumeric(entry[series]) && (seriesMax === null || entry[series] > seriesMax)) {
148 seriesMax = entry[series];
151 if( seriesMax !== null && seriesMax !== 0 ) {
152 // set new actual maximum using the new maximum as the conversion conversion and rescale the series
153 jobGraphMaxima[series] *= seriesMax;
154 var scaleConversion = 1/seriesMax;
155 rescaleJobGraphSeries( series, scaleConversion );
158 // we no longer have any data points displaying for this series
159 jobGraphMaxima[series] = null;
169 function rescaleJobGraphSeries( series, scaleConversion ) {
170 if( isJobSeriesRescalable() ) {
171 $.each( jobGraphData, function( i, entry ) {
172 if( entry[series] !== null && entry[series] !== undefined ) {
173 entry[series] *= scaleConversion;
179 // that's right - we never do this for the 'cpu' series, which will always be between 0 and 1 anyway
180 function isJobSeriesRescalable( series ) {
181 return series != 'cpu';
184 $(document).on('arv-log-event', '#log_graph_div', function(event, eventData) {
185 if( eventData.properties.text ) {
186 var causeRecreate = processLogLineForChart( eventData.properties.text );
187 if( causeRecreate && !window.recreate ) {
188 window.recreate = true;
190 window.redraw = true;
195 $(document).on('ready', function(){
196 window.recreate = false;
197 window.redraw = false;
198 setInterval( function() {
200 window.recreate = false;
201 // series have changed, draw entirely new graph
202 $('#log_graph_div').html('');
203 window.jobGraph = Morris.Line({
204 element: 'log_graph_div',
208 ykeys: jobGraphSeries,
209 labels: jobGraphSeries
211 } else if( redraw ) {
212 window.redraw = false;
213 jobGraph.setData( jobGraphData );