From a64d5b3e61698a2534f399198b40db52e0eb1609 Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 10:42:49 -0400 Subject: [PATCH 01/20] Fix JSHint warnings in console.html Also use === and !== for more predictable comparisons instead of == and != The "dot notation" warnings were not fixed as array-style access provides more clarity here. Those warnings can be suppressed with: /* jshint sub:true */ --- clblob/console.html | 108 ++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 9afa0d5..1185073 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -271,7 +271,7 @@ if ($('#replica_' + replica).hasClass('hide')) continue; } - else if (filter && filter != replica) + else if (filter && filter !== replica) continue; callback(replica); } @@ -280,28 +280,28 @@ function handle_keypress(event) { if (event.ctrlKey) return; - if (event.which == 104) { + if (event.which === 104) { $('div.replica').addClass('hide'); return; } - if (event.which == 72) { + if (event.which === 72) { $('div.replica').removeClass('hide'); return; } - if (event.which == 73 || event.which == 105) { + if (event.which === 73 || event.which === 105) { $('#by_ip').toggleClass('hide'); return; } var url; var upper_key = event.which & 95; - var filter = event.which == 32 || event.which >= 97; - if (upper_key == 82 || event.which == 32) + var filter = event.which === 32 || event.which >= 97; + if (upper_key === 82 || event.which === 32) url = null; - if (upper_key == 66) + if (upper_key === 66) url = '/_buffer/'; - if (upper_key == 80) + if (upper_key === 80) url = '/_purge/'; - if (upper_key == 83) + if (upper_key === 83) url = '/_sync/'; if (url === undefined) return; @@ -309,11 +309,11 @@ event.preventDefault(); var current = Math.round(new Date().getTime() / keypress_max_ms); - if (current == last_keypress) + if (current === last_keypress) return; last_keypress = current; - if (url != null) { + if (url !== null) { foreach_replica(function (replica) { $.ajax({url: url + replica, type: "GET", timeout: replica_timeout}); }, filter); @@ -400,18 +400,18 @@ var usage = 0; var total = 0; var count = 0; - for (replica in details['values']) { + for (var replica in details['values']) { count++; - if (details['type'] == 'usage') { + if (details['type'] === 'usage') { usage += details['values'][replica][0]; total += details['values'][replica][1]; } else total += details['values'][replica]; } - if (count == 0) + if (count === 0) return ''; - if (details['type'] == 'usage') + if (details['type'] === 'usage') return hover_help( usage_metric(name, total, usage, 60, 80, details['factor']), details['help_text']); @@ -422,17 +422,18 @@ } function update_replica_usage(cluster, bucket) { + var replica; var replicas = config.clblob.client.clusters[cluster][bucket].replicas; var max_usage = 0; - for (var replica = 0; replica < replicas.length; replica++) { + for (replica = 0; replica < replicas.length; replica++) { if (max_usage < replica_usage[replicas[replica]]) max_usage = replica_usage[replicas[replica]]; } - if (max_usage == 0) + if (max_usage === 0) return; - for (var replica = 0; replica < replicas.length; replica++) { + for (replica = 0; replica < replicas.length; replica++) { var replica_name = replicas[replica]; - if (replica_usage[replica_name] == undefined) + if (replica_usage[replica_name] === undefined) continue; var relative = (replica_usage[replica_name] / max_usage) * 100; var replica_object = $('.replica_name_' + replica_name); @@ -467,19 +468,19 @@ html += button('sync', '/_sync/' + replica, true); html += hover_help( metric('sync status', status.sync_status, 0, 0, null, - status.sync_status.indexOf('failed') == 0 ? 1 : 0), + status.sync_status.indexOf('failed') === 0 ? 1 : 0), sync_status_help(status.sync_status)); html += button('purge', '/_purge/' + replica, true); html += hover_help( metric('purge status', status.purge_status, 0, 0, null, - status.purge_status.indexOf('failed') == 0 ? 1 : 0), + status.purge_status.indexOf('failed') === 0 ? 1 : 0), purge_status_help(status.purge_status)); html += button('buffer', '/_buffer/' + replica, true); html += hover_help( metric('buffer status', status.buffer_status, 0, 0, null, - status.buffer_status.indexOf('failed') == 0 ? 1 : 0), + status.buffer_status.indexOf('failed') === 0 ? 1 : 0), buffer_status_help(status.buffer_status)); html += ''; @@ -515,39 +516,39 @@ update_replica_status(replica, replica_status); update_summary(); update_replica_usage(status.cluster, status.bucket); - } + }; } function sync_status_help(status) { - if (status.indexOf('start delay') == 0) + if (status.indexOf('start delay') === 0) return 'Sync has not been run since server start.'; - if (status.indexOf('failed') == 0) + if (status.indexOf('failed') === 0) return 'Sync failed to complete.'; - if (status.indexOf('local checksums') == 0) + if (status.indexOf('local checksums') === 0) help = 'Sync is running and is currently computing a list of ' + 'checksums for this replica.'; - else if (status.indexOf('source checksums') == 0) + else if (status.indexOf('source checksums') === 0) help = 'Sync is running and is currently computing a list of ' + 'checksums for a peer replica.'; - else if (status.indexOf('diff checksums') == 0) + else if (status.indexOf('diff checksums') === 0) help = 'Sync is running and is currently comparing a list of ' + 'checksums between this replica and a peer replica.'; - else if (status.indexOf('local blobs') == 0) + else if (status.indexOf('local blobs') === 0) help = 'Sync is running and is currently getting a list of blobs ' + 'for this replica.'; - else if (status.indexOf('source blobs') == 0) + else if (status.indexOf('source blobs') === 0) help = 'Sync is running and is currently getting a list of blobs ' + 'for a peer replica.'; - else if (status.indexOf('diff blobs') == 0) + else if (status.indexOf('diff blobs') === 0) help = 'Sync is running and is currently comparing a list of blobs ' + 'between this replica and a peer replica.'; - else if (status.indexOf('put') == 0) + else if (status.indexOf('put') === 0) help = 'Sync is running and is currently grabbing a blob from a ' + 'peer to put into this replica.'; - else if (status.indexOf('delete') == 0) + else if (status.indexOf('delete') === 0) help = 'Sync is running and is currently updating the delete time ' + 'for a blob in this replica.'; - else if (status.indexOf('synced') == 0) + else if (status.indexOf('synced') === 0) help = 'Sync is done running.'; else help = 'Status of current/last sync operation.'; @@ -556,20 +557,20 @@ } function purge_status_help(status) { - if (status.indexOf('start delay') == 0) + if (status.indexOf('start delay') === 0) return 'Purge has not been run since server start.'; - if (status.indexOf('failed') == 0) + if (status.indexOf('failed') === 0) return 'Purge failed to complete.'; - if (status.indexOf('get_expired') == 0) + if (status.indexOf('get_expired') === 0) help = 'Purge is running and is currently getting a list of expired ' + 'blobs from the index.'; - else if (status.indexOf('delete') == 0) + else if (status.indexOf('delete') === 0) help = 'Purge is running and is currently deleting an expired blob ' + 'from disk.'; - else if (status.indexOf('delete_expired') == 0) + else if (status.indexOf('delete_expired') === 0) help = 'Purge is running and is currently deleting expired blobs ' + 'from the index.'; - else if (status.indexOf('purged') == 0) + else if (status.indexOf('purged') === 0) help = 'Purge is done running.'; else help = 'Status of current/last purge operation.'; @@ -578,17 +579,17 @@ } function buffer_status_help(status) { - if (status.indexOf('start delay') == 0) + if (status.indexOf('start delay') === 0) return 'Buffer has not been run since server start.'; - if (status.indexOf('failed') == 0) + if (status.indexOf('failed') === 0) help = 'Buffer failed to complete.'; - if (status.indexOf('get_replica_events') == 0) + if (status.indexOf('get_replica_events') === 0) help = 'Buffer is running and is currently getting a list of ' + 'replica events from the index to buffer into memory.'; - else if (status.indexOf('get_cluster_events') == 0) + else if (status.indexOf('get_cluster_events') === 0) help = 'Buffer is running and is currently getting a list of ' + 'cluster events from the index to buffer into memory.'; - else if (status.indexOf('buffered') == 0) + else if (status.indexOf('buffered') === 0) help = 'Buffer is done running.'; else help = 'Status of current/last buffer operation.'; @@ -630,7 +631,7 @@ 'The <used>/<total> number of disk inodes.'); summary['disk inodes']['values'][status.device_id] = [status.total_inodes - status.free_inodes, status.total_inodes]; - replica_usage[replica] = status.total_inodes - status.free_inodes + replica_usage[replica] = status.total_inodes - status.free_inodes; html += hover_help( metric('disk queue size', status.queue_size, 10, 20), 'The number of events in the disk thread queue.'); @@ -657,7 +658,7 @@ replica_retry, 0, null); } return html; - } + }; } function cluster_metrics(replica) { @@ -675,7 +676,7 @@ summary['cluster events']['values'][replica] += status.events; } return html; - } + }; } function event_metrics(event, status) { @@ -701,7 +702,7 @@ $('#replica_' + replica).html(html); $('#replica_' + replica).addClass('warning'); update_replica_status(replica, 'critical'); - } + }; } function button(name, url, background) { @@ -737,11 +738,12 @@ function iterate_sorted_object(key_values, callback) { var keys = []; - for (var key in key_values) + var key; + for (key in key_values) keys.push(key); keys.sort(); var html = ''; - for (var key in keys) { + for (key in keys) { key = keys[key]; html += callback(key, key_values[key]); } @@ -755,7 +757,7 @@ return ' critical'; } else if (value < warning) { - if (replica_status != 'critical') + if (replica_status !== 'critical') replica_status = 'warning'; return ' warning'; } @@ -766,7 +768,7 @@ return ' critical'; } else if (value > warning) { - if (replica_status != 'critical') + if (replica_status !== 'critical') replica_status = 'warning'; return ' warning'; } From c8cfd5542fe10f5bdddde746ebb952dfca331dce Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 11:47:13 -0400 Subject: [PATCH 02/20] Start converting console.html to use Mithril.js --- clblob/console.html | 101 ++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 27 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 1185073..9e96fd5 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -155,12 +155,13 @@ + - -
-
b - Run buffer on visible replicas
-
B - Run buffer on all replicas
-
h - Hide all replicas
-
H - Show all replicas
-
i/I - Toggle ip view
-
p - Run purge on visible replicas
-
P - Run purge on all replicas
-
r or <space> - Refresh visible replicas
-
R - Refresh all replicas
-
s - Run sync on visible replicas
-
S - Run sync on all replicas
-
-
-
-
-
+ From 19447493b1ba5f0932c1168f81cf85c4d59c0527 Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 12:05:18 -0400 Subject: [PATCH 03/20] Use a function to generate help div in console.html --- clblob/console.html | 69 +++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 46 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 9e96fd5..9f53b16 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -810,6 +810,28 @@ isHelpHidden = true; isByIPHidden = true; +function generateHelpDiv() { + var help = { + b: "Run buffer on visible replicas", + B: "Run buffer on all replicas", + h: "Hide all replicas", + H: "Show all replicas", + "i/I": "Toggle ip view", + p: "Run purge on visible replicas", + P: "Run purge on all replicas", + "r or <space>": "Refresh visible replicas", + R: "Refresh all replicas", + s: "Run sync on visible replicas", + S: "Run sync on all replicas" + }; + + return m("#help", { class: isHelpHidden ? "hide" : "nohide" }, + Object.keys(help).map(function (key) { + return m("div", m("b", m.trust(key)), " - ", help[key]) + }) + ); +} + function main() { return m("div", [ m("#header", [ @@ -826,52 +848,7 @@ m("b", "by ip") ]) ]), - m("#help", { class: isHelpHidden ? "hide" : "nohide" }, [ - m("div", [ - m("b", "b"), - " - Run buffer on visible replicas" - ]), - m("div", [ - m("b", "B"), - " - Run buffer on all replicas" - ]), - m("div", [ - m("b", "h"), - " - Hide all replicas" - ]), - m("div", [ - m("b", "H"), - " - Show all replicas" - ]), - m("div", [ - m("b", "i/I"), - " - Toggle ip view" - ]), - m("div", [ - m("b", "p"), - " - Run purge on visible replicas" - ]), - m("div", [ - m("b", "P"), - " - Run purge on all replicas" - ]), - m("div", [ - m("b", "r or ", m.trust("<space>")), - " - Refresh visible replicas" - ]), - m("div", [ - m("b", "R"), - " - Refresh all replicas" - ]), - m("div", [ - m("b", "s"), - " - Run sync on visible replicas" - ]), - m("div", [ - m("b", "S"), - " - Run sync on all replicas" - ]) - ]), + generateHelpDiv(), m("#grid"), m("#summary"), m("#by_ip", { class: isByIPHidden ? "hide" : "" } ), From 10b0e0bdb15f089b5bbcc728b85580748b342dcb Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 12:11:12 -0400 Subject: [PATCH 04/20] Declare local help variables to prevent creation of global in console.html --- clblob/console.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clblob/console.html b/clblob/console.html index 9f53b16..2459d96 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -521,6 +521,7 @@ } function sync_status_help(status) { + var help; if (status.indexOf('start delay') === 0) return 'Sync has not been run since server start.'; if (status.indexOf('failed') === 0) @@ -558,6 +559,7 @@ } function purge_status_help(status) { + var help; if (status.indexOf('start delay') === 0) return 'Purge has not been run since server start.'; if (status.indexOf('failed') === 0) @@ -580,6 +582,7 @@ } function buffer_status_help(status) { + var help; if (status.indexOf('start delay') === 0) return 'Buffer has not been run since server start.'; if (status.indexOf('failed') === 0) From 814547288108bd0076f617698f87352962721c29 Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 12:11:45 -0400 Subject: [PATCH 05/20] Rename key_help function to be consistent with other help functions in console.html --- clblob/console.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 2459d96..49be867 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -813,7 +813,7 @@ isHelpHidden = true; isByIPHidden = true; -function generateHelpDiv() { +function key_help() { var help = { b: "Run buffer on visible replicas", B: "Run buffer on all replicas", @@ -851,7 +851,7 @@ m("b", "by ip") ]) ]), - generateHelpDiv(), + key_help(), m("#grid"), m("#summary"), m("#by_ip", { class: isByIPHidden ? "hide" : "" } ), From fcac7e375cca81d15ea328e64e3b04a6310e643e Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 13:27:38 -0400 Subject: [PATCH 06/20] console: refactor update_config into smaller functions Also, store the clusters and by_ip in globals. This is to make it easier to convert the individual functions to Mithril vdom generated from a well-defined model. --- clblob/console.html | 121 ++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 49be867..8760a77 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -175,12 +175,40 @@ var replica_usage = {}; var replica_timeout = 2000; +var clusters = []; +var by_ip = {}; + function update_config(data) { config = data; + clusters = config.clblob.client.clusters; + + for (var cluster = 0; cluster < clusters.length; cluster++) { + var buckets = clusters[cluster]; + for (var bucket = 0; bucket < buckets.length; bucket++) { + var replicas = buckets[bucket].replicas; + for (var replica = 0; replica < replicas.length; replica++) { + var ip = config.clblob.client.replicas[replicas[replica]].ip; + var port = config.clblob.client.replicas[replicas[replica]].port; + if (!by_ip[ip]) by_ip[ip] = {}; + by_ip[ip][port] = replicas[replica]; + refresh_replicas.push(replicas[replica]); + } + } + } + + $('#grid').html(grid()); + $('#by_ip').html(iterate_sorted_object(by_ip, combine_ips)); + $('#main').html(main()); + + $('div.replica').addClass('hide'); + $(document).keypress(handle_keypress); + refresh(); + + m.redraw(); +} + +function grid() { var grid = ''; - var main = ''; - var by_ip = {}; - var clusters = config.clblob.client.clusters; grid += '
'; for (var cluster = 0; cluster < clusters.length; cluster++) { @@ -190,26 +218,12 @@ '
' + '
'; - main += '
' + - '
' + - '
cluster ' + cluster + '
' + - '
'; - var buckets = clusters[cluster]; for (var bucket = 0; bucket < buckets.length; bucket++) { grid += '
' + '
' + bucket + '
'; - main += '
' + - '
' + - '
bucket ' + bucket + '
' + - metric('write weight', buckets[bucket].write_weight, 0, 0, - null, 0); - var replicas = buckets[bucket].replicas; - var replica_main = ''; for (var replica = 0; replica < replicas.length; replica++) { grid += hover_help( '
', replicas[replica]); + } + grid += '
'; + } + grid += '
'; + } + grid += '
'; - main += '
' + replicas[replica] + '
'; + return grid; +} - replica_main += '
' + - '
' + - '
' + replicas[replica] + '
' + - '
'; +function main() { + var main = ''; - var ip = config.clblob.client.replicas[replicas[replica]].ip; - var port = config.clblob.client.replicas[replicas[replica]].port; - if (!by_ip[ip]) - by_ip[ip] = {}; - by_ip[ip][port] = replicas[replica]; + for (var cluster = 0; cluster < clusters.length; cluster++) { + main += '
' + + '
' + + '
cluster ' + cluster + '
' + + '
'; - refresh_replicas.push(replicas[replica]); + var buckets = clusters[cluster]; + for (var bucket = 0; bucket < buckets.length; bucket++) { + main += '
' + + '
' + + '
bucket ' + bucket + '
' + + metric('write weight', buckets[bucket].write_weight, 0, 0, + null, 0); + + var replicas = buckets[bucket].replicas; + for (var replica = 0; replica < replicas.length; replica++) { + main += '
' + replicas[replica] + '
'; } - grid += '
'; - main += '
' + replica_main + '
'; + main += replica_main(replicas); } - grid += ''; main += ''; } - grid += ''; - $('#grid').html(grid); - $('#by_ip').html(iterate_sorted_object(by_ip, combine_ips)); - $('#main').html(main); - $('div.replica').addClass('hide'); - $(document).keypress(handle_keypress); - refresh(); + return main; +} + +function replica_main(replicas) { + var replica_main = ''; + + for (var replica = 0; replica < replicas.length; replica++) { + replica_main += '
' + + '
' + + '
' + replicas[replica] + '
' + + '
'; + } + return '' + replica_main + ''; } function combine_ips(ip, ports) { @@ -835,7 +870,7 @@ ); } -function main() { +function body() { return m("div", [ m("#header", [ m("h1", m.trust("☮"), " craigslist blob console"), @@ -861,6 +896,6 @@ - + From fbeadc32350f3ad3a4ac6963a36c85c89c4347b9 Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 15:18:32 -0400 Subject: [PATCH 07/20] console: refactor grid function so uses mithril --- clblob/console.html | 74 ++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 8760a77..b0d87f0 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -158,8 +158,6 @@ - + From e01cbc0b5514b965e2eb3b43614318bc67f81a2a Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sat, 13 May 2017 17:47:34 -0400 Subject: [PATCH 08/20] console: convert main() and replica_main() to generate Mithril --- clblob/console.html | 91 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index b0d87f0..bd9b6bf 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -195,13 +195,9 @@ } $('#by_ip').html(iterate_sorted_object(by_ip, combine_ips)); - $('#main').html(main()); - $('div.replica').addClass('hide'); $(document).keypress(handle_keypress); refresh(); - - m.redraw(); } function grid() { @@ -238,49 +234,52 @@ } function main() { - var main = ''; - - for (var cluster = 0; cluster < clusters.length; cluster++) { - main += '
' + - '
' + - '
cluster ' + cluster + '
' + - '
'; - - var buckets = clusters[cluster]; - for (var bucket = 0; bucket < buckets.length; bucket++) { - main += '
' + - '
' + - '
bucket ' + bucket + '
' + - metric('write weight', buckets[bucket].write_weight, 0, 0, - null, 0); - - var replicas = buckets[bucket].replicas; - for (var replica = 0; replica < replicas.length; replica++) { - main += '
' + replicas[replica] + '
'; - } - main += replica_main(replicas); - } - main += '
'; - } - - return main; + return clusters.map(function (cluster, clusterIndex) { + return m(".cluster#cluster_" + clusterIndex, + m(".group", + m(".name", + m("b", "cluster " + clusterIndex) + ) + ), + cluster.map(function (bucket, bucketIndex) { + return m(".bucket#bucket_" + clusterIndex + '_' + bucketIndex, + m(".group.clickable", + { + onclick: function(event) { + // TODO: store this state in domain not DOM + $(this).siblings('div.replica').toggleClass('hide'); + } + }, + m(".name", + m("b", "bucket " + bucketIndex) + ), + // TODO: Remove this use of m.trust + m.trust(metric('write weight', bucket.write_weight, 0, 0, null, 0)), + bucket.replicas.map(function (replica, replicaIndex) { + return m(".metric#replica_name_" + replica, + m("b", replica) + ) + }), + ), + replica_main(bucket.replicas) + ) + }) + ) + }); } function replica_main(replicas) { - var replica_main = ''; - - for (var replica = 0; replica < replicas.length; replica++) { - replica_main += '
' + - '
' + - '
' + replicas[replica] + '
' + - '
'; - } - return '
' + replica_main + '
'; + return replicas.map(function (replica, replicaIndex) { + // TODO: Use of css class .hide here is problematical + // since we're relying on vdom not putting it back while jQuery interacts with classes + return m(".replica.hide#replica_" + replica, + m(".group", + m(".name#replica_name_" + replica, + m("b", replica) + ) + ) + ) + }); } function combine_ips(ip, ports) { @@ -873,7 +872,7 @@ m("#header", [ m("h1", m.trust("☮"), " craigslist blob console"), m("a.clickable", { - onclick: function() { isHelpHidden = !isHelpHidden; console.log("isHelpHidden", isHelpHidden); }, + onclick: function() { isHelpHidden = !isHelpHidden; }, }, [ m("b", "shortcuts") ]), @@ -888,7 +887,7 @@ m("#grid", grid()), m("#summary"), m("#by_ip", { class: isByIPHidden ? "hide" : "" } ), - m("#main") + m("#main", main()) ]); } From 073a87be362e32a2620138e73dd419e8a9f428ec Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sun, 14 May 2017 02:48:43 +0000 Subject: [PATCH 09/20] console: remove more warnings --- clblob/console.html | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index bd9b6bf..07738ee 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -157,6 +157,8 @@ - + From fb470ddd498a2eeddfae852ddf7f0b64d5295b3b Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Sun, 14 May 2017 09:32:00 -0400 Subject: [PATCH 10/20] console: clarify names of globals with replicas --- clblob/console.html | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index 07738ee..b824733 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -176,27 +176,30 @@ var replica_timeout = 2000; var clusters = []; -var by_ip = {}; +var allReplicasByName = {}; +var allReplicasByIP = {}; function update_config(data) { config = data; clusters = config.clblob.client.clusters; + allReplicasByName = config.clblob.client.replicas; - for (var cluster = 0; cluster < clusters.length; cluster++) { - var buckets = clusters[cluster]; - for (var bucket = 0; bucket < buckets.length; bucket++) { - var replicas = buckets[bucket].replicas; - for (var replica = 0; replica < replicas.length; replica++) { - var ip = config.clblob.client.replicas[replicas[replica]].ip; - var port = config.clblob.client.replicas[replicas[replica]].port; - if (!by_ip[ip]) by_ip[ip] = {}; - by_ip[ip][port] = replicas[replica]; - refresh_replicas.push(replicas[replica]); + for (var clusterIndex = 0; clusterIndex < clusters.length; clusterIndex++) { + var buckets = clusters[clusterIndex]; + for (var bucketIndex = 0; bucketIndex < buckets.length; bucketIndex++) { + var replicasInBucket = buckets[bucketIndex].replicas; + for (var replicaIndex = 0; replicaIndex < replicasInBucket.length; replicaIndex++) { + var replica = replicasInBucket[replicaIndex]; + var ip = allReplicasByName[replica].ip; + var port = allReplicasByName[replica].port; + if (!allReplicasByIP[ip]) allReplicasByIP[ip] = {}; + allReplicasByIP[ip][port] = replica; + refresh_replicas.push(replica); } } } - $('#by_ip').html(iterate_sorted_object(by_ip, combine_ips)); + $('#by_ip').html(iterate_sorted_object(allReplicasByIP, combine_ips)); $(document).keypress(handle_keypress); refresh(); @@ -454,7 +457,7 @@ function update_replica_usage(cluster, bucket) { var replica; - var replicas = config.clblob.client.clusters[cluster][bucket].replicas; + var replicas = clusters[cluster][bucket].replicas; var max_usage = 0; for (replica = 0; replica < replicas.length; replica++) { if (max_usage < replica_usage[replicas[replica]]) @@ -485,8 +488,7 @@ running_status--; replica_status = 'ok'; var html = ''; - var address = config.clblob.client.replicas[replica].ip + ':' + - config.clblob.client.replicas[replica].port; + var address = allReplicasByName[replica].ip + ':' + allReplicasByName[replica].port; html += '
'; html += '
' + replica + '
'; @@ -724,8 +726,7 @@ flush_status(); running_status--; var html = ''; - var address = config.clblob.client.replicas[replica].ip + ':' + - config.clblob.client.replicas[replica].port; + var address = allReplicasByName[replica].ip + ':' + allReplicasByName[replica].port; html += '
' + '
' + replica + '
' + From 5c4269e120e838bd569a0087c38b9aa13f22387f Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Mon, 15 May 2017 23:56:45 -0400 Subject: [PATCH 11/20] console: finish bulk of changeover to Mithril Create stateForReplica global for tracking replica state, and make support functions to query it and update it. Use stateForReplica instead of CSS classes to store information about the replicas (including warning level and whether they should be hidden). The use of maximumWarningSeverity may need more work and testing relative to when replica_status is set and when CSS is styled. Ideally that global should be removed. --- clblob/console.html | 581 ++++++++++++++++++++++++++++---------------- 1 file changed, 365 insertions(+), 216 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index b824733..d5bef9e 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -179,6 +179,89 @@ var allReplicasByName = {}; var allReplicasByIP = {}; +// stateForReplica stores the last known state for a replica +// its contents are: +// replica_ID: { +// requestResult: "success" | "error" | null, +// status: Object, +// hidden: boolean, +// maximumWarningSeverity: "ok" | "warning" | "critical", +// updating: boolean, +// relativeUsagePercent: number +//} +var stateForReplica = {}; + +function getStateForReplica(replica) { + var state = stateForReplica[replica]; + if (!state) { + state = { + requestResult: null, + status: {}, + hidden: true, + maximumWarningSeverity: "ok", + updating: false, + relativeUsagePercent: null + }; + stateForReplica[replica] = state; + } + return state; +} + +// Will only change keys supplied in newState and leave others as they were +function updateStateForReplica(replica, newState) { + var state = getStateForReplica(replica); + for (var key in newState) { + state[key] = newState[key]; + } +} + +function updateStateForAllReplicas(newState) { + for (let replica in stateForReplica) { + var state = getStateForReplica(replica); + for (var key in newState) { + state[key] = newState[key]; + } + } +} + +function updateStateForBucketReplicas(bucket, newState) { + bucket.replicas.map(function (replica) { + var state = getStateForReplica(replica); + for (var key in newState) { + state[key] = newState[key]; + } + }); +} + +function getStateForBucketReplicas(bucket) { + var result = []; + bucket.replicas.map(function (replica) { + var state = getStateForReplica(replica); + result.push(state); + }); + return result; +} + +function getReplicaLabelCSSClasses(replica) { + var state = getStateForReplica(replica); + var severity = "." + state.maximumWarningSeverity; + var updating = state.updating ? ".updating" : "" + return severity + updating; +} + +// type is name or metric +// Label generated may include relative usage percent if available. +// Label will also include warning classes if needed. +function getReplicaLabel(replica, type) { + var relativeUsagePercent = getStateForReplica(replica).relativeUsagePercent; + var relativeUsagePercentText = relativeUsagePercent === null ? "" : " " + relativeUsagePercent + "%"; + var possibleColonForRelativeUsagePercent = relativeUsagePercent === null ? "" : ":"; + return m("." + type + ".replica_name_" + replica + getReplicaLabelCSSClasses(replica), + m("b", replica, possibleColonForRelativeUsagePercent), + relativeUsagePercentText + ) +} + function update_config(data) { config = data; clusters = config.clblob.client.clusters; @@ -199,8 +282,6 @@ } } - $('#by_ip').html(iterate_sorted_object(allReplicasByIP, combine_ips)); - $(document).keypress(handle_keypress); refresh(); } @@ -217,16 +298,15 @@ return m(".grid_bucket", m(".grid_bucket_number", bucketIndex), bucket.replicas.map(function (replica, replicaIndex) { - return m_hover_help( + return hover_help( m("a", { href: "#bucket_" + clusterIndex + '_' + bucketIndex, onclick: function() { - // TODO: Store open/close state in domain not DOM - $('#replica_' + replica).parent().children('div.replica').removeClass('hide'); + updateStateForBucketReplicas(bucket, {hidden: false}); } }, - m(".grid_replica#grid_replica_" + replica) + m(".grid_replica#grid_replica_" + replica + getReplicaLabelCSSClasses(replica)) ), replica ) @@ -251,19 +331,15 @@ m(".group.clickable", { onclick: function(event) { - // TODO: store this state in domain not DOM - $(this).siblings('div.replica').toggleClass('hide'); + updateStateForBucketReplicas(bucket, {hidden: !getStateForBucketReplicas(bucket)[0].hidden}); } }, m(".name", m("b", "bucket " + bucketIndex) ), - // TODO: Remove this use of m.trust - m.trust(metric('write weight', bucket.write_weight, 0, 0, null, 0)), + metric('write weight', bucket.write_weight, 0, 0, null, 0), bucket.replicas.map(function (replica, replicaIndex) { - return m(".metric#replica_name_" + replica, - m("b", replica) - ) + return getReplicaLabel(replica, "metric"); }) ), replica_main(bucket.replicas) @@ -275,34 +351,45 @@ function replica_main(replicas) { return replicas.map(function (replica, replicaIndex) { - // TODO: Use of css class .hide here is problematical - // since we're relying on vdom not putting it back while jQuery interacts with classes - return m(".replica.hide#replica_" + replica, - m(".group", - m(".name#replica_name_" + replica, - m("b", replica) - ) + var requestResult = getStateForReplica(replica).requestResult; + var innerDiv; + if (requestResult === "success") { + innerDiv = generate_replica_status_success_div(replica); + } else if (requestResult === "error") { + innerDiv = generate_replica_status_fail_div(replica); + } else { + innerDiv = m(".group", + getReplicaLabel(replica, "name") ) + } + + var state = getStateForReplica(replica); + var hideClass = state.hidden ? ".hide" : ""; + var warningClass = state.requestResult === "error" ? ".warning" : ""; + return m(".replica#replica_" + replica + hideClass + warningClass, + innerDiv ) }); } function combine_ips(ip, ports) { - return '
' + '
ip ' + - ip + '
' + iterate_sorted_object(ports, combine_ports) + - '
'; + return m(".ip", + m(".group", + m(".name", m("b", "ip " + ip)), + iterate_sorted_object(ports, combine_ports) + ) + ) } function combine_ports(port, replica) { - return '
' + replica + - '
'; + return getReplicaLabel(replica, "metric"); } function foreach_replica(callback, filter) { for (var index = 0; index < refresh_replicas.length; index++) { var replica = refresh_replicas[index]; if (filter === true) { - if ($('#replica_' + replica).hasClass('hide')) + if (getStateForReplica(replica).hidden) continue; } else if (filter && filter !== replica) @@ -312,18 +399,20 @@ } function handle_keypress(event) { + // TODO: Remove this when full Mithril changeover + setTimeout(m.redraw, 0); if (event.ctrlKey) return; if (event.which === 104) { - $('div.replica').addClass('hide'); + updateStateForAllReplicas({hidden: true}); return; } if (event.which === 72) { - $('div.replica').removeClass('hide'); + updateStateForAllReplicas({hidden: false}); return; } if (event.which === 73 || event.which === 105) { - $('#by_ip').toggleClass('hide'); + isByIPHidden = !isByIPHidden; return; } var url; @@ -349,7 +438,7 @@ if (url !== null) { foreach_replica(function (replica) { - $.ajax({url: url + replica, type: "GET", timeout: replica_timeout}); + $.ajax({url: url + replica, type: "GET", timeout: replica_timeout, success: m.redraw, error: m.redraw}); }, filter); } else @@ -362,8 +451,7 @@ if (!replica) break; running_status++; - $('#grid_replica_' + replica).addClass('updating'); - $('div.replica_name_' + replica).addClass('updating'); + updateStateForReplica(replica, {updating: true}); $.ajax({ url: '/_status/' + replica, type: "GET", @@ -398,39 +486,42 @@ 'type': 'metric', 'values': {}, 'warning': 1000, - 'help_text': 'The total number of events queued to a cluster across ' + - 'all replicas.'}, + 'help_text': 'The total number of events queued to a cluster across all replicas.' + }, 'disk inodes': { 'factor': 1000, 'type': 'usage', 'values': {}, - 'help_text': 'The <used>/<total> number of disk inodes ' + - 'across all replicas.'}, + 'help_text': 'The <used>/<total> number of disk inodes across all replicas.' + }, 'disk space': { 'factor': 1024, 'type': 'usage', 'values': {}, - 'help_text': 'The <used>/<total> amount of disk space ' + - 'across all replicas.'}, + 'help_text': 'The <used>/<total> amount of disk space across all replicas.' + }, 'replica events': { 'critical': 10000, 'factor': 1000, 'type': 'metric', 'values': {}, 'warning': 1000, - 'help_text': 'The total number of events queued to a replica across ' + - 'all replicas.'}}; + 'help_text': 'The total number of events queued to a replica across all replicas.' + } +}; function update_summary() { - var html = ''; - html += '
'; - html += '
summary
'; - html += iterate_sorted_object(summary, summary_metrics); - html += '
'; - $('#summary').html(html); + iterate_sorted_object(summary, update_summary_metrics); } -function summary_metrics(name, details) { +function generate_summary_div() { + return m(".group", + m(".name", m("b", "summary")), + iterate_sorted_object(summary, summary_metrics) + ); +} + +function update_summary_metrics(name, details) { var usage = 0; var total = 0; var count = 0; @@ -443,113 +534,144 @@ else total += details['values'][replica]; } - if (count === 0) + // Add storage object into summary data structure for later use when generating summary metrics divs + details._replicaStats = { + usage: usage, + total: total, + count: count + } +} + +function summary_metrics(name, details) { + if (!details._replicaStats || details._replicaStats.count === 0) return ''; if (details['type'] === 'usage') return hover_help( - usage_metric(name, total, usage, 60, 80, details['factor']), - details['help_text']); + usage_metric(name, details._replicaStats.total, details._replicaStats.usage, 60, 80, details['factor']), + details['help_text'] + ); return hover_help( - metric(name, total, details['warning'], details['critical'], - details['factor']), - details['help_text']); + metric(name, details._replicaStats.total, details['warning'], details['critical'], details['factor']), + details['help_text'] + ); } function update_replica_usage(cluster, bucket) { - var replica; + var replicaIndex; var replicas = clusters[cluster][bucket].replicas; var max_usage = 0; - for (replica = 0; replica < replicas.length; replica++) { - if (max_usage < replica_usage[replicas[replica]]) - max_usage = replica_usage[replicas[replica]]; + for (replicaIndex = 0; replicaIndex < replicas.length; replicaIndex++) { + if (max_usage < replica_usage[replicas[replicaIndex]]) + max_usage = replica_usage[replicas[replicaIndex]]; } if (max_usage === 0) return; - for (replica = 0; replica < replicas.length; replica++) { - var replica_name = replicas[replica]; - if (replica_usage[replica_name] === undefined) + for (replicaIndex = 0; replicaIndex < replicas.length; replicaIndex++) { + var replica = replicas[replicaIndex]; + if (replica_usage[replica] === undefined) continue; - var relative = (replica_usage[replica_name] / max_usage) * 100; - var replica_object = $('.replica_name_' + replica_name); - replica_object.html('' + replica_name + - ': ' + Math.round(relative) + '%'); - if (relative < 98 && !replica_object.hasClass('critical')) { - $('#grid_replica_' + replica_name).addClass('warning'); - replica_object.addClass('warning'); + var relative = (replica_usage[replica] / max_usage) * 100; + + var relativeUsagePercent = Math.round(relative); + updateStateForReplica(replica, {relativeUsagePercent: relativeUsagePercent}) + + var severity = getStateForReplica(replica).maximumWarningSeverity; + + // If a replica's relativeUsagePercent is lagging behind the other replicas, it may be experiencing issues, so warn + if (relative < 98 && severity !== "critical") { + updateStateForReplica(replica, {maximumWarningSeverity: "warning"}) } } } +// TODO: Remove replica_status global +// This global is updated through the process of generating the status display. +// It is the maximum severity of all the status warnings for a replica. var replica_status; function update_status(replica) { return function (status) { flush_status(); running_status--; - replica_status = 'ok'; - var html = ''; - var address = allReplicasByName[replica].ip + ':' + allReplicasByName[replica].port; - html += '
'; - html += '
' + - replica + '
'; - html += button(address, 'http://' + address + '/_console'); - html += '
refresh
'; - html += '
'; - html += '
'; - - html += button('sync', '/_sync/' + replica, true); - html += hover_help( - metric('sync status', status.sync_status, 0, 0, null, - status.sync_status.indexOf('failed') === 0 ? 1 : 0), - sync_status_help(status.sync_status)); - - html += button('purge', '/_purge/' + replica, true); - html += hover_help( - metric('purge status', status.purge_status, 0, 0, null, - status.purge_status.indexOf('failed') === 0 ? 1 : 0), - purge_status_help(status.purge_status)); - - html += button('buffer', '/_buffer/' + replica, true); - html += hover_help( - metric('buffer status', status.buffer_status, 0, 0, null, - status.buffer_status.indexOf('failed') === 0 ? 1 : 0), - buffer_status_help(status.buffer_status)); - - html += '
'; - html += '
'; + + updateStateForReplica(replica, { + requestResult: "success", + status: status, + maximumWarningSeverity: "ok", + updating: false + }); + summary['replica events']['values'][replica] = 0; - html += iterate_sorted_object(status.replicas, - replica_metrics(replica)); - summary['cluster events']['values'][replica] = 0; - html += iterate_sorted_object(status.clusters, - cluster_metrics(replica)); - html += '
'; - - html += '
'; - html += hover_help(iterate_sorted_object(status.events, event_metrics), - 'The <current>/<max>/<total> number of events ' + - 'this replica has created.'); - html += '
'; - - if ('index_sqlite' in status) { - html += '
'; - html += index_sqlite_metrics(replica, status.index_sqlite); - html += '
'; - } + iterate_sorted_object(status.replicas, update_replica_metrics(replica)); - if ('store_disk' in status) { - html += '
'; - html += store_disk_metrics(replica, status.store_disk); - html += '
'; - } + summary['cluster events']['values'][replica] = 0; + iterate_sorted_object(status.clusters, update_cluster_metrics(replica)); + + if ('store_disk' in status) update_store_disk_metrics(replica, status.store_disk); - $('#replica_' + replica).html(html); - $('#replica_' + replica).removeClass('warning'); - update_replica_status(replica, replica_status); update_summary(); + update_replica_usage(status.cluster, status.bucket); - }; + + // TODO: Remove this when full Mithril changeover + setTimeout(m.redraw, 0); + } +} + +function generate_replica_status_success_div(replica) { + // TODO: This replica_status global is updated as a side effect of generating displays for metrics + replica_status = 'ok'; + var address = allReplicasByName[replica].ip + ':' + allReplicasByName[replica].port; + var status = getStateForReplica(replica).status; + var sync_status_severity = (status.sync_status || "").indexOf('failed') === 0 ? 1 : 0; + var purge_status_severity = (status.purge_status || "").indexOf('failed') === 0 ? 1 : 0; + var buffer_status_severity = (status.buffer_status || "").indexOf('failed') === 0 ? 1 : 0; + var result = [ + m(".group", + getReplicaLabel(replica, "name"), + button(address, 'http://' + address + '/_console'), + m(".button", { + onclick: refresh.bind(null, replica) + }, m("b", "refresh")) + ), + m(".group", + button('sync', '/_sync/' + replica, true), + hover_help( + metric('sync status', status.sync_status, 0, 0, null, sync_status_severity), + sync_status_help(status.sync_status), + ), + + button('purge', '/_purge/' + replica, true), + hover_help( + metric('purge status', status.purge_status, 0, 0, null, purge_status_severity), + purge_status_help(status.purge_status) + ), + + button('buffer', '/_buffer/' + replica, true), + hover_help( + metric('buffer status', status.buffer_status, 0, 0, null, buffer_status_severity), + buffer_status_help(status.buffer_status) + ) + ), + + m(".group", + iterate_sorted_object(status.replicas, replica_metrics(replica)), + iterate_sorted_object(status.clusters, cluster_metrics(replica)) + ), + + m(".group", + hover_help( + iterate_sorted_object(status.events, event_metrics), + 'The <current>/<max>/<total> number of events this replica has created.' + ) + ), + + ('index_sqlite' in status) ? m(".group", index_sqlite_metrics(replica, status.index_sqlite)) : [], + ('store_disk' in status) ? m(".group", store_disk_metrics(replica, status.store_disk)) : [] + ]; + + updateStateForReplica(replica, {maximumWarningSeverity: replica_status}); + return result; } function sync_status_help(status) { @@ -633,75 +755,90 @@ 'that could be buffered during this run.'; } -function update_replica_status(replica, replica_status) { - $('#grid_replica_' + replica).removeClass().addClass( - 'grid_replica ' + replica_status); - $('div.replica_name_' + replica).removeClass().addClass( - 'name replica_name_' + replica + ' ' + replica_status); -} - function index_sqlite_metrics(replica, status) { - var html = ''; - html += metric('sqlite database', status.database); - html += hover_help( - usage_metric('sqlite size', status.max_size, status.size, 60, 80, 1024), - 'The <used>/<total> amount of disk space for the index.'); - html += hover_help( - metric('sqlite queue size', status.queue_size, 10, 20), - 'The number of events in the index thread queue.'); - return html; + return [ + metric('sqlite database', status.database), + hover_help( + usage_metric('sqlite size', status.max_size, status.size, 60, 80, 1024), + 'The <used>/<total> amount of disk space for the index.' + ), + hover_help( + metric('sqlite queue size', status.queue_size, 10, 20), + 'The number of events in the index thread queue.' + ) + ]; } -function store_disk_metrics(replica, status) { - var html = ''; - html += metric('disk path', status.path); - html += hover_help( - usage_metric('disk space', status.total_space, - status.total_space - status.free_space, 60, 80, 1024), - 'The <used>/<total> amount of disk space.'); +function update_store_disk_metrics(replica, status) { summary['disk space']['values'][status.device_id] = [status.total_space - status.free_space, status.total_space]; - html += hover_help( - usage_metric('disk inodes', status.total_inodes, - status.total_inodes - status.free_inodes, 60, 80, 1000), - 'The <used>/<total> number of disk inodes.'); summary['disk inodes']['values'][status.device_id] = [status.total_inodes - status.free_inodes, status.total_inodes]; replica_usage[replica] = status.total_inodes - status.free_inodes; - html += hover_help( - metric('disk queue size', status.queue_size, 10, 20), - 'The number of events in the disk thread queue.'); - return html; } -function replica_metrics(summary_replica) { +function store_disk_metrics(replica, status) { + return [ + metric('disk path', status.path), + hover_help( + usage_metric('disk space', status.total_space, + status.total_space - status.free_space, 60, 80, 1024), + 'The <used>/<total> amount of disk space.' + ), + hover_help( + usage_metric('disk inodes', status.total_inodes, + status.total_inodes - status.free_inodes, 60, 80, 1000), + 'The <used>/<total> number of disk inodes.' + ), + hover_help( + metric('disk queue size', status.queue_size, 10, 20), + 'The number of events in the disk thread queue.' + ) + ]; +} + +function update_replica_metrics(summary_replica) { return function (replica, status) { - var html = ''; if ('events' in status) { - html += hover_help( - metric(replica + ' events', - status.buffer_events + '/' + status.events, - config.clblob.client.replica_event_buffer_size, - config.clblob.client.replica_event_buffer_size * 10, - 1000, status.events), - 'The number of <buffered>/<total> events queued ' + - 'up to go to replica ' + replica + '.'); summary['replica events']['values'][summary_replica] += status.events; } + }; +} + +function replica_metrics(summary_replica) { + return function (replica, status) { var replica_retry = config.clblob.client.replica_retry; - if ('last_failed' in status && status.last_failed < replica_retry) { - html += metric(replica + ' failed', status.last_failed, - replica_retry, 0, null); + return [ + ('events' in status) ? + hover_help( + metric(replica + ' events', + status.buffer_events + '/' + status.events, + config.clblob.client.replica_event_buffer_size, + config.clblob.client.replica_event_buffer_size * 10, + 1000, status.events), + 'The number of <buffered>/<total> events queued ' + + 'up to go to replica ' + replica + '.' + ) : + [], + ('last_failed' in status && status.last_failed < replica_retry) ? + metric(replica + ' failed', status.last_failed, replica_retry, 0, null) : + [] + ]; + }; +} + +function update_cluster_metrics(replica) { + return function (cluster, status) { + if ('events' in status) { + summary['cluster events']['values'][replica] += status.events; } - return html; }; } function cluster_metrics(replica) { return function (cluster, status) { - var html = ''; if ('events' in status) { - html += hover_help( + return hover_help( metric('cluster ' + cluster + ' events', status.buffer_events + '/' + status.events, config.clblob.client.cluster_event_buffer_size, @@ -709,9 +846,8 @@ 1000, status.events), 'The number of <buffered>/<total> events queued ' + 'up to go to cluster ' + cluster + '.'); - summary['cluster events']['values'][replica] += status.events; } - return html; + return []; }; } @@ -722,57 +858,68 @@ } function update_status_fail(replica) { - return function (data) { + return function (status) { flush_status(); running_status--; - var html = ''; - var address = allReplicasByName[replica].ip + ':' + allReplicasByName[replica].port; - html += '
' + - '
' + replica + - '
' + - button(address, 'http://' + address + '/_console') + - metric('failed', data.statusText, 0, 0, null, 1) + - '
'; - - $('#replica_' + replica).html(html); - $('#replica_' + replica).addClass('warning'); - update_replica_status(replica, 'critical'); - }; + updateStateForReplica(replica, { + requestResult: "error", + status: status, + maximumWarningSeverity: "critical", + updating: false + }); + // TODO: Remove this when full Mithril changeover + setTimeout(m.redraw, 0); + } +} + +function generate_replica_status_fail_div(replica) { + var address = allReplicasByName[replica].ip + ':' + allReplicasByName[replica].port; + var status = getStateForReplica(replica).status; + + return m(".group", + getReplicaLabel(replica, "name"), + button(address, 'http://' + address + '/_console'), + metric('failed', status.statusText, 0, 0, null, 1) + ); } function button(name, url, background) { + // This could be simplified to return one vdom node with an if statement to choose function action if (background) { - return '
' + name + '
'; + return m(".button", { + onclick: function() { + $.ajax({url: url, type: 'GET', timeout: replica_timeout, success: m.redraw, error: m.redraw}); + } + }, m("b", name)); } - return '
' + name + '
'; + return m(".button", { + onclick: function() { + location.href = url; + } + }, m("b", name)); } function metric(name, value, warning, critical, factor, notify_value) { if (notify_value === undefined) notify_value = value; var notify = get_notify_class(notify_value, warning, critical); - return '
' + name + ': ' + - si_prefix(value, factor) + '
'; + return m(".metric." + notify, m("b", name + ":"), si_prefix(value, factor)); } function usage_metric(name, total, used, warning, critical, factor) { var used_percent = Math.round((used / total) * 100); - return metric(name, si_prefix(used, factor) + '/' + - si_prefix(total, factor) + ' (' + used_percent + '%)', warning, - critical, null, used_percent); + return metric( + name, + si_prefix(used, factor) + '/' + si_prefix(total, factor) + ' (' + used_percent + '%)', + warning, + critical, + null, + used_percent + ); } function hover_help(content, help_text) { - return '' + content + - '
' + help_text + '
'; -} - -function m_hover_help(content, help_text) { - return m("span.hover_help_wrapper", content, m(".hover_help", help_text)); + return m("span.hover_help_wrapper", content, m(".hover_help", m.trust(help_text))); } function iterate_sorted_object(key_values, callback) { @@ -781,38 +928,40 @@ for (key in key_values) keys.push(key); keys.sort(); - var html = ''; + var result = []; for (key in keys) { key = keys[key]; - html += callback(key, key_values[key]); + result.push(callback(key, key_values[key])); } - return html; + return result; } +// TODO: Remove global use from this function +// This function has a side-effect of updating a global replica_status with increasingly severe status function get_notify_class(value, warning, critical) { if (warning > critical) { if (value < critical) { replica_status = 'critical'; - return ' critical'; + return 'critical'; } else if (value < warning) { if (replica_status !== 'critical') replica_status = 'warning'; - return ' warning'; + return 'warning'; } } else { if (value > critical) { replica_status = 'critical'; - return ' critical'; + return 'critical'; } else if (value > warning) { if (replica_status !== 'critical') replica_status = 'warning'; - return ' warning'; + return 'warning'; } } - return ''; + return 'normal'; } var SI_PREFIXES_LARGE = ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; @@ -888,8 +1037,8 @@ ]), key_help(), m("#grid", grid()), - m("#summary"), - m("#by_ip", { class: isByIPHidden ? "hide" : "" } ), + m("#summary", generate_summary_div()), + m("#by_ip", { class: isByIPHidden ? "hide" : "" }, iterate_sorted_object(allReplicasByIP, combine_ips)), m("#main", main()) ]); } From 9211377fc85fdbab835d9c0f9c33bc03d47586f7 Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Tue, 16 May 2017 00:47:59 -0400 Subject: [PATCH 12/20] console: replace last jQuery functions with Mithril or plain JavaScript Remove loading the jQuery library too. --- clblob/console.html | 55 +++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/clblob/console.html b/clblob/console.html index d5bef9e..94d8f60 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -154,10 +154,10 @@ } - + // TODO: IMPORTANT For production use, mithril.js should be a local file like jQuery was From 615ff241ad79dd4c0fa67e4069cd29a40da5e43b Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Wed, 17 May 2017 13:08:01 +0000 Subject: [PATCH 19/20] console: don't display summary label when no status available yet --- clblob/console.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clblob/console.html b/clblob/console.html index 3035a42..efd92cc 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -181,6 +181,7 @@ var clusters = [] var allReplicasByName = {} var allReplicasByIP = {} +var summaryAvailable = false; // stateForReplica stores the last known state for a replica // its contents are: @@ -533,10 +534,12 @@ } function update_summary() { + summaryAvailable = true; iterate_sorted_object(summary, update_summary_metrics) } function generate_summary_div() { + if (!summaryAvailable) return []; return m(".group", m(".name", m("b", "summary")), iterate_sorted_object(summary, summary_metrics) From 0797a8d658fe56566fe60e0436e6b4913dd1ecf1 Mon Sep 17 00:00:00 2001 From: "Paul D. Fernhout" Date: Wed, 17 May 2017 14:00:10 +0000 Subject: [PATCH 20/20] console: improve error statusText display when xhr fails --- clblob/console.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clblob/console.html b/clblob/console.html index efd92cc..a60eae5 100644 --- a/clblob/console.html +++ b/clblob/console.html @@ -472,7 +472,14 @@ m.request({ url: "/_status/" + replica, method: "GET", - config: configXHRTimeout + config: configXHRTimeout, + extract: function(xhr) { + // If there is an error, return xhr with its statusText instead of the parsed JSON result + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) { + return JSON.parse(xhr.responseText); + } + return xhr; + } }) .then(update_status(replica)) .catch(update_status_fail(replica))