diff --git a/app/controllers/elasticsearch/GetShardStats.scala b/app/controllers/elasticsearch/GetShardStats.scala new file mode 100644 index 0000000000000000000000000000000000000000..b6e1b6682884426f0eef81fffa1de2aa5215ba72 --- /dev/null +++ b/app/controllers/elasticsearch/GetShardStats.scala @@ -0,0 +1,21 @@ +package controllers.elasticsearch + +import elastic.ElasticClient._ +import elastic.ElasticResponse +import models.ShardStats +import scala.concurrent.ExecutionContext.Implicits.global + +class GetShardStats extends ElasticsearchController { + + def index = processRequest { request => + val index = request.get("index") + val node = request.get("node") + val shard = request.getInt("shard") + getShardStats(index, request.host).zip(getIndexRecovery(index, request.host)).map { + case (indexStats, indexRecovery) => + val stats = ShardStats(index, node, shard, indexStats.body, indexRecovery.body) + ElasticResponse(200, stats) + } + } + +} diff --git a/app/elastic/ElasticClient.scala b/app/elastic/ElasticClient.scala index 843d3e945bbb51c1548f7d53a3ccc04bead63ecc..18c9ca698d40719f5df51ed6638bc55640897bed 100644 --- a/app/elastic/ElasticClient.scala +++ b/app/elastic/ElasticClient.scala @@ -4,6 +4,8 @@ import play.api.Play.current import play.api.libs.ws.WS import play.api.mvc.Results.EmptyContent +import scala.concurrent.Future + trait ElasticClient { def main(host: String) = @@ -69,6 +71,13 @@ trait ElasticClient { def disableShardAllocation(host: String) = putClusterSettings(allocationSettings("none"), host) + def getShardStats(index: String, host: String) = + ElasticResponse(WS.url(s"$host/$index/_stats?level=shards&human=true").get()) + + def getIndexRecovery(index: String, host: String) = + ElasticResponse(WS.url(s"$host/$index/_recovery?active_only=true&human=true").get()) + + } object ElasticClient extends ElasticClient diff --git a/app/models/CerebroRequest.scala b/app/models/CerebroRequest.scala index 27e0a95f2750d76b4fd5908ac6275299e35417f0..32b969137095fb95b970cdccd290326ec6aeab58 100644 --- a/app/models/CerebroRequest.scala +++ b/app/models/CerebroRequest.scala @@ -5,7 +5,11 @@ import play.api.libs.json.JsValue class CerebroRequest(val host: String, body: JsValue) { - def get(name: String) = (body \ name).asOpt[String].getOrElse(throw MissingRequiredParamException(name)) + def get(name: String) = + (body \ name).asOpt[String].getOrElse(throw MissingRequiredParamException(name)) + + def getInt(name: String) = + (body \ name).asOpt[Int].getOrElse(throw MissingRequiredParamException(name)) def getArray(name: String) = (body \ name).asOpt[Array[String]].getOrElse(throw MissingRequiredParamException(name)) diff --git a/app/models/ShardStats.scala b/app/models/ShardStats.scala new file mode 100644 index 0000000000000000000000000000000000000000..193762002bea1ccbb0bcdbeeea9c2ed065e22b7e --- /dev/null +++ b/app/models/ShardStats.scala @@ -0,0 +1,30 @@ +package models + +import play.api.libs.json.{JsArray, JsNull, JsValue} + +object ShardStats { + + def apply(index: String, node: String, shard: Int, stats: JsValue, recovery: JsValue): JsValue = { + val shardStats = getShardStats(index, node, shard, stats) + shardStats.getOrElse(getShardRecovery(index, node, shard, recovery).getOrElse(JsNull)) + } + + private def getShardStats(index: String, node: String, shard: Int, stats: JsValue): Option[JsValue] = + (stats \ "indices" \ index \ "shards" \ shard.toString).asOpt[JsArray] match { + case Some(JsArray(shards)) => shards.collectFirst { + case s if (s \ "routing" \ "node").as[String].equals(node) => s + } + case _ => None + } + + + private def getShardRecovery(index: String, node: String, shard: Int, recovery: JsValue): Option[JsValue] = + (recovery \ "index" \ "shards").asOpt[JsArray] match { + case Some(JsArray(recoveries)) => recoveries.collectFirst { + case r if (r \ "target" \ "id").as[String].equals(node) && (r \ "id").as[Int].equals(shard) => r + } + case _ => None + } + + +} diff --git a/conf/routes b/conf/routes index ca4c9ec93a2d365a966ce292b6745ee29fd9e017..781aa5d6882504d295a8b83cda5e9a9386a943a8 100644 --- a/conf/routes +++ b/conf/routes @@ -21,6 +21,7 @@ POST /apis/update_cluster_settings @controllers.elasticsearch.PutClust POST /apis/get_node_stats @controllers.elasticsearch.NodeStatsController.index POST /apis/disable_shard_allocation @controllers.elasticsearch.DisableShardAllocationController.index POST /apis/enable_shard_allocation @controllers.elasticsearch.EnableShardAllocationController.index +POST /apis/get_shard_stats @controllers.elasticsearch.GetShardStats.index GET /apis/hosts @controllers.HostsController.index diff --git a/public/app.js b/public/app.js index 2849b29d9466fba6db5f6c6234b159d123cb5284..4e40437811b1477b1bae488dff7ef9985486ab89 100644 --- a/public/app.js +++ b/public/app.js @@ -327,6 +327,10 @@ angular.module('cerebro').controller('OverviewController', ['$scope', '$http', ' ); }; + $scope.shardStats = function(index, node, shard) { + DataService.getShardStats(index, node, shard, displayInfo, error); + }; + $scope.nodeStats = function(node) { DataService.nodeStats(node, displayInfo, error); }; @@ -983,6 +987,22 @@ angular.module('cerebro').factory('DataService', function ($rootScope, $timeout, error(error); }; + this.getShardStats = function(index, node, shard, success, error) { + var config = { + method: 'POST', + url: baseUrl + '/apis/get_shard_stats', + data: { + host: host, + index: index, + node: node, + shard: shard + } + }; + $http(config). + success(success). + error(error); + }; + this.getHosts = function (success, error) { var config = { method: 'GET', diff --git a/public/overview.html b/public/overview.html index c33d663adde57bbaa0f55f0201a2a99f01f3a576..8ea17c44547a143bbf5fca8f96c754b018ce78be 100644 --- a/public/overview.html +++ b/public/overview.html @@ -144,7 +144,9 @@ - + + {{shard.shard}} + @@ -192,7 +194,9 @@ - + + {{shard.shard}} + diff --git a/src/controllers/overview.js b/src/controllers/overview.js index 8aabaa53ec7bcc63a9ccbc2dd182f2ab3a11a8c8..7887fbc0883857f9c6f22ab6b99f803b053d4987 100644 --- a/src/controllers/overview.js +++ b/src/controllers/overview.js @@ -209,6 +209,10 @@ angular.module('cerebro').controller('OverviewController', ['$scope', '$http', ' ); }; + $scope.shardStats = function(index, node, shard) { + DataService.getShardStats(index, node, shard, displayInfo, error); + }; + $scope.nodeStats = function(node) { DataService.nodeStats(node, displayInfo, error); }; diff --git a/src/services/data.js b/src/services/data.js index 3d7feb38bc4ef98b6c289b8085324acbf2a0a207..7704cd6dd70d9b6ea737e1be9e81adc55ba7a196 100644 --- a/src/services/data.js +++ b/src/services/data.js @@ -208,6 +208,22 @@ angular.module('cerebro').factory('DataService', function ($rootScope, $timeout, error(error); }; + this.getShardStats = function(index, node, shard, success, error) { + var config = { + method: 'POST', + url: baseUrl + '/apis/get_shard_stats', + data: { + host: host, + index: index, + node: node, + shard: shard + } + }; + $http(config). + success(success). + error(error); + }; + this.getHosts = function (success, error) { var config = { method: 'GET',