Fix error when sorting Job list by object.
[ganeti_webmgr.git] / ganeti_web / static / js / job_status.js
blobaaa3d3002e20d7c9ae6287bd638437d97a2a3494
1 /** @namespace data.opresult */
3 /* Capitalize the first letter of every word in a string. */
4 function cap_first(str) {
5     var a = str.split(" ");
6     var len = a.length;
8     for (var i = 0; i < len; i++) {
9         a[i] = a[i][0].toUpperCase() + a[i].substring(1);
10     }
12     return a.join(" ");
15 /* Format an operation string.
16  *
17  * Operation strings look like "OP_DO_SOMETHING". They should be formatted to
18  * appear as "Do Something". */
19 function format_op(op, stat) {
20     op = op.substring(3).toLowerCase();
21     op = op.replace(/_/g, " ");
22     str = cap_first(op) + ": " + cap_first(stat);
23     return str;
26 function JobPoller() {
27     var actions_enabled;
28     var get_interval;
29     var get_interval_speed;
30     var get_jobs_url = '';
31     var messages = $('#messages');
32     var callback;
33     var errback;
34     var cluster;
35     this.FAST = 3000;
36     this.SLOW = 60000;
37     var get_xhr = undefined;
38     var poller;
41     this.init = function (url, new_cluster, new_callback, new_errback) {
42         poller = this;
43         get_jobs_url = url;
44         cluster = new_cluster;
45         callback = new_callback==undefined ? $.noop : new_callback;
46         errback = new_errback==undefined ? $.noop : new_errback;
47     };
49     // poll for active jobs.  This maintains the list of jobs that are being
50     // actively queried for updates.  This will pull in new jobs started elsewhere
51     this.poll = function (interval) {
52         interval = interval==undefined ? poller.SLOW : interval;
53         if (get_interval_speed != interval) {
54             if (get_interval != undefined) {
55                 clearInterval(get_interval);
56             }
57             get_interval_speed = interval;
58             get_interval = setInterval(poller.get_jobs, interval);
59         }
60     };
63     // get list of active jobs
64     this.get_jobs = function () {
65         /* Run the AJAX call. if a call is pending, just skip this one */
66         if (get_xhr == undefined) {
67             get_xhr = $.ajax({
68                 url: get_jobs_url,
69                 error: errback,
70                 success: function(data) {
71                     get_xhr = undefined;
72                     process_get_jobs(data);
73                 }
74             });
75         }
76     };
79     // process list of active jobs.  This will import new jobs if any are not being
80     // tracked already
81     function process_get_jobs(data) {
82         var active = [];
83         for (var i in data) {
84             var job_data = data[i];
85             if (job_data.status == 'success') {
86                 callback();
87             } else {
88                 poller.render_job(job_data);
89                 active.push(job_data.id);
90                 if (job_data.status == 'error') {
91                     errback();
92                 }
93             }
94         }
96         // clear any jobs that weren't in the active list any more
97         $('#messages').children('.job').each(function(){
98             var job_id = this.id.substring(4);
99             if (active.indexOf(job_id) == -1) {
100                 if (!$(this).hasClass('error')) {
101                     callback();
102                 }
103                 $(this).remove();
104             }
105         });
107         if (data.length==0) {
108             poller.poll(poller.SLOW);
109         } else {
110             poller.poll(poller.FAST);
111         }
112     }
115     function active_op(data) {
116         /* Find a sub-operation which has not successfully completed. */
117         for (var sub_op = 0; sub_op < data['opstatus'].length; sub_op++) {
118             if (data['opstatus'][sub_op] != 'success') {
119                 return sub_op;
120             }
121         }
122         return sub_op;
123     }
126     this.render_job = function (data) {
127         var job_id = data['id'];
128         var html = $('#job_'+job_id);
129         var op_index = active_op(data);
130         var status = data['status'];
131         var op = format_op(data['ops'][op_index]['OP_ID'], status);
132         var new_job = (html.length==0);
134         if (new_job) {
135             html = $("<li class='job'><h3>"+op+"</h3></li>");
136             html.attr('id', 'job_'+job_id);
137             html.addClass(status);
138             $('#messages').append(html);
139             html.append('<div class="scrollable"><div class="detector"></div></div>');
140         }
142         var error = undefined;
143         var scrollable = html.children('.scrollable');
145         if (status=='running' || status=='error') {
146             html.addClass(status);
148             if (data.status == 'error' && html.find('pre.error').length==0) {
149                 var reason = data.opresult[op_index][1][0];
150                 var href = cluster + "/job/" + job_id + "/clear/";
151                 html.children('h3')
152                 .append("<a class='clear' title='clear error' href='"+href+"'></a>");
153                 error = $("<pre class='error'>" + reason + "</pre>");
154                 scrollable.prepend(error);
155                 actions_enabled = true;
156                 $('#actions a').removeClass('disabled');
157             }
159             // append log messages that are not already displayed
160             var current_log_count = html.find(".op_log ul li").length;
161             if (data['oplog'][op_index].length != 0) {
162                 var log_html = html.find('.op_log');
163                 if (log_html.length==0){
164                     log_html = $("<pre class='op_log'><ul></ul></pre>");
165                     scrollable.append(log_html);
166                 }
167                 var log = data['oplog'][op_index];
168                 for (var i=current_log_count; i<log.length; i++) {
169                     log_html.children("ul")
170                     .append("<li>"+log[i][3]+"</li>");
171                 }
172             }
174             // XXX hack to ensure log area is same width as error area.
175             var width = undefined;
176             if (log_html != undefined) {
177                 width = $(html).find('.scrollable .detector').width();
178                 width -= (log_html.innerWidth() - log_html.width()); // subtract padding
179                 log_html.width(width);
180                 $(html).find('.scrollable').css('display', 'block')
181             } else if (error != undefined) {
182                 width = error.width();
183                 $(html).find('.scrollable .detector').width(width);
184                 $(html).find('.scrollable').css('display', 'block')
185             }
186         }
187     };
189     $("#messages a.clear").live("click", function(event){
190         event.preventDefault();
191         var error = $(this).parent().parent();
192         $.post(this.href, function(){
193             error.remove();
194         });
195     });