Loading app/controllers/ClusterOverviewController.scala +11 −0 Original line number Diff line number Diff line Loading @@ -84,4 +84,15 @@ class ClusterOverviewController extends BaseController { } } def relocateShard = process { (request, client) => val index = request.get("index") val shard = request.getInt("shard") val from = request.get("from") val to = request.get("to") val server = ElasticServer(request.host, request.authentication) client.relocateShard(shard, index, from, to, server).map { response => Status(response.status)(response.body) } } } app/elastic/ElasticClient.scala +20 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,26 @@ trait ElasticClient { execute(s"${target.host}$path", "GET", None, target.authentication) } def relocateShard(shard: Int, index: String, from: String, to: String, target: ElasticServer) = { val path = "/_cluster/reroute" val commands = s""" |{ | "commands": [ | { | "move": { | "shard": $shard, | "index": \"$index\", | "from_node": \"$from\", | "to_node": \"$to\" | } | } | ] |} """.stripMargin execute(s"${target.host}$path", "POST", Some(commands), target.authentication) } def getIndexRecovery(index: String, target: ElasticServer) = { val path = s"/$index/_recovery?active_only=true&human=true" execute(s"${target.host}$path", "GET", None, target.authentication) Loading conf/routes +1 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ POST /overview/clear_indices_cache @controllers.ClusterOvervi POST /overview/refresh_indices @controllers.ClusterOverviewController.refreshIndex POST /overview/delete_indices @controllers.ClusterOverviewController.deleteIndex POST /overview/get_shard_stats @controllers.ClusterOverviewController.getShardStats POST /overview/relocate_shard @controllers.ClusterOverviewController.relocateShard # Navbar module POST /navbar @controllers.NavbarController.index Loading public/css/app.css +2 −0 Original line number Diff line number Diff line Loading @@ -35,8 +35,10 @@ table.shard-map { table-layout: fixed; } .shard-started { border: 2px solid #1AC98E; color: #1AC98E; } .shard-initializing { border: 1px solid #1CA8DD; color: #1CA8DD; } .shard-relocated { border: 1px solid #9F85FF; color: #9F85FF; } .shard-relocating { border: 1px solid #9F85FF; color: #9F85FF; } .shard-recovering { border: 1px solid #E4D836; color: #E4D836; } .shard-unassigned { border: 1px solid #8B8F95; color: #8B8F95; } .shard-spot { border: 1px dashed; opacity: 0.7; padding-top: 2px; } .shard-replica { border: 1px dashed; opacity: 0.7; } .row-condensed { margin-right: -.1875rem !important; margin-left: -.1875rem !important; } .col-condensed { margin-left: 0px; margin-right: 0px; padding-left: 2px !important; padding-right: 2px !important; } Loading public/js/app.js +63 −1 Original line number Diff line number Diff line Loading @@ -684,7 +684,6 @@ angular.module('cerebro').controller('OverviewController', ['$scope', '$http', $scope.initializing_shards = 0; $scope.closed_indices = 0; $scope.special_indices = 0; $scope.expandedView = false; $scope.shardAllocation = true; $scope.indices_filter = new IndexFilter('', true, false, true, true, 0); Loading Loading @@ -940,6 +939,44 @@ angular.module('cerebro').controller('OverviewController', ['$scope', '$http', $location.path('index_settings').search('index', index); }; $scope.select = function(shard) { $scope.relocatingShard = shard; }; $scope.relocateShard = function(node) { var shard = $scope.relocatingShard; DataService.relocateShard(shard.shard, shard.index, shard.node, node.id, function(response) { $scope.relocatingShard = undefined; RefreshService.refresh(); AlertService.info('Relocation successfully started', response); }, function(error) { AlertService.error('Error while starting relocation', error); } ); }; $scope.canReceiveShard = function(index, node) { var shard = $scope.relocatingShard; if (shard && index) { // in case num indices < num columns if (shard.node !== node.id && shard.index === index.name) { var shards = index.shards[node.id]; if (shards) { var sameShard = function(s) { return s.shard === shard.shard; }; if (shards.filter(sameShard).length === 0) { return true; } } else { return true; } } } return false; }; }]); angular.module('cerebro').controller('RepositoriesController', ['$scope', Loading Loading @@ -2069,6 +2106,26 @@ angular.module('cerebro').directive('ngPlainInclude', function() { }; }); angular.module('cerebro').directive('ngShard', function() { return { scope: true, link: function(scope) { var shard = scope.shard; scope.state = shard.state.toLowerCase(); scope.replica = !shard.primary && shard.node; scope.id = shard.shard + '_' + shard.node + '_' + shard.index; scope.clazz = scope.replica ? 'shard-replica' : ''; scope.equal = function(other) { return other && shard.index === other.index && shard.node === other.node && shard.shard === other.shard; }; }, templateUrl: function() { return 'overview/shard.html'; } }; }); angular.module('cerebro').factory('AceEditorService', function() { this.init = function(name) { Loading Loading @@ -2349,6 +2406,11 @@ angular.module('cerebro').factory('DataService', ['$rootScope', '$timeout', request('/overview/get_shard_stats', data, success, error); }; this.relocateShard = function(shard, index, from, to, success, error) { var data = {shard: shard, index: index, from: from, to: to}; request('/overview/relocate_shard', data, success, error); }; // ---------- Create index ---------- this.createIndex = function(index, metadata, success, error) { var data = {index: index, metadata: metadata}; Loading Loading
app/controllers/ClusterOverviewController.scala +11 −0 Original line number Diff line number Diff line Loading @@ -84,4 +84,15 @@ class ClusterOverviewController extends BaseController { } } def relocateShard = process { (request, client) => val index = request.get("index") val shard = request.getInt("shard") val from = request.get("from") val to = request.get("to") val server = ElasticServer(request.host, request.authentication) client.relocateShard(shard, index, from, to, server).map { response => Status(response.status)(response.body) } } }
app/elastic/ElasticClient.scala +20 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,26 @@ trait ElasticClient { execute(s"${target.host}$path", "GET", None, target.authentication) } def relocateShard(shard: Int, index: String, from: String, to: String, target: ElasticServer) = { val path = "/_cluster/reroute" val commands = s""" |{ | "commands": [ | { | "move": { | "shard": $shard, | "index": \"$index\", | "from_node": \"$from\", | "to_node": \"$to\" | } | } | ] |} """.stripMargin execute(s"${target.host}$path", "POST", Some(commands), target.authentication) } def getIndexRecovery(index: String, target: ElasticServer) = { val path = s"/$index/_recovery?active_only=true&human=true" execute(s"${target.host}$path", "GET", None, target.authentication) Loading
conf/routes +1 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ POST /overview/clear_indices_cache @controllers.ClusterOvervi POST /overview/refresh_indices @controllers.ClusterOverviewController.refreshIndex POST /overview/delete_indices @controllers.ClusterOverviewController.deleteIndex POST /overview/get_shard_stats @controllers.ClusterOverviewController.getShardStats POST /overview/relocate_shard @controllers.ClusterOverviewController.relocateShard # Navbar module POST /navbar @controllers.NavbarController.index Loading
public/css/app.css +2 −0 Original line number Diff line number Diff line Loading @@ -35,8 +35,10 @@ table.shard-map { table-layout: fixed; } .shard-started { border: 2px solid #1AC98E; color: #1AC98E; } .shard-initializing { border: 1px solid #1CA8DD; color: #1CA8DD; } .shard-relocated { border: 1px solid #9F85FF; color: #9F85FF; } .shard-relocating { border: 1px solid #9F85FF; color: #9F85FF; } .shard-recovering { border: 1px solid #E4D836; color: #E4D836; } .shard-unassigned { border: 1px solid #8B8F95; color: #8B8F95; } .shard-spot { border: 1px dashed; opacity: 0.7; padding-top: 2px; } .shard-replica { border: 1px dashed; opacity: 0.7; } .row-condensed { margin-right: -.1875rem !important; margin-left: -.1875rem !important; } .col-condensed { margin-left: 0px; margin-right: 0px; padding-left: 2px !important; padding-right: 2px !important; } Loading
public/js/app.js +63 −1 Original line number Diff line number Diff line Loading @@ -684,7 +684,6 @@ angular.module('cerebro').controller('OverviewController', ['$scope', '$http', $scope.initializing_shards = 0; $scope.closed_indices = 0; $scope.special_indices = 0; $scope.expandedView = false; $scope.shardAllocation = true; $scope.indices_filter = new IndexFilter('', true, false, true, true, 0); Loading Loading @@ -940,6 +939,44 @@ angular.module('cerebro').controller('OverviewController', ['$scope', '$http', $location.path('index_settings').search('index', index); }; $scope.select = function(shard) { $scope.relocatingShard = shard; }; $scope.relocateShard = function(node) { var shard = $scope.relocatingShard; DataService.relocateShard(shard.shard, shard.index, shard.node, node.id, function(response) { $scope.relocatingShard = undefined; RefreshService.refresh(); AlertService.info('Relocation successfully started', response); }, function(error) { AlertService.error('Error while starting relocation', error); } ); }; $scope.canReceiveShard = function(index, node) { var shard = $scope.relocatingShard; if (shard && index) { // in case num indices < num columns if (shard.node !== node.id && shard.index === index.name) { var shards = index.shards[node.id]; if (shards) { var sameShard = function(s) { return s.shard === shard.shard; }; if (shards.filter(sameShard).length === 0) { return true; } } else { return true; } } } return false; }; }]); angular.module('cerebro').controller('RepositoriesController', ['$scope', Loading Loading @@ -2069,6 +2106,26 @@ angular.module('cerebro').directive('ngPlainInclude', function() { }; }); angular.module('cerebro').directive('ngShard', function() { return { scope: true, link: function(scope) { var shard = scope.shard; scope.state = shard.state.toLowerCase(); scope.replica = !shard.primary && shard.node; scope.id = shard.shard + '_' + shard.node + '_' + shard.index; scope.clazz = scope.replica ? 'shard-replica' : ''; scope.equal = function(other) { return other && shard.index === other.index && shard.node === other.node && shard.shard === other.shard; }; }, templateUrl: function() { return 'overview/shard.html'; } }; }); angular.module('cerebro').factory('AceEditorService', function() { this.init = function(name) { Loading Loading @@ -2349,6 +2406,11 @@ angular.module('cerebro').factory('DataService', ['$rootScope', '$timeout', request('/overview/get_shard_stats', data, success, error); }; this.relocateShard = function(shard, index, from, to, success, error) { var data = {shard: shard, index: index, from: from, to: to}; request('/overview/relocate_shard', data, success, error); }; // ---------- Create index ---------- this.createIndex = function(index, metadata, success, error) { var data = {index: index, metadata: metadata}; Loading