You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by st...@apache.org on 2012/04/20 13:34:43 UTC
svn commit: r1328330 - in /lucene/dev/trunk/solr/webapp/web:
css/styles/cloud.css img/ico/asterisk.png js/scripts/cloud.js tpl/cloud.html
Author: steffkes
Date: Fri Apr 20 11:34:43 2012
New Revision: 1328330
URL: http://svn.apache.org/viewvc?rev=1328330&view=rev
Log:
SOLR-3174: Visualize Cluster State
Added:
lucene/dev/trunk/solr/webapp/web/img/ico/asterisk.png (with props)
Modified:
lucene/dev/trunk/solr/webapp/web/css/styles/cloud.css
lucene/dev/trunk/solr/webapp/web/js/scripts/cloud.js
lucene/dev/trunk/solr/webapp/web/tpl/cloud.html
Modified: lucene/dev/trunk/solr/webapp/web/css/styles/cloud.css
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/webapp/web/css/styles/cloud.css?rev=1328330&r1=1328329&r2=1328330&view=diff
==============================================================================
--- lucene/dev/trunk/solr/webapp/web/css/styles/cloud.css (original)
+++ lucene/dev/trunk/solr/webapp/web/css/styles/cloud.css Fri Apr 20 11:34:43 2012
@@ -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;
}
\ No newline at end of file
Added: lucene/dev/trunk/solr/webapp/web/img/ico/asterisk.png
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/webapp/web/img/ico/asterisk.png?rev=1328330&view=auto
==============================================================================
Binary file - no diff available.
Modified: lucene/dev/trunk/solr/webapp/web/js/scripts/cloud.js
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/webapp/web/js/scripts/cloud.js?rev=1328330&r1=1328329&r2=1328330&view=diff
==============================================================================
--- lucene/dev/trunk/solr/webapp/web/js/scripts/cloud.js (original)
+++ lucene/dev/trunk/solr/webapp/web/js/scripts/cloud.js Fri Apr 20 11:34:43 2012
@@ -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 );
+
+ if( p.target.data.leader )
+ {
+ classes.push( 'leader' );
+ }
+
+ if( p.target.data.state )
+ {
+ classes.push( p.target.data.state );
+ }
+
+ return classes.join( ' ' );
+};
+
+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]);
+ .size([h, w - 400]);
var diagonal = d3.svg.diagonal()
- .projection(function(d) { return [d.y, d.x]; });
+ .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 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 r = 860 / 2;
+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 tree = d3.layout.tree()
- .size([360, r - 120])
- .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
+ 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 - 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; });
- //*/
+ .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 init_graph = function( graph_element )
+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] )
+ live_nodes[response.tree[0].children[c].data.title] = true;
+ }
+
+ $.ajax
+ (
{
- var nodes = [];
- for( var n in state[c][s] )
+ 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 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
+ 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 );
}
- };
- nodes.push( node );
- }
- var shard = {
- id: s,
- name: s,
- data: {
- type : 'shard',
- },
- children: nodes
- };
- shards.push( shard );
- }
+ var collection = {
+ name: c,
+ data: {
+ type : 'collection',
+ },
+ children: shards
+ };
+ collections.push( collection );
+ }
- var collection = {
- id: c,
- 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 );
- var graph_data = collections.shift();
- generate_graph( graph_element, graph_data );
+ 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' );
Modified: lucene/dev/trunk/solr/webapp/web/tpl/cloud.html
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/webapp/web/tpl/cloud.html?rev=1328330&r1=1328329&r2=1328330&view=diff
==============================================================================
--- lucene/dev/trunk/solr/webapp/web/tpl/cloud.html (original)
+++ lucene/dev/trunk/solr/webapp/web/tpl/cloud.html Fri Apr 20 11:34:43 2012
@@ -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>