SOLR-3174: Visualize Cluster State

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1328330 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Matheis 2012-04-20 11:34:43 +00:00
parent a441ffd1a2
commit 71cd0b00c1
4 changed files with 402 additions and 134 deletions

View File

@ -5,11 +5,12 @@
#content #cloud #navigation
{
width: 10%;
width: 11%;
}
#content #cloud #navigation .tree a { background-image: url( ../../img/ico/folder-tree.png ); }
#content #cloud #navigation .graph a { background-image: url( ../../img/ico/molecule.png ); }
#content #cloud #navigation .rgraph a { background-image: url( ../../img/ico/asterisk.png ); }
#content #cloud #navigation .dump a { background-image: url( ../../img/ico/download-cloud.png ); }
#content #cloud #navigation .dump
@ -25,7 +26,7 @@
#content #cloud #frame
{
float: right;
width: 89%;
width: 88%;
}
#content #cloud #frame .content
@ -249,17 +250,98 @@
color: #00f;
}
#content #graph-content .node
{
fill: #333;
}
#content #graph-content .node circle
{
fill: #f0f0f0;
fill: #fff;
stroke: #c0c0c0;
stroke-width: 1.5px;
}
#content #graph-content .node.lvl-2 text
{
cursor: pointer;
}
#content #graph-content .node.lvl-2:hover circle
{
stroke: #000 !important;
}
#content #graph-content .node.lvl-2:hover text
{
fill: #000 !important;
}
#content #graph-content .link
{
fill: none;
stroke: #c0c0c0;
stroke: #e0e0e0;
stroke-width: 1.5px;
}
#content #graph-content .node.gone circle,
#content #graph-content .link.gone
{
stroke: #f0f0f0;
}
#content #graph-content .node.gone text
{
fill: #f0f0f0;
}
#content #graph-content .node.recovery_failed circle
{
stroke: #C43C35;
}
#content #graph-content .node.recovery_failed text
{
fill: #C43C35;
}
#content #graph-content .node.down circle
{
stroke: #c48f00;
}
#content #graph-content .node.down text
{
fill: #c48f00;
}
#content #graph-content .node.recovering circle
{
stroke: #d5dd00;
}
#content #graph-content .node.recovering text
{
fill: #d5dd00;
}
#content #graph-content .node.active circle
{
stroke: #57A957;
}
#content #graph-content .node.active text
{
fill: #57A957;
}
#content #graph-content .node.leader circle
{
fill: #000;
}
#content #graph-content .link.lvl-1,
#content #graph-content .link.leader
{
stroke: #c0c0c0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

View File

@ -153,152 +153,300 @@ var init_debug = function( cloud_element )
);
};
var generate_graph = function( graph_element, graph_data )
var helper_path_class = function( p )
{
var w = 900,
h = 300;
var classes = [ 'link' ];
classes.push( 'lvl-' + p.target.depth );
var tree = d3.layout.tree()
.size([h, w - 400]);
if( p.target.data.leader )
{
classes.push( 'leader' );
}
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select("#canvas").append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(100, 0)");
var nodes = tree.nodes(graph_data);
var link = vis.selectAll("path.link")
.data(tree.links(nodes))
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = vis.selectAll("g.node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dx", function(d) { return d.children ? -8 : 8; })
.attr("dy", 3)
.attr("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return d.name; });
/*
var r = 860 / 2;
var tree = d3.layout.tree()
.size([360, r - 120])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
var vis = d3.select("#canvas").append("svg")
.attr("width", r * 2)
.attr("height", r * 2 - 150)
.append("g")
.attr("transform", "translate(" + r + "," + r + ")");
var nodes = tree.nodes(graph_data);
var link = vis.selectAll("path.link")
.data(tree.links(nodes))
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = vis.selectAll("g.node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
node.append("circle")
.attr("r", 4.5);
node.append("text")
.attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
.attr("dy", ".31em")
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
.text(function(d) { return d.name; });
//*/
if( p.target.data.state )
{
classes.push( p.target.data.state );
}
return classes.join( ' ' );
};
var init_graph = function( graph_element )
var helper_node_class = function( d )
{
var classes = [ 'node' ];
classes.push( 'lvl-' + d.depth );
if( d.data.leader )
{
classes.push( 'leader' );
}
if( d.data.state )
{
classes.push( d.data.state );
}
return classes.join( ' ' );
};
var helper_data = {
protocol: [],
host: [],
hostname: [],
port: [],
pathname: []
};
var helper_node_text = function( d )
{
if( !d.data.uri )
{
return d.name;
}
var name = d.data.uri.hostname;
if( 1 !== helper_data.protocol.length )
{
name = d.data.uri.protocol + '//' + name;
}
if( 1 !== helper_data.port.length )
{
name += ':' + d.data.uri.port;
}
if( 1 !== helper_data.pathname.length )
{
name += d.data.uri.pathname;
}
return name;
};
var generate_graph = function( graph_element, graph_data, leaf_count )
{
var w = graph_element.width(),
h = leaf_count * 20;
var tree = d3.layout.tree()
.size([h, w - 400]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select( '#canvas' ).append( 'svg' )
.attr( 'width', w )
.attr( 'height', h)
.append( 'g' )
.attr( 'transform', 'translate(100, 0)' );
var nodes = tree.nodes( graph_data );
var link = vis.selectAll( 'path.link' )
.data( tree.links( nodes ) )
.enter().append( 'path' )
.attr( 'class', helper_path_class )
.attr( 'd', diagonal );
var node = vis.selectAll( 'g.node' )
.data( nodes )
.enter().append( 'g' )
.attr( 'class', helper_node_class )
.attr( 'transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; } )
node.append( 'circle' )
.attr( 'r', 4.5 );
node.append( 'text' )
.attr( 'dx', function( d ) { return 0 === d.depth ? -8 : 8; } )
.attr( 'dy', function( d ) { return 5; } )
.attr( 'text-anchor', function( d ) { return 0 === d.depth ? 'end' : 'start'; } )
.attr( 'data-href', function( d ) { return d.name; } )
.text( helper_node_text );
$( 'text[data-href*="//"]', graph_element )
.die( 'click' )
.live
(
'click',
function()
{
location.href = $( this ).data( 'href' );
}
);
};
var generate_rgraph = function( graph_element, graph_data, leaf_count )
{
var max_val = Math.min( graph_element.width(), $( 'body' ).height() )
var r = max_val / 2;
var cluster = d3.layout.cluster()
.size([360, r - 160]);
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
var vis = d3.select( '#canvas' ).append( 'svg' )
.attr( 'width', r * 2 )
.attr( 'height', r * 2 )
.append( 'g' )
.attr( 'transform', 'translate(' + r + ',' + r + ')' );
var nodes = cluster.nodes( graph_data );
var link = vis.selectAll( 'path.link' )
.data( cluster.links( nodes ) )
.enter().append( 'path' )
.attr( 'class', helper_path_class )
.attr( 'd', diagonal );
var node = vis.selectAll( 'g.node' )
.data( nodes )
.enter().append( 'g' )
.attr( 'class', helper_node_class )
.attr( 'transform', function(d) { return 'rotate(' + (d.x - 90) + ')translate(' + d.y + ')'; } )
node.append( 'circle' )
.attr( 'r', 4.5 );
node.append( 'text' )
.attr( 'dx', function(d) { return d.x < 180 ? 8 : -8; } )
.attr( 'dy', '.31em' )
.attr( 'text-anchor', function(d) { return d.x < 180 ? 'start' : 'end'; } )
.attr( 'transform', function(d) { return d.x < 180 ? null : 'rotate(180)'; } )
.attr( 'data-href', function( d ) { return d.name; } )
.text( helper_node_text );
$( 'text[data-href*="//"]', graph_element )
.die( 'click' )
.live
(
'click',
function()
{
location.href = $( this ).data( 'href' );
}
);
};
var prepare_graph = function( graph_element, callback )
{
$.ajax
(
{
url : app.config.solr_path + '/zookeeper?wt=json&detail=true&path=%2Fclusterstate.json',
url : app.config.solr_path + '/zookeeper?wt=json&path=%2Flive_nodes',
dataType : 'json',
context : graph_element,
beforeSend : function( xhr, settings )
{
this
.show();
},
success : function( response, text_status, xhr )
{
var state = null;
eval( 'state = ' + response.znode.data + ';' );
var collections = [];
for( var c in state )
var live_nodes = {};
for( var c in response.tree[0].children )
{
var shards = [];
for( var s in state[c] )
{
var nodes = [];
for( var n in state[c][s] )
{
var node = {
id: state[c][s][n].node_name,
name: state[c][s][n].base_url,
data: {
type : 'node',
state : state[c][s][n].state,
leader : 'true' === state[c][s][n].leader
}
};
nodes.push( node );
}
var shard = {
id: s,
name: s,
data: {
type : 'shard',
},
children: nodes
};
shards.push( shard );
}
var collection = {
id: c,
name: c,
data: {
type : 'collection',
},
children: shards
};
collections.push( collection );
live_nodes[response.tree[0].children[c].data.title] = true;
}
var graph_data = collections.shift();
generate_graph( graph_element, graph_data );
$.ajax
(
{
url : app.config.solr_path + '/zookeeper?wt=json&detail=true&path=%2Fclusterstate.json',
dataType : 'json',
context : graph_element,
beforeSend : function( xhr, settings )
{
this
.show();
},
success : function( response, text_status, xhr )
{
var state = null;
eval( 'state = ' + response.znode.data + ';' );
var leaf_count = 0;
var collections = [];
for( var c in state )
{
var shards = [];
for( var s in state[c] )
{
var nodes = [];
for( var n in state[c][s] )
{
leaf_count++;
var uri = state[c][s][n].base_url;
var parts = uri.match( /^(\w+:)\/\/(([\w\d\.-]+)(:(\d+))?)(.+)$/ );
var uri_parts = {
protocol: parts[1],
host: parts[2],
hostname: parts[3],
port: parseInt( parts[5] || 80, 10 ),
pathname: parts[6]
};
helper_data.protocol.push( uri_parts.protocol );
helper_data.host.push( uri_parts.host );
helper_data.hostname.push( uri_parts.hostname );
helper_data.port.push( uri_parts.port );
helper_data.pathname.push( uri_parts.pathname );
var status = state[c][s][n].state;
if( !live_nodes[state[c][s][n].node_name] )
{
status = 'gone';
}
var node = {
name: uri,
data: {
type : 'node',
state : status,
leader : 'true' === state[c][s][n].leader,
uri : uri_parts
}
};
nodes.push( node );
}
var shard = {
name: s,
data: {
type : 'shard',
},
children: nodes
};
shards.push( shard );
}
var collection = {
name: c,
data: {
type : 'collection',
},
children: shards
};
collections.push( collection );
}
var graph_data = collections.shift();
helper_data.protocol = $.unique( helper_data.protocol );
helper_data.host = $.unique( helper_data.host );
helper_data.hostname = $.unique( helper_data.hostname );
helper_data.port = $.unique( helper_data.port );
helper_data.pathname = $.unique( helper_data.pathname );
callback( graph_element, graph_data, leaf_count );
},
error : function( xhr, text_status, error_thrown)
{
},
complete : function( xhr, text_status )
{
}
}
);
},
error : function( xhr, text_status, error_thrown)
{
@ -311,6 +459,30 @@ var init_graph = function( graph_element )
};
var init_graph = function( graph_element )
{
prepare_graph
(
graph_element,
function( graph_element, graph_data, leaf_count )
{
generate_graph( graph_element, graph_data, leaf_count );
}
);
}
var init_rgraph = function( graph_element )
{
prepare_graph
(
graph_element,
function( graph_element, graph_data, leaf_count )
{
generate_rgraph( graph_element, graph_data, leaf_count );
}
);
}
var init_tree = function( tree_element )
{
$.ajax
@ -531,6 +703,18 @@ sammy.get
}
);
$( '.rgraph', navigation_element )
.die( 'activate' )
.live
(
'activate',
function( event )
{
$( this ).addClass( 'current' );
init_rgraph( $( '#graph-content', cloud_element ) );
}
);
$( 'a[href="' + context.path + '"]', navigation_element )
.trigger( 'activate' );

View File

@ -47,6 +47,8 @@ limitations under the License.
<ul>
<li class="tree"><a href="#/~cloud">Tree</a></li>
<li class="graph"><a href="#/~cloud?view=graph">Graph</a></li>
<li class="rgraph"><a href="#/~cloud?view=rgraph">Graph (Radial)</a></li>
<li class="dump"><a href="#/~cloud">Dump</a></li>
</ul>