| /* |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| solrAdminApp.controller('CloudController', |
| function($scope, $location, Zookeeper, Constants) { |
| |
| $scope.showDebug = false; |
| |
| $scope.$on("cloud-dump", function(event) { |
| $scope.showDebug = true; |
| }); |
| |
| $scope.closeDebug = function() { |
| $scope.showDebug = false; |
| } |
| |
| var view = $location.search().view ? $location.search().view : "graph"; |
| if (view == "tree") { |
| $scope.resetMenu("cloud-tree", Constants.IS_ROOT_PAGE); |
| treeSubController($scope, Zookeeper); |
| } else if (view == "rgraph") { |
| $scope.resetMenu("cloud-rgraph", Constants.IS_ROOT_PAGE); |
| graphSubController($scope, Zookeeper, true); |
| } else if (view == "graph") { |
| $scope.resetMenu("cloud-graph", Constants.IS_ROOT_PAGE); |
| graphSubController($scope, Zookeeper, false); |
| } |
| } |
| ); |
| |
| var treeSubController = function($scope, Zookeeper) { |
| $scope.showTree = true; |
| $scope.showGraph = false; |
| $scope.tree = {}; |
| $scope.showData = false; |
| |
| $scope.showTreeLink = function(link) { |
| var path = decodeURIComponent(link.replace(/.*[\\?&]path=([^&#]*).*/, "$1")); |
| Zookeeper.detail({path: path}, function(data) { |
| $scope.znode = data.znode; |
| var path = data.znode.path.split( '.' ); |
| if(path.length >1) { |
| $scope.lang = path.pop(); |
| } else { |
| var lastPathElement = data.znode.path.split( '/' ).pop(); |
| if (lastPathElement == "managed-schema") { |
| $scope.lang = "xml"; |
| } |
| } |
| $scope.showData = true; |
| }); |
| }; |
| |
| $scope.hideData = function() { |
| $scope.showData = false; |
| }; |
| |
| $scope.initTree = function() { |
| Zookeeper.simple(function(data) { |
| $scope.tree = data.tree; |
| }); |
| }; |
| |
| $scope.initTree(); |
| }; |
| |
| var graphSubController = function ($scope, Zookeeper, isRadial) { |
| $scope.showTree = false; |
| $scope.showGraph = true; |
| |
| $scope.filterType = "status"; |
| |
| $scope.helperData = { |
| protocol: [], |
| host: [], |
| hostname: [], |
| port: [], |
| pathname: [] |
| }; |
| |
| $scope.next = function() { |
| $scope.pos += $scope.rows; |
| $scope.initGraph(); |
| } |
| |
| $scope.previous = function() { |
| $scope.pos = Math.max(0, $scope.pos - $scope.rows); |
| $scope.initGraph(); |
| } |
| |
| $scope.resetGraph = function() { |
| $scope.pos = 0; |
| $scope.initGraph(); |
| } |
| |
| $scope.initGraph = function() { |
| Zookeeper.liveNodes(function (data) { |
| var live_nodes = {}; |
| for (var c in data.tree[0].children) { |
| live_nodes[data.tree[0].children[c].data.title] = true; |
| } |
| |
| var params = {view: "graph"}; |
| if ($scope.rows) { |
| params.start = $scope.pos; |
| params.rows = $scope.rows; |
| } |
| |
| var filter = ($scope.filterType=='status') ? $scope.pagingStatusFilter : $scope.pagingFilter; |
| |
| if (filter) { |
| params.filterType = $scope.filterType; |
| params.filter = filter; |
| } |
| |
| Zookeeper.clusterState(params, function (data) { |
| eval("var state=" + data.znode.data); // @todo fix horrid means to parse JSON |
| |
| var leaf_count = 0; |
| var graph_data = { |
| name: null, |
| children: [] |
| }; |
| |
| for (var c in state) { |
| var shards = []; |
| for (var s in state[c].shards) { |
| var nodes = []; |
| for (var n in state[c].shards[s].replicas) { |
| leaf_count++; |
| var replica = state[c].shards[s].replicas[n] |
| |
| var uri = replica.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] |
| }; |
| |
| $scope.helperData.protocol.push(uri_parts.protocol); |
| $scope.helperData.host.push(uri_parts.host); |
| $scope.helperData.hostname.push(uri_parts.hostname); |
| $scope.helperData.port.push(uri_parts.port); |
| $scope.helperData.pathname.push(uri_parts.pathname); |
| |
| var status = replica.state; |
| |
| if (!live_nodes[replica.node_name]) { |
| status = 'gone'; |
| } |
| |
| var node = { |
| name: uri, |
| data: { |
| type: 'node', |
| state: status, |
| leader: 'true' === replica.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 |
| }; |
| graph_data.children.push(collection); |
| } |
| |
| $scope.helperData.protocol = $.unique($scope.helperData.protocol); |
| $scope.helperData.host = $.unique($scope.helperData.host); |
| $scope.helperData.hostname = $.unique($scope.helperData.hostname); |
| $scope.helperData.port = $.unique($scope.helperData.port); |
| $scope.helperData.pathname = $.unique($scope.helperData.pathname); |
| |
| if (!isRadial && data.znode && data.znode.paging) { |
| $scope.showPaging = true; |
| |
| var parr = data.znode.paging.split('|'); |
| if (parr.length < 3) { |
| $scope.showPaging = false; |
| } else { |
| $scope.start = Math.max(parseInt(parr[0]), 0); |
| $scope.prevEnabled = ($scope.start > 0); |
| $scope.rows = parseInt(parr[1]); |
| $scope.total = parseInt(parr[2]); |
| |
| if ($scope.rows == -1) { |
| $scope.showPaging = false; |
| } else { |
| var filterType = parr.length > 3 ? parr[3] : ''; |
| |
| if (filterType == '' || filterType == 'none') filterType = 'status'; |
| |
| +$('#cloudGraphPagingFilterType').val(filterType); |
| |
| var filter = parr.length > 4 ? parr[4] : ''; |
| var page = Math.floor($scope.start / $scope.rows) + 1; |
| var pages = Math.ceil($scope.total / $scope.rows); |
| $scope.last = Math.min($scope.start + $scope.rows, $scope.total); |
| $scope.nextEnabled = ($scope.last < $scope.total); |
| } |
| } |
| } |
| else { |
| $scope.showPaging = false; |
| } |
| $scope.graphData = graph_data; |
| $scope.leafCount = leaf_count; |
| $scope.isRadial = isRadial; |
| }); |
| }); |
| }; |
| |
| $scope.initGraph(); |
| }; |
| |
| solrAdminApp.directive('graph', function(Constants) { |
| return { |
| restrict: 'EA', |
| scope: { |
| data: "=", |
| leafCount: "=", |
| helperData: "=", |
| isRadial: "=" |
| }, |
| link: function (scope, element, attrs) { |
| var helper_path_class = function (p) { |
| var classes = ['link']; |
| classes.push('lvl-' + p.target.depth); |
| |
| if (p.target.data && p.target.data.leader) { |
| classes.push('leader'); |
| } |
| |
| if (p.target.data && 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 && d.data.leader) { |
| classes.push('leader'); |
| } |
| |
| if (d.data && d.data.state) { |
| classes.push(d.data.state); |
| } |
| |
| return classes.join(' '); |
| }; |
| |
| var helper_node_text = function (d) { |
| if (!d.data || !d.data.uri) { |
| return d.name; |
| } |
| |
| var name = d.data.uri.hostname; |
| |
| if (1 !== scope.helperData.protocol.length) { |
| name = d.data.uri.protocol + '//' + name; |
| } |
| |
| if (1 !== scope.helperData.port.length) { |
| name += ':' + d.data.uri.port; |
| } |
| |
| if (1 !== scope.helperData.pathname.length) { |
| name += d.data.uri.pathname; |
| } |
| |
| return name; |
| }; |
| |
| scope.$watch("data", function(newValue, oldValue) { |
| if (newValue) { |
| if (scope.isRadial) { |
| radialGraph(element, scope.data, scope.leafCount); |
| } else { |
| flatGraph(element, scope.data, scope.leafCount); |
| } |
| } |
| }); |
| |
| |
| function setNodeNavigationBehavior(node, view){ |
| node |
| .attr('data-href', function (d) { |
| if (d.type == "node"){ |
| return getNodeUrl(d, view); |
| } |
| else{ |
| return ""; |
| } |
| }) |
| .on('click', function(d) { |
| if (d.data.type == "node"){ |
| location.href = getNodeUrl(d, view); |
| } |
| }); |
| } |
| |
| function getNodeUrl(d, view){ |
| var url = d.name + Constants.ROOT_URL + "#/~cloud"; |
| if (view != undefined){ |
| url += "?view=" + view; |
| } |
| return url; |
| } |
| |
| var flatGraph = function(element, graphData, leafCount) { |
| var w = element.width(), |
| h = leafCount * 20; |
| |
| var tree = d3.layout.tree().size([h, w - 400]); |
| |
| var diagonal = d3.svg.diagonal().projection(function (d) { |
| return [d.y, d.x]; |
| }); |
| |
| d3.select('#canvas', element).html(''); |
| var vis = d3.select('#canvas', element).append('svg') |
| .attr('width', w) |
| .attr('height', h) |
| .append('g') |
| .attr('transform', 'translate(100, 0)'); |
| |
| var nodes = tree.nodes(graphData); |
| |
| 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'; |
| }) |
| .text(helper_node_text); |
| |
| setNodeNavigationBehavior(node); |
| }; |
| |
| var radialGraph = function(element, graphData, leafCount) { |
| var max_val = Math.min(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]; |
| }); |
| |
| d3.select('#canvas', element).html(''); |
| 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(graphData); |
| |
| 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)'; |
| }) |
| .text(helper_node_text); |
| |
| setNodeNavigationBehavior(node, "rgraph"); |
| } |
| } |
| }; |
| }) |
| |
| /* |
| |
| ======================== |
| var init_debug = function( cloud_element ) |
| { |
| var debug_element = $( '#debug', cloud_element ); |
| var debug_button = $( '#menu #cloud .dump a' ); |
| |
| var clipboard_element = $( '.clipboard', debug_element ); |
| var clipboard_button = $( 'a', clipboard_element ); |
| |
| $( '.clipboard', debug_element ) |
| .die( 'click' ) |
| .live |
| ( |
| 'click', |
| function( event ) |
| { |
| return false; |
| } |
| ); |
| |
| url : app.config.solr_path + '/zookeeper?wt=json&dump=true', |
| ZeroClipboard.setMoviePath( 'img/ZeroClipboard.swf' ); |
| |
| clipboard_client = new ZeroClipboard.Client(); |
| |
| clipboard_client.addEventListener |
| ( |
| 'load', |
| function( client ) |
| { |
| } |
| ); |
| |
| clipboard_client.addEventListener |
| ( |
| 'complete', |
| function( client, text ) |
| { |
| clipboard_element |
| .addClass( 'copied' ); |
| |
| clipboard_button |
| .data( 'text', clipboard_button.text() ) |
| .text( clipboard_button.data( 'copied' ) ); |
| } |
| ); |
| }, |
| success : function( response, text_status, xhr ) |
| { |
| clipboard_client.glue |
| ( |
| clipboard_element.get(0), |
| clipboard_button.get(0) |
| ); |
| |
| clipboard_client.setText( response.replace( /\\/g, '\\\\' ) ); |
| |
| $( '.debug', debug_element ) |
| .removeClass( 'loader' ) |
| .text( response ); |
| }, |
| error : function( xhr, text_status, error_thrown ) |
| { |
| }, |
| complete : function( xhr, text_status ) |
| { |
| } |
| } |
| ); |
| } |
| ) |
| .die( 'hide' ) |
| .live |
| ( |
| 'hide', |
| function( event ) |
| { |
| $( '.debug', debug_element ) |
| .empty(); |
| |
| clipboard_element |
| .removeClass( 'copied' ); |
| |
| clipboard_button |
| .data( 'copied', clipboard_button.text() ) |
| .text( clipboard_button.data( 'text' ) ); |
| |
| clipboard_client.destroy(); |
| |
| debug_element.hide(); |
| } |
| ); |
| }; |
| |
| */ |