flower.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. var flower = (function () {
  2. "use strict";
  3. /*jslint browser: true */
  4. /*global $, WebSocket, jQuery, Rickshaw */
  5. function on_alert_close(event) {
  6. event.preventDefault();
  7. event.stopPropagation();
  8. $(event.target).parent().hide();
  9. }
  10. function show_error_alert(message) {
  11. $("#alert").removeClass("alert-success").addClass("alert-error");
  12. $("#alert-message").html("<strong>Error!</strong> " + message);
  13. $("#alert").show();
  14. }
  15. function show_success_alert(message) {
  16. $("#alert").removeClass("alert-error").addClass("alert-success");
  17. $("#alert-message").html("<strong>Success!</strong> " + message);
  18. $("#alert").show();
  19. }
  20. function get_selected_workers() {
  21. return $('#workers-table tr').has('td.is_selected > input:checked');
  22. }
  23. function select_all_workers() {
  24. $('#workers-table td.is_selected > input').filter(':not(:checked)').click();
  25. }
  26. function select_none_workers() {
  27. $('#workers-table td.is_selected > input:checked').click();
  28. }
  29. function toggle_selected_workers(event) {
  30. var $checkbox = $('#select-workers-toggler');
  31. $checkbox.is(':checked') ? select_all_workers()
  32. : select_none_workers();
  33. }
  34. function shutdown_selected(event) {
  35. var $selected_workes = get_selected_workers();
  36. /* atomic would be better with list of ids (not-names) */
  37. $selected_workes.each(function () {
  38. var $worker = $(this),
  39. worker_name = $worker.attr('id');
  40. $.ajax({
  41. type: 'POST',
  42. url: url_prefix() + '/api/worker/shutdown/' + worker_name,
  43. dataType: 'json',
  44. data: { workername: worker_name },
  45. success: function (data) {
  46. show_success_alert(data.message);
  47. },
  48. error: function (data) {
  49. show_error_alert(data.responseText);
  50. }
  51. });
  52. });
  53. }
  54. function restart_selected(event) {
  55. var $selected_workes = get_selected_workers();
  56. /* atomic would be better with list of ids (not-names) */
  57. $selected_workes.each(function () {
  58. var $worker = $(this),
  59. worker_name = $worker.attr('id');
  60. $.ajax({
  61. type: 'POST',
  62. url: url_prefix() + '/api/worker/pool/restart/' + worker_name,
  63. dataType: 'json',
  64. data: { workername: worker_name },
  65. success: function (data) {
  66. show_success_alert(data.message);
  67. },
  68. error: function (data) {
  69. show_error_alert(data.responseText);
  70. }
  71. });
  72. });
  73. }
  74. function on_pool_grow(event) {
  75. event.preventDefault();
  76. event.stopPropagation();
  77. var workername = $('#workername').text(),
  78. grow_size = $('#pool-size option:selected').html();
  79. $.ajax({
  80. type: 'POST',
  81. url: url_prefix() + '/api/worker/pool/grow/' + workername,
  82. dataType: 'json',
  83. data: {
  84. 'workername': workername,
  85. 'n': grow_size,
  86. },
  87. success: function (data) {
  88. show_success_alert(data.message);
  89. },
  90. error: function (data) {
  91. show_error_alert(data.responseText);
  92. }
  93. });
  94. }
  95. function on_pool_shrink(event) {
  96. event.preventDefault();
  97. event.stopPropagation();
  98. var workername = $('#workername').text(),
  99. shrink_size = $('#pool-size option:selected').html();
  100. $.ajax({
  101. type: 'POST',
  102. url: url_prefix() + '/api/worker/pool/shrink/' + workername,
  103. dataType: 'json',
  104. data: {
  105. 'workername': workername,
  106. 'n': shrink_size,
  107. },
  108. success: function (data) {
  109. show_success_alert(data.message);
  110. },
  111. error: function (data) {
  112. show_error_alert(data.responseText);
  113. }
  114. });
  115. }
  116. function on_pool_autoscale(event) {
  117. event.preventDefault();
  118. event.stopPropagation();
  119. var workername = $('#workername').text(),
  120. min = $('#min-autoscale').val(),
  121. max = $('#max-autoscale').val();
  122. $.ajax({
  123. type: 'POST',
  124. url: url_prefix() + '/api/worker/pool/autoscale/' + workername,
  125. dataType: 'json',
  126. data: {
  127. 'workername': workername,
  128. 'min': min,
  129. 'max': max,
  130. },
  131. success: function (data) {
  132. show_success_alert(data.message);
  133. },
  134. error: function (data) {
  135. show_error_alert(data.responseText);
  136. }
  137. });
  138. }
  139. function on_add_consumer(event) {
  140. event.preventDefault();
  141. event.stopPropagation();
  142. var workername = $('#workername').text(),
  143. queue = $('#add-consumer-name').val();
  144. $.ajax({
  145. type: 'POST',
  146. url: url_prefix() + '/api/worker/queue/add-consumer/' + workername,
  147. dataType: 'json',
  148. data: {
  149. 'workername': workername,
  150. 'queue': queue,
  151. },
  152. success: function (data) {
  153. show_success_alert(data.message);
  154. setTimeout(function () {
  155. $('#tab-queues').load(url_prefix() + '/worker/' + workername + ' #tab-queues').fadeIn('show');
  156. }, 10000);
  157. },
  158. error: function (data) {
  159. show_error_alert(data.responseText);
  160. }
  161. });
  162. }
  163. function on_cancel_consumer(event) {
  164. event.preventDefault();
  165. event.stopPropagation();
  166. var workername = $('#workername').text(),
  167. queue = $(event.target).closest("tr").children("td:eq(0)").text();
  168. $.ajax({
  169. type: 'POST',
  170. url: url_prefix() + '/api/worker/queue/cancel-consumer/' + workername,
  171. dataType: 'json',
  172. data: {
  173. 'workername': workername,
  174. 'queue': queue,
  175. },
  176. success: function (data) {
  177. show_success_alert(data.message);
  178. setTimeout(function () {
  179. $('#tab-queues').load(url_prefix() + '/worker/' + workername + ' #tab-queues').fadeIn('show');
  180. }, 10000);
  181. },
  182. error: function (data) {
  183. show_error_alert(data.responseText);
  184. }
  185. });
  186. }
  187. function on_task_timeout(event) {
  188. event.preventDefault();
  189. event.stopPropagation();
  190. var workername = $('#workername').text(),
  191. taskname = $(event.target).closest("tr").children("td:eq(0)").text(),
  192. type = $(event.target).html().toLowerCase(),
  193. timeout = $(event.target).siblings().closest("input").val(),
  194. data = {};
  195. taskname = taskname.split(' ')[0]; // removes [rate_limit=xxx]
  196. console.log(type);
  197. data.taskname = taskname;
  198. data[type] = timeout;
  199. $.ajax({
  200. type: 'POST',
  201. url: url_prefix() + '/api/task/timeout/' + workername,
  202. dataType: 'json',
  203. data: data,
  204. success: function (data) {
  205. show_success_alert(data.message);
  206. },
  207. error: function (data) {
  208. show_error_alert(data.responseText);
  209. }
  210. });
  211. }
  212. function on_task_rate_limit(event) {
  213. event.preventDefault();
  214. event.stopPropagation();
  215. var workername = $('#workername').text(),
  216. taskname = $(event.target).closest("tr").children("td:eq(0)").text(),
  217. ratelimit = $(event.target).prev().val();
  218. taskname = taskname.split(' ')[0]; // removes [rate_limit=xxx]
  219. $.ajax({
  220. type: 'POST',
  221. url: url_prefix() + '/api/task/rate-limit/' + workername,
  222. dataType: 'json',
  223. data: {
  224. 'taskname': taskname,
  225. 'ratelimit': ratelimit,
  226. },
  227. success: function (data) {
  228. show_success_alert(data.message);
  229. setTimeout(function () {
  230. $('#tab-limits').load(url_prefix() + '/worker/' + workername + ' #tab-limits').fadeIn('show');
  231. }, 10000);
  232. },
  233. error: function (data) {
  234. show_error_alert(data.responseText);
  235. }
  236. });
  237. }
  238. function on_task_revoke(event) {
  239. event.preventDefault();
  240. event.stopPropagation();
  241. var taskid = $('#taskid').text();
  242. $.ajax({
  243. type: 'POST',
  244. url: url_prefix() + '/api/task/revoke/' + taskid,
  245. dataType: 'json',
  246. data: {
  247. 'terminate': false,
  248. },
  249. success: function (data) {
  250. show_success_alert(data.message);
  251. },
  252. error: function (data) {
  253. show_error_alert(data.responseText);
  254. }
  255. });
  256. }
  257. function on_task_terminate(event) {
  258. event.preventDefault();
  259. event.stopPropagation();
  260. var taskid = $('#taskid').text();
  261. $.ajax({
  262. type: 'POST',
  263. url: url_prefix() + '/api/task/revoke/' + taskid,
  264. dataType: 'json',
  265. data: {
  266. 'terminate': true,
  267. },
  268. success: function (data) {
  269. show_success_alert(data.message);
  270. },
  271. error: function (data) {
  272. show_error_alert(data.responseText);
  273. }
  274. });
  275. }
  276. function on_workers_table_update(update) {
  277. $.each(update, function (name) {
  278. var id = encodeURIComponent(name),
  279. sel = id.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/%@])/g,'\\$1'),
  280. tr = $('#' + sel);
  281. if (tr.length === 0) {
  282. $('#workers-table-row').clone().removeClass('hidden').attr('id', id).appendTo('tbody');
  283. tr = $('#' + sel);
  284. tr.children('td').children('a').attr('href', url_prefix() + '/worker/' + name).text(name);
  285. }
  286. var stat = tr.children('td:eq(2)').children(),
  287. concurrency = tr.children('td:eq(3)'),
  288. completed_tasks = tr.children('td:eq(4)'),
  289. running_tasks = tr.children('td:eq(5)'),
  290. queues = tr.children('td:eq(6)');
  291. stat.text($(this).attr('status') ? "Online" : "Offline");
  292. stat.removeClass("label-success label-important");
  293. stat.addClass($(this).attr('status') ? "label-success" : "label-important");
  294. concurrency.text($(this).attr('concurrency'));
  295. completed_tasks.text($(this).attr('completed_tasks'));
  296. running_tasks.text($(this).attr('running_tasks'));
  297. queues.text($(this).attr('queues').toString().replace(/,/g, ', '));
  298. });
  299. }
  300. function on_cancel_task_filter(event) {
  301. event.preventDefault();
  302. event.stopPropagation();
  303. $('#task-filter-form').each(function () {
  304. $(this).find('SELECT').val('');
  305. });
  306. $('#task-filter-form').submit();
  307. }
  308. function create_graph(data, id, width, height) {
  309. id = id || '';
  310. width = width || 700;
  311. height = height || 400;
  312. var name, seriesData = [];
  313. for (name in data) {
  314. seriesData.push({name: name});
  315. }
  316. var palette = new Rickshaw.Color.Palette({scheme: 'colorwheel'});
  317. var graph = new Rickshaw.Graph({
  318. element: document.getElementById("chart" + id),
  319. width: width,
  320. height: height,
  321. renderer: 'stack',
  322. series: new Rickshaw.Series(seriesData, palette),
  323. maxDataPoints: 10000,
  324. });
  325. var ticksTreatment = 'glow';
  326. var xAxis = new Rickshaw.Graph.Axis.Time({
  327. graph: graph,
  328. ticksTreatment: ticksTreatment,
  329. });
  330. xAxis.render();
  331. var yAxis = new Rickshaw.Graph.Axis.Y({
  332. graph: graph,
  333. tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
  334. ticksTreatment: ticksTreatment
  335. });
  336. yAxis.render();
  337. var slider = new Rickshaw.Graph.RangeSlider({
  338. graph: graph,
  339. element: $('#slider' + id)
  340. });
  341. var hoverDetail = new Rickshaw.Graph.HoverDetail({
  342. graph: graph
  343. });
  344. var legend = new Rickshaw.Graph.Legend({
  345. graph: graph,
  346. element: document.getElementById('legend' + id)
  347. });
  348. var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
  349. graph: graph,
  350. legend: legend
  351. });
  352. var order = new Rickshaw.Graph.Behavior.Series.Order({
  353. graph: graph,
  354. legend: legend
  355. });
  356. var highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({
  357. graph: graph,
  358. legend: legend
  359. });
  360. legend.shelving = shelving;
  361. graph.series.legend = legend;
  362. graph.render();
  363. return graph;
  364. }
  365. function update_graph(graph, url, lastquery) {
  366. $.ajax({
  367. type: 'GET',
  368. url: url,
  369. data: {lastquery: lastquery},
  370. success: function (data) {
  371. graph.series.addData(data);
  372. graph.update();
  373. },
  374. });
  375. }
  376. function current_unix_time() {
  377. var now = new Date();
  378. return Date.UTC(now.getUTCFullYear(), now.getUTCMonth(),
  379. now.getUTCDate(), now.getUTCHours(),
  380. now.getUTCMinutes(), now.getUTCSeconds())/1000;
  381. }
  382. function url_prefix() {
  383. // prefix is initialized in base.html
  384. if (prefix) {
  385. return '/' + prefix;
  386. }
  387. return '';
  388. }
  389. $.urlParam = function(name){
  390. var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
  391. return results && results[1] || 0;
  392. }
  393. $(document).ready(function () {
  394. if ($.inArray($(location).attr('pathname'), [url_prefix(), url_prefix() + '/workers'])) {
  395. var host = $(location).attr('host'),
  396. protocol = $(location).attr('protocol') == 'http:' ? 'ws://' : 'wss://',
  397. ws = new WebSocket(protocol + host + url_prefix() + "/update-workers");
  398. ws.onmessage = function (event) {
  399. var update = $.parseJSON(event.data);
  400. on_workers_table_update(update);
  401. };
  402. }
  403. //https://github.com/twitter/bootstrap/issues/1768
  404. var shiftWindow = function() { scrollBy(0, -50) };
  405. if (location.hash) shiftWindow();
  406. window.addEventListener("hashchange", shiftWindow);
  407. // Make bootstrap tabs persistent
  408. $(document).ready(function () {
  409. if (location.hash !== '') {
  410. $('a[href="' + location.hash + '"]').tab('show');
  411. }
  412. $('a[data-toggle="tab"]').on('shown', function (e) {
  413. location.hash = $(e.target).attr('href').substr(1);
  414. });
  415. });
  416. if ($(location).attr('pathname') === url_prefix() + '/monitor') {
  417. var sts = current_unix_time(),
  418. fts = current_unix_time(),
  419. tts = current_unix_time(),
  420. updateinterval = parseInt($.urlParam('updateInterval')) || 3000,
  421. succeeded_graph = null,
  422. failed_graph = null,
  423. time_graph = null,
  424. broker_graph = null;
  425. $.ajax({
  426. type: 'GET',
  427. url: url_prefix() + '/monitor/succeeded-tasks',
  428. data: {lastquery: current_unix_time()},
  429. success: function (data) {
  430. succeeded_graph = create_graph(data, '-succeeded');
  431. succeeded_graph.update();
  432. succeeded_graph.series.setTimeInterval(updateinterval);
  433. setInterval(function () {
  434. update_graph(succeeded_graph,
  435. url_prefix() + '/monitor/succeeded-tasks',
  436. sts);
  437. sts = current_unix_time();
  438. }, updateinterval);
  439. },
  440. });
  441. $.ajax({
  442. type: 'GET',
  443. url: url_prefix() + '/monitor/completion-time',
  444. data: {lastquery: current_unix_time()},
  445. success: function (data) {
  446. time_graph = create_graph(data, '-time');
  447. time_graph.update();
  448. time_graph.series.setTimeInterval(updateinterval);
  449. setInterval(function () {
  450. update_graph(time_graph,
  451. url_prefix() + '/monitor/completion-time',
  452. tts);
  453. tts = current_unix_time();
  454. }, updateinterval);
  455. },
  456. });
  457. $.ajax({
  458. type: 'GET',
  459. url: url_prefix() + '/monitor/failed-tasks',
  460. data: {lastquery: current_unix_time()},
  461. success: function (data) {
  462. failed_graph = create_graph(data, '-failed');
  463. failed_graph.update();
  464. failed_graph.series.setTimeInterval(updateinterval);
  465. setInterval(function () {
  466. update_graph(failed_graph,
  467. url_prefix() + '/monitor/failed-tasks',
  468. fts);
  469. fts = current_unix_time();
  470. }, updateinterval);
  471. },
  472. });
  473. $.ajax({
  474. type: 'GET',
  475. url: url_prefix() + '/monitor/broker',
  476. success: function (data) {
  477. broker_graph = create_graph(data, '-broker');
  478. broker_graph.update();
  479. broker_graph.series.setTimeInterval(updateinterval);
  480. setInterval(function () {
  481. update_graph(broker_graph,
  482. url_prefix() + '/monitor/broker');
  483. }, updateinterval);
  484. },
  485. });
  486. }
  487. });
  488. return {
  489. toggle_selected_workers: toggle_selected_workers,
  490. select_all_workers: select_all_workers,
  491. select_none_workers: select_none_workers,
  492. shutdown_selected: shutdown_selected,
  493. restart_selected: restart_selected,
  494. on_alert_close: on_alert_close,
  495. on_pool_grow: on_pool_grow,
  496. on_pool_shrink: on_pool_shrink,
  497. on_pool_autoscale: on_pool_autoscale,
  498. on_add_consumer: on_add_consumer,
  499. on_cancel_consumer: on_cancel_consumer,
  500. on_task_timeout: on_task_timeout,
  501. on_task_rate_limit: on_task_rate_limit,
  502. on_cancel_task_filter: on_cancel_task_filter,
  503. on_task_revoke: on_task_revoke,
  504. on_task_terminate: on_task_terminate,
  505. };
  506. }(jQuery));