eagle-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From h..@apache.org
Subject eagle git commit: [EAGLE-908] Hdfs JMX Monitoring Dashboard
Date Wed, 22 Feb 2017 12:58:14 GMT
Repository: eagle
Updated Branches:
  refs/heads/master 86eed4f56 -> 610b9f2ec


[EAGLE-908] Hdfs JMX Monitoring Dashboard

[EAGLE-908] Hdfs JMX Monitoring Dashboard
- Hdfs overview page, use tab to show multiple active namenode, hdfs summary is displayed at the upper portion of the page.

https://issues.apache.org/jira/browse/EAGLE-908

Author: chitin <chitin1027@gmail.com>

Closes #836 from chitin/eagle908-2.


Project: http://git-wip-us.apache.org/repos/asf/eagle/repo
Commit: http://git-wip-us.apache.org/repos/asf/eagle/commit/610b9f2e
Tree: http://git-wip-us.apache.org/repos/asf/eagle/tree/610b9f2e
Diff: http://git-wip-us.apache.org/repos/asf/eagle/diff/610b9f2e

Branch: refs/heads/master
Commit: 610b9f2ecc1221c6aee2243615860a86a3e16593
Parents: 86eed4f
Author: chitin <chitin1027@gmail.com>
Authored: Wed Feb 22 20:58:08 2017 +0800
Committer: Hao Chen <hao@apache.org>
Committed: Wed Feb 22 20:58:08 2017 +0800

----------------------------------------------------------------------
 .../app/apps/hbase/widgets/availabilityChart.js |   8 +-
 .../src/main/webapp/app/apps/hdfs/config.json   |  34 +++
 .../app/apps/hdfs/ctrl/datanodeDetailCtrl.js    | 221 ++++++++++++++
 .../app/apps/hdfs/ctrl/datanodeListCtrl.js      |  38 +++
 .../app/apps/hdfs/ctrl/namenodeListCtrl.js      |  35 +++
 .../main/webapp/app/apps/hdfs/ctrl/overview.js  | 263 ++++++++++++++++-
 .../src/main/webapp/app/apps/hdfs/index.js      | 291 ++++++++++++++++++-
 .../app/apps/hdfs/partials/datanodeDetail.html  | 117 ++++++++
 .../app/apps/hdfs/partials/datanodeList.html    |  53 ++++
 .../app/apps/hdfs/partials/namenodeList.html    |  52 ++++
 .../webapp/app/apps/hdfs/partials/overview.html | 106 ++++++-
 .../app/apps/hdfs/widget/availabilityChart.js   | 114 +++++---
 12 files changed, 1276 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hbase-web/src/main/webapp/app/apps/hbase/widgets/availabilityChart.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hbase-web/src/main/webapp/app/apps/hbase/widgets/availabilityChart.js b/eagle-metric/eagle-hbase-web/src/main/webapp/app/apps/hbase/widgets/availabilityChart.js
index bd0a278..c74dece 100644
--- a/eagle-metric/eagle-hbase-web/src/main/webapp/app/apps/hbase/widgets/availabilityChart.js
+++ b/eagle-metric/eagle-hbase-web/src/main/webapp/app/apps/hbase/widgets/availabilityChart.js
@@ -21,12 +21,6 @@
 	 * `register` without params will load the module which using require
 	 */
 	register(function (hadoopMetricApp) {
-		var COLOR_MAPPING = {
-			HDFS: 'orange',
-			HBase: 'yellow',
-			Yarn: 'green'
-		};
-
 		hadoopMetricApp.directive("hadoopMetricWidget", function () {
 			return {
 				restrict: 'AE',
@@ -52,7 +46,7 @@
 					$scope.type = $attrs.type;
 
 					// Customize chart color
-					$scope.bgColor = COLOR_MAPPING[$scope.type];
+					$scope.bgColor = "yellow";
 
 					function countHBaseRole(site, status, role, groups, filed, limit) {
 						var jobCond = {

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/config.json
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/config.json b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/config.json
new file mode 100644
index 0000000..a74e803
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/config.json
@@ -0,0 +1,34 @@
+{
+	"namenode": {
+		"heap" : "hadoop.memory.heapmemoryusage.used",
+		"nonheap" : "hadoop.memory.nonheapmemoryusage.used",
+		"fsnamesystemstate" : "hadoop.namenode.fsnamesystemstate.capacitytotal",
+		"capacityused" : "hadoop.namenode.dfs.capacityused",
+		"capacityremaining" : "hadoop.namenode.dfs.capacityremaining",
+		"blockstotal" : "hadoop.namenode.dfs.blockstotal",
+		"filestotal" : "hadoop.namenode.dfs.filestotal",
+		"underreplicatedblocks" : "hadoop.namenode.dfs.underreplicatedblocks",
+		"missingblocks" : "hadoop.namenode.dfs.missingblocks",
+		"corruptblocks" : "hadoop.namenode.dfs.corruptblocks",
+		"lastcheckpointtime" : "hadoop.namenode.dfs.lastcheckpointtime",
+		"transactionssincelastcheckpoint" : "hadoop.namenode.dfs.transactionssincelastcheckpoint",
+		"lastwrittentransactionid" : "hadoop.namenode.dfs.lastwrittentransactionid",
+		"snapshottabledirectories" : "hadoop.namenode.dfs.snapshottabledirectories",
+		"snapshots" : "hadoop.namenode.dfs.snapshots",
+		"rpcqueuetimeavgtime" : "hadoop.namenode.rpc.rpcqueuetimeavgtime",
+		"rpcprocessingtimeavgtime" : "hadoop.namenode.rpc.rpcprocessingtimeavgtime",
+		"numopenconnections" : "hadoop.namenode.rpc.numopenconnections",
+		"callqueuelength" : "hadoop.namenode.rpc.callqueuelength"
+	},
+	"datanode": {
+		"nonheap" : "hadoop.memory.nonheapmemoryusage.used",
+		"heap" : "hadoop.memory.heapmemoryusage.used",
+		"capacity" : "hadoop.datanode.fsdatasetstate.capacity",
+		"dfsused" : "hadoop.datanode.fsdatasetstate.dfsused",
+		"xceivercount" : "hadoop.datanode.datanodeinfo.xceivercount",
+		"rpcqueuetimeavgtime" : "hadoop.datanode.rpc.rpcqueuetimeavgtime",
+		"rpcprocessingtimeavgtime" : "hadoop.datanode.rpc.rpcprocessingtimeavgtime",
+		"numopenconnections" : "hadoop.datanode.rpc.numopenconnections",
+		"callqueuelength" : "hadoop.datanode.rpc.callqueuelength"
+	}
+}

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeDetailCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeDetailCtrl.js b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeDetailCtrl.js
new file mode 100644
index 0000000..cdb9ad5
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeDetailCtrl.js
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+(function () {
+	/**
+	 * `register` without params will load the module which using require
+	 */
+	register(function (hdfsMetricApp) {
+		hdfsMetricApp.controller("datanodeDetailCtrl", function ($q, $wrapState, $scope, PageConfig, Time, HDFSMETRIC) {
+			var cache = {};
+			$scope.site = $wrapState.param.siteId;
+			$scope.hostname = $wrapState.param.hostname;
+			PageConfig.title = 'Datanode ' + "(" + $scope.hostname + ")";
+			Time.autoRefresh = false;
+
+			var sizeoption = {
+				animation: false,
+				tooltip: {
+					formatter: function (points) {
+						return points[0].name + "<br/>" +
+							$.map(points, function (point) {
+								return '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+									point.seriesName + ": " +
+									common.number.abbr(point.value, true);
+							}).reverse().join("<br/>");
+					}
+				},
+				legend: {
+					x: 'center', y: 'bottom'
+				},
+				areaStyle: {normal: {}},
+				yAxis: [{
+					axisLabel: {
+						formatter: function (value) {
+							return common.number.abbr(value, true);
+						}
+					}
+				}]
+			};
+			var digitalOption = {
+				animation: false,
+				tooltip: {
+					formatter: function (points) {
+						return points[0].name + "<br/>" +
+							$.map(points, function (point) {
+								return '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+									point.seriesName + ": " +
+									common.number.abbr(point.value, false, 0);
+							}).reverse().join("<br/>");
+					}
+				},
+				yAxis: [{
+					axisLabel: {
+						formatter: function (value) {
+							return common.number.abbr(value, false);
+						}
+					}
+				}]
+			};
+
+			var gctimeoption = {
+				legend: {
+					x: 'center', y: 'bottom'
+				},
+				yAxis: [{
+					axisLabel: {
+						formatter: function (value) {
+							return value / 1000 + ' S';
+						}
+					}
+				}]
+			};
+			$scope.chartList = [
+				{
+					name: "Memory Usage",
+					metrics: ["nonheap", "heap"],
+					option: sizeoption
+				},
+				{
+					name: "Fsdatasetstate",
+					metrics: ["capacity", "dfsused"],
+					option: {}
+				},
+				{
+					name: "Xceivercount",
+					metrics: ["xceivercount"],
+					option: {}
+				},
+				{
+					name: "Rpcqueuetimeavgtime",
+					metrics: ["rpcqueuetimeavgtime"],
+					option: gctimeoption
+				},
+				{
+					name: "Rpcprocessingtimeavgtime",
+					metrics: ["rpcprocessingtimeavgtime"],
+					option: gctimeoption
+				},
+				{
+					name: "Numopenconnections",
+					metrics: ["numopenconnections"],
+					option: {}
+				},
+				{
+					name: "Callqueuelength",
+					metrics: ["callqueuelength"],
+					option: {}
+				}
+			];
+
+			$scope.metricList = [];
+			$.each($scope.chartList, function (i) {
+				var chart = $scope.chartList[i];
+				var chartname = chart.name;
+				$scope.metricList[chartname] = {
+					title: chartname,
+					series: {},
+					option: {},
+					loading: true,
+					promises: []
+				};
+			});
+			$scope.refresh = function () {
+				var startTime = Time.startTime();
+				var endTime = Time.endTime();
+
+				HDFSMETRIC.getMetricObj().then(function (res) {
+					var masterMetricList = res.datanode;
+					$.each($scope.chartList, function (i) {
+						var chart = $scope.chartList[i];
+						var metricList = chart.metrics;
+						$.each(metricList, function (j) {
+							var metricKey = metricList[j];
+							var metricspromies = generateHdfsMetric(masterMetricList[metricKey], startTime, endTime, metricKey);
+							var chartname = chart.name;
+							$scope.metricList[chartname].promises.push(metricspromies);
+						});
+					});
+
+					$.each($scope.chartList, function (k) {
+						var chart = $scope.chartList[k];
+						var chartname = chart.name;
+						$q.all($scope.metricList[chartname].promises).then(function (resp) {
+							var series = [];
+							for (var r = 0; r < resp.length; r += 1) {
+								var rs = resp[r][1];
+								if (rs.length > 0) {
+									series.push(rs);
+								}
+							}
+							$scope.metricList[chartname] = mergeSeries(chartname, series, chart.metrics, chart.option);
+						});
+					});
+				});
+
+				HDFSMETRIC.getStatusByRoleAndHost("HdfsServiceInstance",$scope.hostname, "datanode", $scope.site)._promise.then(function (res) {
+					$scope.datanodestatus = res;
+				});
+			};
+			Time.onReload(function () {
+				cache = {};
+				$scope.refresh();
+			}, $scope);
+			$scope.refresh();
+
+
+			function generateHdfsMetric(name, startTime, endTime, flag) {
+				var interval = Time.diffInterval(startTime, endTime);
+				var intervalMin = interval / 1000 / 60;
+				var trendStartTime = Time.align(startTime, interval);
+				var trendEndTime = Time.align(endTime, interval);
+
+				var condition = {
+					site: $scope.site,
+					component: "regionserver",
+					host: $scope.hostname
+				};
+				return HDFSMETRIC.aggMetricsToEntities(HDFSMETRIC.hadoopMetricsAggregation(condition, name, ["site"], "avg(value)", intervalMin, trendStartTime, trendEndTime), flag)
+					._promise.then(function (list) {
+						var metricFlag = $.map(list, function (metrics) {
+							return metrics[0].flag;
+						});
+						return [metricFlag, list];
+					});
+			}
+
+			function mergeSeries(title, metrics, linename, option) {
+				var series = [];
+				$.each(metrics, function (i, metricMap) {
+					$.map(metricMap, function (metric) {
+						if (typeof metric !== 'undefined') {
+							series.push(HDFSMETRIC.metricsToSeries(linename[i], metric, option));
+						}
+					});
+				});
+				return {
+					title: title,
+					series: series,
+					option: option || {},
+					loading: false
+				};
+			}
+		});
+	});
+})
+();

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeListCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeListCtrl.js b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeListCtrl.js
new file mode 100644
index 0000000..aee0536
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/datanodeListCtrl.js
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+(function () {
+	/**
+	 * `register` without params will load the module which using require
+	 */
+	register(function (hdfsMetricApp) {
+
+		hdfsMetricApp.controller("datanodeListCtrl", function ($wrapState, $scope, PageConfig, HDFSMETRIC) {
+
+			// Initialization
+			PageConfig.title = "HDFS Datanode";
+			$scope.tableScope = {};
+			$scope.live = HDFSMETRIC.STATUS_LIVE;
+			$scope.dead = HDFSMETRIC.STATUS_DEAD;
+			$scope.site = $wrapState.param.siteId;
+			$scope.searchPathList = [["tags", "hostname"], ["tags", "rack"], ["tags", "site"], ["status"]];
+			$scope.datanodeList = HDFSMETRIC.getListByRoleName("HdfsServiceInstance", "datanode", $scope.site);
+
+		});
+	});
+})();

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/namenodeListCtrl.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/namenodeListCtrl.js b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/namenodeListCtrl.js
new file mode 100644
index 0000000..3e4ea41
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/namenodeListCtrl.js
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+(function () {
+	/**
+	 * `register` without params will load the module which using require
+	 */
+	register(function (hdfsMetricApp) {
+
+		hdfsMetricApp.controller("namenodeListCtrl", function ($wrapState, $scope, PageConfig, HDFSMETRIC) {
+
+			// Initialization
+			PageConfig.title = "HDFS Namenode List";
+			$scope.tableScope = {};
+			$scope.site = $wrapState.param.siteId;
+			$scope.searchPathList = [["tags", "hostname"], ["tags", "rack"], ["tags", "site"], ["status"]];
+			$scope.namenodeList = HDFSMETRIC.getListByRoleName("HdfsServiceInstance", "namenode", $scope.site);
+		});
+	});
+})();

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/overview.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/overview.js b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/overview.js
index b8d57a7..d77986a 100644
--- a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/overview.js
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/ctrl/overview.js
@@ -21,9 +21,268 @@
 	 * `register` without params will load the module which using require
 	 */
 	register(function (hdfsMetricApp) {
-		hdfsMetricApp.controller("overviewCtrl", function (PageConfig) {
-			PageConfig.title = 'Hdfs';
+		hdfsMetricApp.controller("overviewCtrl", function ($q, $wrapState, $scope, PageConfig, HDFSMETRIC, Time) {
+			PageConfig.title = 'HDFS';
 
+			var cache = {};
+			$scope.site = $wrapState.param.siteId;
+
+			var namenodeInfo = HDFSMETRIC.getListByRoleName("HdfsServiceInstance", "namenode", $scope.site);
+
+			$scope.switchNamenode = function (namenode) {
+				$scope.metricList = [];
+				$.each($scope.chartList, function (i) {
+					var chart = $scope.chartList[i];
+					var chartname = chart.name;
+					$scope.metricList[chartname] = {
+						title: chartname,
+						series: {},
+						option: {},
+						loading: true,
+						promises: []
+					};
+				});
+				$scope.namenode = namenode;
+				$scope.refresh();
+			};
+
+			var storageOption = {
+				animation: false,
+				tooltip: {
+					formatter: function (points) {
+						return points[0].name + "<br/>" +
+							$.map(points, function (point) {
+								return '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+									point.seriesName + ": " +
+									common.number.abbr(point.value, true, 0);
+							}).reverse().join("<br/>");
+					}
+				},
+				yAxis: [{
+					axisLabel: {
+						formatter: function (value) {
+							return common.number.abbr(value, true);
+						}
+					}
+				}]
+			};
+
+			var digitalOption = {
+				animation: false,
+				tooltip: {
+					formatter: function (points) {
+						return points[0].name + "<br/>" +
+							$.map(points, function (point) {
+								return '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + point.color + '"></span> ' +
+									point.seriesName + ": " +
+									common.number.abbr(point.value, false, 0);
+							}).reverse().join("<br/>");
+					}
+				},
+				yAxis: [{
+					axisLabel: {
+						formatter: function (value) {
+							return common.number.abbr(value, false);
+						}
+					}
+				}]
+			};
+			var startTime = Time.startTime();
+			var endTime = Time.endTime();
+			var interval = Time.diffInterval(startTime, endTime);
+			var intervalMin = interval / 1000 / 60;
+			var trendStartTime = Time.align(startTime, interval);
+			var trendEndTime = Time.align(endTime, interval);
+			$scope.site = $wrapState.param.siteId;
+			function generateHdfsMetric(name, flag) {
+				var result = cache[name] || namenodeInfo._promise.then(function (res) {
+						$scope.activeNamenodeList = res;
+						$scope.type = $wrapState.param.hostname || $scope.namenode || res[0].tags.hostname;
+						var hostname = $scope.namenode || res[0].tags.hostname;
+						var jobCond = {
+							site: $scope.site,
+							component: "namenode",
+							host: hostname
+						};
+						return HDFSMETRIC.aggMetricsToEntities(HDFSMETRIC.hadoopMetricsAggregation(jobCond, name, ["site"], "avg(value)", intervalMin, trendStartTime, trendEndTime), flag)
+							._promise.then(function (list) {
+								var metricFlag = $.map(list, function (metrics) {
+									return metrics[0].flag;
+								});
+								return [metricFlag, list];
+							});
+					});
+				return result;
+			}
+
+			function sumMetrics(site, role, groups, filed, limit) {
+				var jobCond = {
+					site: site,
+					role: role
+				};
+				return HDFSMETRIC.aggHadoopInstance("HdfsServiceInstance", jobCond, groups, filed, limit);
+			}
+
+			$scope.chartList = [
+				{
+					name: "MemoryUsage",
+					metrics: ["nonheap", "heap"],
+					linename: ["nonheap", "heap"],
+					option: storageOption
+				},
+				{
+					name: "DFSCapacity",
+					metrics: ["fsnamesystemstate", "capacityused", "capacityremaining"],
+					linename: ["total", "used", "remainning"],
+					option: storageOption
+				},
+				{
+					name: "Blocks",
+					metrics: ["blockstotal", "missingblocks", "corruptblocks"],
+					linename: ["blockstotal", "missingblocks", "corruptblocks"],
+					option: digitalOption
+				},
+				{
+					name: "Filestotal",
+					metrics: ["filestotal"],
+					linename: ["filestotal"],
+					option: digitalOption
+				},
+				{
+					name: "Underreplicatedblocks",
+					metrics: ["underreplicatedblocks"],
+					linename: ["underreplicatedblocks"],
+					option: {}
+				},
+				{
+					name: "LastCheckpointTime",
+					metrics: ["lastcheckpointtime"],
+					linename: ["lastcheckpointtime"],
+					option: digitalOption
+				},
+				{
+					name: "TransactionsSinceLastCheckpoint",
+					metrics: ["transactionssincelastcheckpoint"],
+					linename: ["transactionssincelastcheckpoint"],
+					option: digitalOption
+				},
+				{
+					name: "LastWrittenTransactionId",
+					metrics: ["lastwrittentransactionid"],
+					linename: ["lastwrittentransactionid"],
+					option: digitalOption
+				},
+				{
+					name: "SnapshottableDirectories",
+					metrics: ["snapshottabledirectories"],
+					linename: ["snapshottabledirectories"],
+					option: {}
+				},
+				{
+					name: "Snapshots",
+					metrics: ["snapshots"],
+					linename: ["snapshots"],
+					option: {}
+				},
+				{
+					name: "RpcAvgTime",
+					metrics: ["rpcqueuetimeavgtime", "rpcprocessingtimeavgtime"],
+					linename: ["queuetime", "processingtime"],
+					option: {}
+				},
+				{
+					name: "NumOpenConnections",
+					metrics: ["numopenconnections"],
+					linename: ["numopenconnections"],
+					option: {}
+				},
+				{
+					name: "CallQueueLength",
+					metrics: ["callqueuelength"],
+					linename: ["callqueuelength"],
+					option: {}
+				}
+			];
+
+			$scope.metricList = [];
+			$.each($scope.chartList, function (i) {
+				var chart = $scope.chartList[i];
+				var chartname = chart.name;
+				$scope.metricList[chartname] = {
+					title: chartname,
+					series: {},
+					option: {},
+					loading: true,
+					promises: []
+				};
+			});
+
+			$scope.refresh = function () {
+				HDFSMETRIC.getMetricObj().then(function(res) {
+					var namenodeMetricList = res.namenode;
+					$.each($scope.chartList, function (i) {
+						var chart = $scope.chartList[i];
+						var metricList = chart.metrics;
+						$.each(metricList, function (j) {
+							var metricKey = metricList[j];
+							var metricpromise = generateHdfsMetric(namenodeMetricList[metricKey], metricKey);
+							var chartname = chart.name;
+							$scope.metricList[chartname].promises.push(metricpromise);
+						});
+					});
+					$.each($scope.chartList, function (k) {
+						var chart = $scope.chartList[k];
+						var chartname = chart.name;
+						$q.all($scope.metricList[chartname].promises).then(function (resp) {
+							var series = [];
+							for(var r=0; r < resp.length; r+=1) {
+								var rs = resp[r][1];
+								if(rs.length > 0) {
+									series.push(rs);
+								}
+							}
+							$scope.metricList[chartname] = HDFSMETRIC.mergeMetricToOneSeries(chartname, series, chart.linename, chart.option);
+						});
+					});
+				});
+
+				HDFSMETRIC.countHadoopRole("HdfsServiceInstance", $scope.site, "active", "namenode", ["site"], "count")._promise.then(function (res) {
+					$.map(res, function (data) {
+						$scope.namenodeactivenum = data.value[0];
+					});
+				});
+				HDFSMETRIC.countHadoopRole("HdfsServiceInstance", $scope.site, "standby", "namenode", ["site"], "count")._promise.then(function (res) {
+					$.map(res, function (data) {
+						$scope.namenodestandbynum = data.value[0];
+					});
+				});
+				HDFSMETRIC.countHadoopRole("HdfsServiceInstance", $scope.site, "live", "datanode", ["site"], "count")._promise.then(function (res) {
+					$.map(res, function (data) {
+						$scope.datanodehealtynum = data.value[0];
+					});
+				});
+				HDFSMETRIC.countHadoopRole("HdfsServiceInstance", $scope.site, "dead", "datanode", ["site"], "count")._promise.then(function (res) {
+					$.map(res, function (data) {
+						$scope.datanodeunhealtynum = data.value[0];
+					});
+				});
+				sumMetrics($scope.site, "datanode", ["site"], "sum(configuredCapacityTB)")._promise.then(function (res) {
+					$.map(res, function (data) {
+						$scope.capacityNum = data.value[0];
+					});
+				});
+				sumMetrics($scope.site, "datanode", ["site"], "sum(usedCapacityTB)")._promise.then(function (res) {
+					$.map(res, function (data) {
+						$scope.usedCapacityNum = data.value[0];
+					});
+				});
+			};
+
+			Time.onReload(function () {
+				cache = {};
+				$scope.refresh();
+			}, $scope);
+			$scope.refresh();
 		});
 	});
 })();

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/index.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/index.js b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/index.js
index 48b3feb..a4d6576 100644
--- a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/index.js
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/index.js
@@ -23,11 +23,34 @@
 	var hdfsMetricApp = register(['ngRoute', 'ngAnimate', 'ui.router', 'eagle.service']);
 
 	hdfsMetricApp.route("HdfsMetric", {
-		url: "/hadoopService/hdfs",
+		url: "/hadoopService/hdfs?startTime&endTime",
 		site: true,
 		templateUrl: "partials/overview.html",
 		controller: "overviewCtrl",
 		resolve: {time: true}
+	}).route("datanodeDetail", {
+		url: "/hadoopService/hdfs/datanode/:hostname",
+		site: true,
+		templateUrl: "partials/datanodeDetail.html",
+		controller: "datanodeDetailCtrl",
+		resolve: {time: true}
+	}).route("datanodeList", {
+		url: "/hadoopService/hdfs/datanodeList",
+		site: true,
+		templateUrl: "partials/datanodeList.html",
+		controller: "datanodeListCtrl"
+	}).route("namenodeList", {
+		url: "/hadoopService/hdfs/namenodeList",
+		site: true,
+		templateUrl: "partials/namenodeList.html",
+		controller: "namenodeListCtrl"
+	}).route("namenodeDetail", {
+		url: "/hadoopService/hdfs:hostname?startTime&endTime",
+		site: true,
+		reloadOnSearch: false,
+		templateUrl: "partials/overview.html",
+		controller: "overviewCtrl",
+		resolve: {time: true}
 	});
 
 	hdfsMetricApp.portal({
@@ -36,7 +59,273 @@
 		]
 	}, true);
 
+	hdfsMetricApp.service("HDFSMETRIC", function ($q, $http, Time, Site, Application) {
+		var HDFSMETRIC = window._HDFSMETRIC = {};
+		HDFSMETRIC.STATUS_LIVE = "live";
+		HDFSMETRIC.STATUS_DEAD = "dead";
+		HDFSMETRIC.QUERY_HBASE_HDFSMETRICS = '${baseURL}/rest/entities?query=GenericMetricService[${condition}]{*}&metricName=${metric}&pageSize=${limit}';
+
+		HDFSMETRIC.QUERY_HADOOP_INSTANCE = '${baseURL}/rest/entities?query=${service}[${condition}]{*}&pageSize=${limit}';
+		HDFSMETRIC.QUERY_HADOOP_INSTANCE_AGG = "${baseURL}/rest/entities?query=${service}[${condition}]<${groups}>{${field}}&pageSize=${limit}";
+		HDFSMETRIC.QUERY_HADOOP_HDFSMETRICS_INTERVAL = '${baseURL}/rest/entities?query=GenericMetricService[${condition}]<${groups}>{${field}}${order}${top}&metricName=${metric}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}&intervalmin=${intervalMin}&timeSeries=true';
+
+		/**
+		 * Fetch query content with current site application configuration
+		 * @param {string} queryName
+		 */
+		var getQuery = HDFSMETRIC.getQuery = function (queryName, siteId) {
+			var baseURL;
+			siteId = siteId || Site.current().siteId;
+			var app = Application.find("HDFS_METRIC_WEB_APP", siteId)[0];
+			var host = app.configuration["service.host"];
+			var port = app.configuration["service.port"];
+
+			if (!host && !port) {
+				baseURL = "";
+			} else {
+				if (host === "localhost" || !host) {
+					host = location.hostname;
+				}
+				if (!port) {
+					port = location.port;
+				}
+				baseURL = "http://" + host + ":" + port;
+			}
+
+			return common.template(HDFSMETRIC["QUERY_" + queryName], {baseURL: baseURL});
+		};
+
+		function wrapList(promise) {
+			var _list = [];
+			_list._done = false;
+			_list._promise = promise.then(
+				/**
+				 * @param {{}} res
+				 * @param {{}} res.data
+				 * @param {{}} res.data.obj
+				 */
+				function (res) {
+					_list.splice(0);
+					Array.prototype.push.apply(_list, res.data.obj);
+					_list._done = true;
+					return _list;
+				});
+			return _list;
+		}
+
+		function toFields(fields) {
+			return (fields || []).length > 0 ? $.map(fields, function (field) {
+				return "@" + field;
+			}).join(",") : "*";
+		}
+
+		HDFSMETRIC.metricsToSeries = function (name, metrics, option, rawData) {
+			if (arguments.length === 4 && typeof rawData === "object") {
+				option = rawData;
+				rawData = false;
+			}
+
+			var data = $.map(metrics, function (metric) {
+				return rawData ? metric.value[0] : {
+					x: metric.timestamp,
+					y: metric.value[0]
+				};
+			});
+			return $.extend({
+				name: name,
+				showSymbol: false,
+				type: "line",
+				data: data
+			}, option || {});
+		};
+
+		HDFSMETRIC.get = function (url, params) {
+			return $http({
+				url: url,
+				method: "GET",
+				params: params
+			});
+		};
+
+		HDFSMETRIC.condition = function (condition) {
+			return $.map(condition, function (value, key) {
+				return "@" + key + '="' + value + '"';
+			}).join(" AND ");
+		};
+
+		HDFSMETRIC.aggHadoopInstance = function (service, condition, groups, field, limit) {
+			var fields = field.split(/\s*,\s*/);
+			var fieldStr = $.map(fields, function (field, index) {
+				var matches = field.match(/^([^\s]*)(\s+.*)?$/);
+				if (matches[2]) {
+					orderId = index;
+				}
+				return matches[1];
+			}).join(", ");
+			var config = {
+				service: service,
+				condition: HDFSMETRIC.condition(condition),
+				groups: toFields(groups),
+				field: fieldStr,
+				limit: limit || 10000
+			};
+			var metrics_url = common.template(getQuery("HADOOP_INSTANCE_AGG"), config);
+			return wrapList(HDFSMETRIC.get(metrics_url));
+		};
+
+		HDFSMETRIC.countHadoopRole = function(service, site, status, role, groups, filed, limit) {
+			var jobCond = {
+				site: site,
+				status: status,
+				role: role
+			};
+			return HDFSMETRIC.aggHadoopInstance(service, jobCond, groups, filed, limit);
+		};
+
+		HDFSMETRIC.hadoopMetricsAggregation = function (condition, metric, groups, field, intervalMin, startTime, endTime, top, limit) {
+			var fields = field.split(/\s*,\s*/);
+			var orderId = -1;
+			var fieldStr = $.map(fields, function (field, index) {
+				var matches = field.match(/^([^\s]*)(\s+.*)?$/);
+				if (matches[2]) {
+					orderId = index;
+				}
+				return matches[1];
+			}).join(", ");
+
+
+			var config = {
+				condition: HDFSMETRIC.condition(condition),
+				startTime: Time.format(startTime),
+				endTime: Time.format(endTime),
+				metric: metric,
+				groups: toFields(groups),
+				field: fieldStr,
+				order: orderId === -1 ? "" : ".{" + fields[orderId] + "}",
+				top: top ? "&top=" + top : "",
+				intervalMin: intervalMin,
+				limit: limit || 10000
+			};
+
+			var metrics_url = common.template(getQuery("HADOOP_HDFSMETRICS_INTERVAL"), config);
+			var _list = wrapList(HDFSMETRIC.get(metrics_url));
+			_list._aggInfo = {
+				groups: groups,
+				startTime: Time(startTime).valueOf(),
+				interval: intervalMin * 60 * 1000
+			};
+			_list._promise.then(function () {
+				_list.reverse();
+			});
+			return _list;
+		};
+
+		HDFSMETRIC.aggMetricsToEntities = function (list, param, flatten) {
+			var _list = [];
+			_list.done = false;
+			_list._promise = list._promise.then(function () {
+				var _startTime = list._aggInfo.startTime;
+				var _interval = list._aggInfo.interval;
+
+				$.each(list, function (i, obj) {
+					var tags = {};
+					$.each(list._aggInfo.groups, function (j, group) {
+						tags[group] = obj.key[j];
+					});
+
+					var _subList = $.map(obj.value[0], function (value, index) {
+						return {
+							timestamp: _startTime + index * _interval,
+							value: [value],
+							tags: tags,
+							flag: param
+						};
+					});
+
+					if (flatten) {
+						_list.push.apply(_list, _subList);
+					} else {
+						_list.push(_subList);
+					}
+				});
+				_list.done = true;
+				return _list;
+			});
+			return _list;
+		};
+
+		HDFSMETRIC.hadoopHostStatus = function (service, condition, limit) {
+			var config = {
+				service: service,
+				condition: HDFSMETRIC.condition(condition),
+				limit: limit || 10000
+			};
+
+			var metrics_url = common.template(getQuery("HADOOP_INSTANCE"), config);
+			return wrapList(HDFSMETRIC.get(metrics_url));
+		};
+
+		HDFSMETRIC.getHadoopHostByStatusAndRole = function (service, siteId, status,role, limit) {
+			var condition = {
+				site: siteId,
+				role: role,
+				status: status
+			};
+			return HDFSMETRIC.hadoopHostStatus(service, condition, limit);
+		};
+
+		HDFSMETRIC.getStatusByRoleAndHost = function (service, hostname, role, siteid) {
+			var hoststateinfo;
+			var condition = {
+				site: siteid,
+				role: role,
+				hostname: hostname
+			};
+			hoststateinfo = HDFSMETRIC.hadoopHostStatus(service, condition);
+			return hoststateinfo;
+		};
+
+		HDFSMETRIC.getListByRoleName = function (service, role, siteid) {
+			var hoststateinfos;
+			var condition = {
+				site: siteid,
+				role: role
+			};
+			hoststateinfos = HDFSMETRIC.hadoopHostStatus(service, condition);
+			return hoststateinfos;
+		};
+
+		HDFSMETRIC.getMetricObj = function () {
+			var deferred = $q.defer();
+			$http.get("apps/hdfs/config.json").success(function (resp) {
+				deferred.resolve(resp);
+			});
+			return deferred.promise;
+		};
+
+		HDFSMETRIC.mergeMetricToOneSeries = function (metricTitle, metrics, legendName, dataOption, option) {
+			var series = [];
+
+			$.each(metrics, function (i, metricMap) {
+				if (typeof metricMap !== 'undefined') {
+					series.push(HDFSMETRIC.metricsToSeries(legendName[i], metricMap[0], option));
+				}
+			});
+			return {
+				title: metricTitle,
+				series: series,
+				option: dataOption || {},
+				loading: false
+			};
+		};
+
+		return HDFSMETRIC;
+	});
+
     hdfsMetricApp.requireCSS("style/index.css");
     hdfsMetricApp.require("widget/availabilityChart.js");
 	hdfsMetricApp.require("ctrl/overview.js");
+	hdfsMetricApp.require("ctrl/namenodeListCtrl.js");
+	hdfsMetricApp.require("ctrl/datanodeListCtrl.js");
+	hdfsMetricApp.require("ctrl/datanodeDetailCtrl.js");
 })();

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeDetail.html
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeDetail.html b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeDetail.html
new file mode 100644
index 0000000..7a91cdb
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeDetail.html
@@ -0,0 +1,117 @@
+<!--
+  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.
+  -->
+
+<div class="box box-primary">
+	<div class="box-header with-border">
+		<span class="fa fa-television"></span>
+		<h3 class="box-title">
+			Summary
+		</h3>
+	</div>
+	<div class="box-body">
+		<table class="table table-striped">
+			<tbody ng-if="(datanodestatus || []).length !== 0">
+			<tr>
+				<th>Status</th>
+				<td class="text-break">
+					<span ng-if="datanodestatus[0].status==='live'" class="label label-success">Healthy</span>
+					<span ng-if="datanodestatus[0].status!=='live'" class="label label-danger">UnHealthy</span>
+				</td>
+			</tr>
+			<tr>
+				<th>Rack</th>
+				<td class="text-break">{{datanodestatus[0].tags.rack}}</td>
+			</tr>
+			<tr>
+				<th>ConfiguredCapacityTB</th>
+				<td class="text-break">{{common.number.format(common.number.parse(datanodestatus[0].configuredCapacityTB), 0)}} TB</td>
+			</tr>
+			<tr>
+				<th>UsedCapacityTB</th>
+				<td class="text-break">{{common.number.format(common.number.parse(datanodestatus[0].usedCapacityTB), 0)}} TB</td>
+			</tr>
+			<tr>
+				<th>NumBlocks</th>
+				<td class="text-break">{{common.number.format(common.number.parse(datanodestatus[0].numBlocks), 0)}}</td>
+			</tr>
+			<tr>
+				<th>NumFailedVolumes</th>
+				<td class="text-break">{{common.number.format(common.number.parse(datanodestatus[0].numFailedVolumes), 0)}}</td>
+			</tr>
+			</tbody>
+			<tbody ng-if="datanodestatus.length === 0">
+			<tr>
+				<th>Status</th>
+				<td class="text-break"><span class="label label-danger">N/A</span></td>
+			</tr>
+			<tr>
+				<th>Rack</th>
+				<td class="text-break"><span class="label label-danger">N/A</span></td>
+			</tr>
+			<tr>
+				<th>ConfiguredCapacityTB</th>
+				<td class="text-break"><span class="label label-danger">N/A</span></td>
+			</tr>
+			<tr>
+				<th>UsedCapacityTB</th>
+				<td class="text-break"><span class="label label-danger">N/A</span></td>
+			</tr>
+			<tr>
+				<th>NumBlocks</th>
+				<td class="text-break"><span class="label label-danger">N/A</span></td>
+			</tr>
+			<tr>
+				<th>NumFailedVolumes</th>
+				<td class="text-break"><span class="label label-danger">N/A</span></td>
+			</tr>
+			</tbody>
+		</table>
+	</div>
+</div>
+
+<div class="box box-primary">
+	<div class="box-header with-border">
+		<span class="fa fa-line-chart"></span>
+		<h3 class="box-title">
+			Metrics
+		</h3>
+	</div>
+	<div class="box-body no-padding">
+		<div class="row border-split">
+			<div class="col-sm-6 hadoopMetric-col-md-6 col-lg-6" ng-repeat="chart in chartList track by $index">
+				<div class="hadoopMetric-chart">
+					<h3>{{metricList[chart.name].title}}</h3>
+					<div ng-show="metricList[chart.name].loading" class="hadoopMetric-chart-container"></div>
+					<div ng-show="metricList[chart.name].loading" class="overlay-wrapper">
+						<div class="overlay">
+							<i class="fa fa-refresh fa-spin"></i>
+						</div>
+					</div>
+					<div ng-show="metricList[chart.name].series.length" chart class="hadoopMetric-chart-container"
+						 series="metricList[chart.name].series"
+						 option="metricList[chart.name].option"></div>
+					<div ng-show="!metricList[chart.name].series.length && !metricList[chart.name].loading" class="hadoopMetric-chart-container">
+						<div class="hadoopMetric-no-chart-data">
+							<span class="fa fa-question-circle"></span><span> NO DATA</span>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeList.html
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeList.html b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeList.html
new file mode 100644
index 0000000..0a31b9e
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/datanodeList.html
@@ -0,0 +1,53 @@
+<!--
+  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.
+  -->
+<div class="box box-primary">
+	<div class="box-header with-border">
+		<h3 class="box-title">
+			<span ng-show="!datanodeList._done || isSorting" class="fa fa-refresh fa-spin no-animate"></span>
+		</h3>
+	</div>
+	<div class="box-body">
+		<div id="datanodeList" sort-table="datanodeList" is-sorting="isSorting"
+			 search-path-list="searchPathList"
+			 scope="tableScope">
+			<table class="table table-bordered">
+				<thead>
+				<tr>
+					<th sortpath="tags.hostname">RegionServer</th>
+					<th sortpath="tags.rack">Rack</th>
+					<th sortpath="tags.site">SiteId</th>
+					<th sortpath="status">Status</th>
+				</tr>
+				</thead>
+				<tbody>
+				<tr ng-repeat="item in datanodeList">
+					<td>
+						<a ui-sref="datanodeDetail({siteId: site, hostname: item.tags.hostname})" target="_blank">{{item.tags.hostname}}</a>
+					</td>
+					<td>{{item.tags.rack}}</td>
+					<td>{{item.tags.site}}</td>
+					<td>
+						<span ng-if="item.status===live" class="label label-success">Healthy</span>
+						<span ng-if="item.status===dead" class="label label-danger">UnHealthy</span>
+					</td>
+				</tr>
+				</tbody>
+			</table>
+		</div>
+	</div>
+</div>

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/namenodeList.html
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/namenodeList.html b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/namenodeList.html
new file mode 100644
index 0000000..d10aa0b
--- /dev/null
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/namenodeList.html
@@ -0,0 +1,52 @@
+<!--
+  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.
+  -->
+<div class="box box-primary">
+	<div class="box-header with-border">
+		<h3 class="box-title">
+			<span ng-show="!namenodeList._done || isSorting" class="fa fa-refresh fa-spin no-animate"></span>
+		</h3>
+	</div>
+	<div class="box-body">
+		<div id="namenodeList" sort-table="namenodeList" is-sorting="isSorting"
+			 search-path-list="searchPathList"
+			 scope="tableScope">
+			<table class="table table-bordered">
+				<thead>
+				<tr>
+					<th sortpath="tags.hostname">Namenode</th>
+					<th sortpath="tags.rack">Rack</th>
+					<th sortpath="tags.site">SiteId</th>
+					<th sortpath="status">Status</th>
+				</tr>
+				</thead>
+				<tbody>
+				<tr ng-repeat="item in namenodeList">
+					<td>
+						<a ui-sref="namenodeDetail({siteId: site, hostname: item.tags.hostname})" target="_blank">{{item.tags.hostname}}</a>
+					</td>
+					<td>{{item.tags.rack}}</td>
+					<td>{{item.tags.site}}</td>
+					<td>
+						<span>{{item.status}}</span>
+					</td>
+				</tr>
+				</tbody>
+			</table>
+		</div>
+	</div>
+</div>

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/overview.html
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/overview.html b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/overview.html
index 678b221..328b869 100644
--- a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/overview.html
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/partials/overview.html
@@ -6,7 +6,9 @@
   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.
@@ -14,4 +16,106 @@
   limitations under the License.
   -->
 
-HDFS Page
+<div class="box box-primary">
+    <div class="box-header with-border">
+        <span class="fa fa-television"></span>
+        <h3 class="box-title">
+            HDFS Namenode Service Summary Information
+        </h3>
+    </div>
+    <div class="box-body">
+        <table class="table table-striped hadoopMetric-table">
+            <tr>
+                <td width="5%">
+                    <a ui-sref="namenodeList({siteId: site})" target="_blank">
+                        <span ng-show="namenodeactivenum">{{namenodeactivenum}}</span></a>
+                    <span ng-show="!namenodeactivenum">N/A</span>
+                </td>
+                <th width="30%">Active Namenode</th>
+                <td>
+                    <span ng-show="capacityNum">{{common.number.format(capacityNum, 0)}} TB</span>
+                    <span ng-show="!capacityNum">N/A</span>
+                    Capacity <span ng-show="usedCapacityNum">{{common.number.format(usedCapacityNum, 0)}} TB</span>
+                    <span ng-show="!usedCapacityNum">N/A</span> usedCapacity
+
+                </td>
+                <th width="30%">
+                </th>
+            </tr>
+            <tr>
+                <td>
+                    <a ui-sref="namenodeList({siteId: site})" target="_blank">
+                        <span ng-show="namenodestandbynum">{{namenodestandbynum}}</span></a>
+                    <span ng-show="!namenodestandbynum">N/A</span>
+                </td>
+                <th>Backup Namenode</th>
+            </tr>
+            <tr>
+                <td>
+                    <a ui-sref="datanodeList({siteId: site})">
+                        <span ng-show="datanodehealtynum || datanodeunhealtynum">{{datanodehealtynum+datanodeunhealtynum}}</span></a>
+                    <span ng-show="!datanodehealtynum && !datanodeunhealtynum">N/A</span>
+                </td>
+                <th>Datanodes:
+                    <span ng-show="datanodehealtynum">{{datanodehealtynum}}</span>
+                    <span ng-show="!datanodehealtynum">N/A</span>
+                    <span
+                            class="label label-success">Good Health</span> /
+                    <a ui-sref="datanodeList({siteId: site})">
+                        <span ng-show="datanodeunhealtynum">{{datanodeunhealtynum}}</span></a>
+                    <span ng-show="!datanodeunhealtynum">0</span>
+                    <span class="label label-danger">Bad Health</span>
+                </th>
+                <td></td>
+                <th></th>
+            </tr>
+        </table>
+    </div>
+</div>
+
+<div class="box box-primary">
+    <div class="box-header with-border">
+        <span class="fa fa-line-chart"></span>
+        <h3 class="box-title">
+            Metrics
+        </h3>
+    </div>
+
+    <div class="nav-tabs-custom">
+        <ul class="nav nav-tabs">
+            <li ng-class="{active: type===namenode.tags.hostname}"
+                ng-repeat="namenode in activeNamenodeList track by $index">
+                <a ng-click="switchNamenode(namenode.tags.hostname)" data-toggle="tab" aria-expanded="true">{{namenode.tags.hostname}}({{namenode.status}})</a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active" id="tab_1">
+                <div class="box-body no-padding">
+                    <div class="row border-split">
+                        <div class="col-sm-6 col-md-4 col-lg-6" ng-repeat="chart in chartList track by $index">
+                            <div class="hadoopMetric-chart">
+                                <h3>{{metricList[chart.name].title}}</h3>
+                                <div ng-show="metricList[chart.name].loading"
+                                     class="fa fa-refresh fa-spin no-animate"></div>
+                                <div ng-show="metricList[chart.name].series.length" chart
+                                     class="hadoopMetric-chart-container"
+                                     series="metricList[chart.name].series"
+                                     option="metricList[chart.name].option"></div>
+                                <div ng-show="!metricList[chart.name].series.length && !metricList[chart.name].loading"
+                                     class="hadoopMetric-chart-container">
+                                    <div class="hadoopMetric-chart-container-withborder">
+                                        <div class="hadoopMetric-no-chart-data">
+                                            <span class="fa fa-question-circle"></span><span> NO DATA</span>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+
+</div>

http://git-wip-us.apache.org/repos/asf/eagle/blob/610b9f2e/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/widget/availabilityChart.js
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/widget/availabilityChart.js b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/widget/availabilityChart.js
index fbfc73d..6653438 100644
--- a/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/widget/availabilityChart.js
+++ b/eagle-metric/eagle-hdfs-web/src/main/webapp/app/apps/hdfs/widget/availabilityChart.js
@@ -21,67 +21,91 @@
 	 * `register` without params will load the module which using require
 	 */
 	register(function (hdfsMetricApp) {
-		var COLOR_MAPPING = {
-			HDFS: 'orange',
-			HBase: 'yellow',
-			Yarn: 'green',
-		};
-
-		var widgetChartOption = {
-			color: ['#FFFFFF'],
-			grid: {
-				top: 0,
-				right: 0,
-				bottom: 0,
-				left: 0,
-				containLabel: false,
-			},
-			xAxis: {
-				axisLine: {show: false},
-				axisLabel: {show: false},
-			},
-			yAxis: [{
-				axisLine: {show: false},
-				axisLabel: {show: false},
-				axisTick: {show: false},
-				splitLine: {show: false},
-			}],
-		};
-
 		hdfsMetricApp.directive("hdfsMetricWidget", function () {
 			return {
 				restrict: 'AE',
-				controller: function($scope, $attrs) {
+				controller: function ($scope, $attrs, HDFSMETRIC, Application, $interval) {
 					// Get site
 					var site = $scope.site;
+					var refreshInterval;
 
+					if(!site) {
+						$scope.list = $.map(Application.find("HDFS_METRIC_WEB_APP"), function (app) {
+							return {
+								siteId: app.site.siteId,
+								siteName: app.site.siteName || app.site.siteId
+							};
+						});
+					} else {
+						$scope.list = [{
+							siteId: site.siteId,
+							siteName: site.siteName || site.siteId
+						}];
+					}
 					// Get type
 					$scope.type = $attrs.type;
 
 					// Customize chart color
-					$scope.bgColor = COLOR_MAPPING[$scope.type];
+					$scope.bgColor = "orange";
 
-					$scope.chartOption = widgetChartOption;
+					function refresh() {
+						$.each($scope.list, function (i, site) {
 
-					// Mock fetch data
-					var now = +new Date();
-					var data = [];
-					for(var j = 0 ; j < 30 ; j += 1) {
-						data.push({x: now + j * 1000 * 60, y: Math.random() * 100});
+							HDFSMETRIC.countHadoopRole("HdfsServiceInstance", site.siteId, "active", "namenode", ["site"], "count")._promise.then(function (res) {
+								$.map(res, function (data) {
+									$scope.namenodeactivenum = data.value[0];
+								});
+							});
+							HDFSMETRIC.countHadoopRole("HdfsServiceInstance", site.siteId, "standby", "namenode", ["site"], "count")._promise.then(function (res) {
+								$.map(res, function (data) {
+									$scope.namenodestandbynum = data.value[0];
+								});
+							});
+							HDFSMETRIC.countHadoopRole("HdfsServiceInstance", site.siteId, "live", "datanode", ["site"], "count")._promise.then(function (res) {
+								$.map(res, function (data) {
+									$scope.datanodehealtynum = data.value[0];
+								});
+							});
+							HDFSMETRIC.countHadoopRole("HdfsServiceInstance", site.siteId, "dead", "datanode", ["site"], "count")._promise.then(function (res) {
+								$.map(res, function (data) {
+									$scope.datanodeunhealtynum = data.value[0];
+								});
+							});
+						});
 					}
-					$scope.series = [{
-						name: '',
-						type: 'line',
-						data: data,
-						showSymbol: false,
-					}];
 
-					// Ref: jpm widget if need keep refresh the widget
+					refresh();
+					refreshInterval = $interval(refresh, 30 * 1000);
+
+					$scope.$on('$destroy', function () {
+						$interval.cancel(refreshInterval);
+					});
 				},
 				template:
-				'<div class="hadoopMetric-widget bg-{{bgColor}}">' +
-					'<h3>{{type}}</h3>' +
-					'<div chart class="hadoopMetric-chart-container" series="series" option="chartOption"></div>' +
+				'<div class="small-box hadoopMetric-widget bg-{{bgColor}}">' +
+				'<div class="inner">' +
+				'<h3>{{type}}</h3>' +
+				'<div ng-show="namenodeactivenum" class="hadoopMetric-widget-detail">' +
+				'<a ui-sref="namenodeList({siteId: site.siteId})">' +
+				'<span>{{namenodeactivenum+namenodestandbynum}}</span> Namenodes (' +
+				'<span ng-show="namenodeactivenum">{{namenodeactivenum}}</span><span ng-show="!namenodeactivenum">0</span> Active / ' +
+				'<span ng-show="namenodestandbynum">{{namenodestandbynum}}</span><span ng-show="!namenodestandbynum">0</span> Standby)' +
+				'</a>' +
+				'</div>' +
+				'<div ng-show="!namenodeactivenum" class="hadoopMetric-widget-detail">' +
+				'<span class="fa fa-question-circle"></span><span> NO DATA</span>' +
+				'</div>' +
+				'<div ng-show="namenodeactivenum" class="hadoopMetric-widget-detail">' +
+				'<a ui-sref="datanodeList({siteId: site.siteId})">' +
+				'<span>{{datanodehealtynum+datanodeunhealtynum}}</span> Datanodes (' +
+				'<span ng-show="datanodehealtynum">{{datanodehealtynum}}</span><span ng-show="!datanodehealtynum">0</span> Healthy / ' +
+				'<span ng-show="datanodeunhealtynum">{{datanodeunhealtynum}}</span><span ng-show="!datanodeunhealtynum">0</span> Unhealthy)' +
+				'</a>' +
+				'</div>' +
+				'</div>' +
+				'<div class="icon">' +
+				'<i class="fa fa-taxi"></i>' +
+				'</div>' +
 				'</div>',
 				replace: true
 			};


Mime
View raw message