Live update of nodes list, grouped by node class
This commit is contained in:
parent
51cbec0565
commit
03d066a8d5
|
@ -20,3 +20,19 @@ module StringMap = Map.Make(String)
|
|||
module UuidSet = StringSet
|
||||
|
||||
let string_map_keys m = StringMap.fold (fun k _ acc -> k :: acc) m []
|
||||
|
||||
let classify f xs =
|
||||
let rec loop acc xs =
|
||||
match xs with
|
||||
| [] -> acc
|
||||
| x :: xs' ->
|
||||
(match f x with
|
||||
| Some (classification, v) ->
|
||||
loop
|
||||
(StringMap.add
|
||||
classification
|
||||
(v :: (try StringMap.find classification acc with Not_found -> []))
|
||||
acc)
|
||||
xs'
|
||||
| None -> loop acc xs')
|
||||
in loop StringMap.empty xs
|
||||
|
|
12
ui_main.ml
12
ui_main.ml
|
@ -75,7 +75,17 @@ let api_server_stats _ id r =
|
|||
|> Httpd.add_date_header
|
||||
|
||||
let api_nodes _ id r =
|
||||
Json.resp_ok [] (Json.Rec ["nodes", Json.Arr (List.map Json.str (Node.all_node_name_strings ()))])
|
||||
let by_class_name name =
|
||||
match Node.lookup name with
|
||||
| Some n -> Some (n.Node.class_name, name.Node.label)
|
||||
| None -> None
|
||||
in
|
||||
let info = classify by_class_name (Node.all_node_names ()) in
|
||||
Json.resp_ok []
|
||||
(Json.Rec
|
||||
(List.map
|
||||
(fun (class_name, node_names) -> (class_name, Json.Arr (List.map Json.str node_names)))
|
||||
(StringMap.bindings info)))
|
||||
|> Httpd.add_date_header
|
||||
|
||||
let api_node_info suffix id r =
|
||||
|
|
|
@ -6,24 +6,9 @@
|
|||
<script>$(document).ready(nodes_main);</script>
|
||||
|
||||
<body>
|
||||
<p class="pull-right">Filter nodes: <input id="node_filter" /></p>
|
||||
<h2>Nodes</h2>
|
||||
<div class="row truncate-overflow">
|
||||
<div class="span3">
|
||||
<ul id="nodes0">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<ul id="nodes1">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<ul id="nodes2">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<ul id="nodes3">
|
||||
</ul>
|
||||
</div>
|
||||
<div id="all_node_classes">
|
||||
</div>
|
||||
</body>
|
||||
</page>
|
||||
|
|
|
@ -29,24 +29,9 @@
|
|||
</div>
|
||||
</div></div></div>
|
||||
<div class="container"><body>
|
||||
<p class="pull-right">Filter nodes: <input id="node_filter"></p>
|
||||
<h2>Nodes</h2>
|
||||
<div class="row truncate-overflow">
|
||||
<div class="span3">
|
||||
<ul id="nodes0">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<ul id="nodes1">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<ul id="nodes2">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<ul id="nodes3">
|
||||
</ul>
|
||||
</div>
|
||||
<div id="all_node_classes">
|
||||
</div>
|
||||
</body></div>
|
||||
<script>$(document).ready(nodes_main);</script><script>Hop.install_tap({});</script><script src="bootstrap/js/bootstrap-transition.js"></script><script src="bootstrap/js/bootstrap-alert.js"></script><script src="bootstrap/js/bootstrap-modal.js"></script><script src="bootstrap/js/bootstrap-dropdown.js"></script><script src="bootstrap/js/bootstrap-scrollspy.js"></script><script src="bootstrap/js/bootstrap-tab.js"></script><script src="bootstrap/js/bootstrap-tooltip.js"></script><script src="bootstrap/js/bootstrap-popover.js"></script><script src="bootstrap/js/bootstrap-button.js"></script><script src="bootstrap/js/bootstrap-collapse.js"></script><script src="bootstrap/js/bootstrap-carousel.js"></script><script src="bootstrap/js/bootstrap-typeahead.js"></script>
|
||||
|
|
103
web/nodes.js
103
web/nodes.js
|
@ -1,16 +1,45 @@
|
|||
function refresh_node_list() {
|
||||
$.getJSON("/_/nodes", function (data) {
|
||||
var names = data.nodes;
|
||||
function nodes_main() {
|
||||
var known_nodes_by_class = {};
|
||||
|
||||
function category_column_template(class_name, n) {
|
||||
return '<div class="span3"><ul id="nodes'+class_name+'_'+n+'"></ul></div>';
|
||||
}
|
||||
|
||||
function category_template(class_name) {
|
||||
return '<div id="node_class_'+class_name+'"><h3>Class <tt>'+class_name+'</tt></h3>' +
|
||||
'<div class="row truncate-overflow">' +
|
||||
category_column_template(class_name, 0) +
|
||||
category_column_template(class_name, 1) +
|
||||
category_column_template(class_name, 2) +
|
||||
category_column_template(class_name, 3) +
|
||||
'</div></div>';
|
||||
}
|
||||
|
||||
function refresh_node_class(class_name) {
|
||||
var name_set = known_nodes_by_class[class_name] || {};
|
||||
var names = [];
|
||||
$.each(name_set, function (name) { names.push(name); });
|
||||
names.sort();
|
||||
var column_count = 4; /* change to match nodes.xml */
|
||||
|
||||
var column_count = 4; /* change to match category_template above */
|
||||
var per_column = Math.ceil(names.length / column_count);
|
||||
var column_index, column;
|
||||
function set_column(i) {
|
||||
column_index = i;
|
||||
column = $("#nodes" + i);
|
||||
column.html("");
|
||||
column = $("#nodes" + class_name + '_' + i);
|
||||
column.children().remove();
|
||||
}
|
||||
|
||||
if (!($("#node_class_"+class_name)[0])) {
|
||||
$("#all_node_classes").append($(category_template(class_name)));
|
||||
} else {
|
||||
for (var i = 0; i < column_count; i++) {
|
||||
set_column(i); // clears out old column contents
|
||||
}
|
||||
}
|
||||
|
||||
set_column(0);
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
if (i >= (column_index + 1) * per_column) {
|
||||
set_column(column_index + 1);
|
||||
|
@ -22,9 +51,65 @@ function refresh_node_list() {
|
|||
li.append(link);
|
||||
column.append(li);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function refresh_node_list() {
|
||||
$.getJSON("/_/nodes", function (data) {
|
||||
$("#all_node_classes").children().remove();
|
||||
|
||||
known_nodes_by_class = {};
|
||||
$.each(data, function (class_name, names) {
|
||||
var s = {};
|
||||
$.each(names, function (i, name) { s[name] = true; });
|
||||
known_nodes_by_class[class_name] = s;
|
||||
});
|
||||
$.each(known_nodes_by_class, refresh_node_class);
|
||||
});
|
||||
}
|
||||
|
||||
function on_node_added(node_name, node_class) {
|
||||
if (!(node_class in known_nodes_by_class)) {
|
||||
known_nodes_by_class[node_class] = {};
|
||||
}
|
||||
known_nodes_by_class[node_class][node_name] = true;
|
||||
refresh_node_class(node_class);
|
||||
}
|
||||
|
||||
function on_node_removed(node_name, node_class) {
|
||||
if (!(node_class in known_nodes_by_class)) {
|
||||
return;
|
||||
}
|
||||
delete known_nodes_by_class[node_class][node_name];
|
||||
refresh_node_class(node_class);
|
||||
}
|
||||
|
||||
function on_message(event) {
|
||||
var body = event.data;
|
||||
switch (body[0]) {
|
||||
case "post":
|
||||
switch (body[1]) {
|
||||
case "log_messages": {
|
||||
switch (body[2][0]) {
|
||||
case "Node bound":
|
||||
on_node_added(body[2][1], body[2][2]);
|
||||
break;
|
||||
case "Node unbound":
|
||||
on_node_removed(body[2][1], body[2][2]);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
function nodes_main() {
|
||||
Hop.$open_hooks.push(refresh_node_list);
|
||||
Hop.$open_hooks.push(function () {
|
||||
Hop.create("fanout", ["system.log"], "");
|
||||
Hop.subscribe("system.log", "", "log_messages", "");
|
||||
});
|
||||
Hop.$message_hooks.push(on_message);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue