[EAGLE-952] system dashbord

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

Author: koone <[email protected]>

Closes #871 from koone/EAGLE-952.


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

Branch: refs/heads/master
Commit: 877d3e77b18c162455d1970a0cff9d5e04dc44b2
Parents: fa713a4
Author: koone <[email protected]>
Authored: Thu Mar 16 08:10:51 2017 +0000
Committer: r7raul1984 <[email protected]>
Committed: Thu Mar 16 08:10:51 2017 +0000

----------------------------------------------------------------------
 eagle-metric/eagle-system-web/pom.xml           |  61 ++++
 .../SystemMetricWebApplicationProvider.java     |  22 ++
 ...etric.SystemMetricWebApplicationProvider.xml |  46 +++
 ...org.apache.eagle.app.spi.ApplicationProvider |  16 +
 .../src/main/webapp/app/apps/system/config.json |  80 +++++
 .../webapp/app/apps/system/ctrl/overview.js     | 321 +++++++++++++++++++
 .../app/apps/system/ctrl/serverDetailCtrl.js    | 229 +++++++++++++
 .../app/apps/system/ctrl/serverListCtrl.js      |  39 +++
 .../src/main/webapp/app/apps/system/index.html  |   6 +
 .../src/main/webapp/app/apps/system/index.js    | 293 +++++++++++++++++
 .../app/apps/system/partials/overview.html      | 126 ++++++++
 .../app/apps/system/partials/serverDetail.html  | 111 +++++++
 .../app/apps/system/partials/serverList.html    |  60 ++++
 .../main/webapp/app/apps/system/style/index.css |  88 +++++
 .../app/apps/system/widget/availabilityChart.js | 138 ++++++++
 .../src/main/webapp/package.json                |   0
 eagle-metric/pom.xml                            |   3 +-
 eagle-server/pom.xml                            |  10 +-
 ...org.apache.eagle.app.spi.ApplicationProvider |   3 +-
 eagle-topology-check/eagle-topology-app/pom.xml |   5 +
 .../apache/eagle/topology/TopologyCheckApp.java |  27 +-
 .../eagle/topology/TopologyCheckAppConfig.java  |  20 ++
 .../system/SystemCheckPersistBolt.java          | 139 ++++++++
 ....eagle.topology.TopologyCheckAppProvider.xml |  32 ++
 .../eagle/topology/TopologyConstants.java       |   2 +
 .../entity/SystemServiceTopologyAPIEntity.java  | 122 +++++++
 .../entity/TopologyEntityRepository.java        |   2 +
 27 files changed, 1994 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/pom.xml
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-system-web/pom.xml 
b/eagle-metric/eagle-system-web/pom.xml
new file mode 100644
index 0000000..3a762af
--- /dev/null
+++ b/eagle-metric/eagle-system-web/pom.xml
@@ -0,0 +1,61 @@
+<!--
+  ~ 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <artifactId>eagle-metric-parent</artifactId>
+        <groupId>org.apache.eagle</groupId>
+        <version>0.5.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>eagle-system-web</artifactId>
+    <name>Eagle::App::HadoopMetric::System::WebUI</name>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.eagle</groupId>
+            <artifactId>eagle-app-base</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.eagle</groupId>
+            <artifactId>eagle-hadoop-metric</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.eagle</groupId>
+            <artifactId>eagle-topology-app</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/webapp/app</directory>
+                <targetPath>assets/</targetPath>
+            </resource>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+    </build>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/java/org/apache/eagle/metric/SystemMetricWebApplicationProvider.java
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/java/org/apache/eagle/metric/SystemMetricWebApplicationProvider.java
 
b/eagle-metric/eagle-system-web/src/main/java/org/apache/eagle/metric/SystemMetricWebApplicationProvider.java
new file mode 100644
index 0000000..ff0cb25
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/java/org/apache/eagle/metric/SystemMetricWebApplicationProvider.java
@@ -0,0 +1,22 @@
+/*
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ */
+package org.apache.eagle.metric;
+
+import org.apache.eagle.app.StaticApplicationProvider;
+
+public class SystemMetricWebApplicationProvider extends 
StaticApplicationProvider {
+}

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/resources/META-INF/providers/org.apache.eagle.metric.SystemMetricWebApplicationProvider.xml
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/resources/META-INF/providers/org.apache.eagle.metric.SystemMetricWebApplicationProvider.xml
 
b/eagle-metric/eagle-system-web/src/main/resources/META-INF/providers/org.apache.eagle.metric.SystemMetricWebApplicationProvider.xml
new file mode 100644
index 0000000..e53c182
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/resources/META-INF/providers/org.apache.eagle.metric.SystemMetricWebApplicationProvider.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ 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.
+  -->
+
+<application>
+    <type>SYSTEM_METRIC_WEB_APP</type>
+    <name>System Metric Monitoring Web </name>
+    <viewPath>/apps/system</viewPath>
+    <description>System Metric Monitoring Web</description>
+    <dependencies>
+        <dependency>
+            <type>HADOOP_METRIC_MONITOR</type>
+            <required>true</required>
+        </dependency>
+        <dependency>
+            <type>TOPOLOGY_HEALTH_CHECK_APP</type>
+            <required>true</required>
+        </dependency>
+    </dependencies>
+    <configuration>
+        <property>
+            <name>service.host</name>
+            <displayName>Eagle Service Host</displayName>
+            <description>Set additional eagle service host, default: using 
current host</description>
+        </property>
+        <property>
+            <name>service.port</name>
+            <displayName>Eagle Service Port</displayName>
+            <description>Set additional eagle service port, default: using 
current port</description>
+        </property>
+    </configuration>
+</application>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
 
b/eagle-metric/eagle-system-web/src/main/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
new file mode 100644
index 0000000..c7df1e8
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
@@ -0,0 +1,16 @@
+# 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.
+
+org.apache.eagle.metric.SystemMetricWebApplicationProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/config.json
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/config.json 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/config.json
new file mode 100644
index 0000000..7258147
--- /dev/null
+++ b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/config.json
@@ -0,0 +1,80 @@
+{
+       "totaleverage": {
+
+               "memtotal" : "system.memory.memtotal.kb",
+               "memfree" : "system.memory.memfree.kb",
+
+               "cpuidle" : "system.cpu.idle",
+               "cputotalusage" : "system.cpu.totalusage",
+               "1minloadavg" : "system.cpu.loadavg.1min",
+               "5minloadavg" : "system.cpu.loadavg.5min",
+               "15minloadavg" : "system.cpu.loadavg.15min",
+
+               "receivedbytes" : "system.nic.receivedbytes",
+               "transmitbytes" : "system.nic.transmitbytes",
+
+               "diskreadrate" : "system.disk.readrate",
+               "diskwriterate" : "system.disk.writerate",
+               "disktotal" : "system.disk.disktotal",
+               "diskused" : "system.disk.diskused"
+       },
+       "systemnode": {
+               "cpuuser" : "system.cpu.user",
+               "cpunice" : "system.cpu.nice",
+               "cpusytem" : "system.cpu.system",
+               "cpuidle" : "system.cpu.idle",
+               "cpuwait" : "system.cpu.wait",
+               "cpuirq" : "system.cpu.irq",
+               "cpusoftirq" : "system.cpu.softirq",
+               "cpusteal" : "system.cpu.steal",
+               "cpuguest" : "system.cpu.guest",
+               "cpuusage" : "system.cpu.usage",
+               "cputotalusage" : "system.cpu.totalusage",
+
+               "1minloadavg" : "system.cpu.loadavg.1min",
+               "5minloadavg" : "system.cpu.loadavg.5min",
+               "15minloadavg" : "system.cpu.loadavg.15min",
+
+               "uptime" : "system.uptime.uptime.day",
+               "idletime" : "system.uptime.idletime.day",
+
+               "memtotal" : "system.memory.memtotal.kb",
+               "memfree" : "system.memory.memfree.kb",
+               "memorybuffers" : "system.memory.buffers.kb",
+               "memorycached" : "system.memory.cached.kb",
+               "memoryswapcached" : "system.memory.swapcached.kb",
+               "memoryactive" : "system.memory.active.kb",
+               "memoryinactive" : "system.memory.inactive.kb",
+               "memoryactiveanon" : "system.memory.activeanon.kb",
+               "memoryinactiveanon" : "system.memory.inactiveanon.kb",
+               "memoryactivefile" : "system.memory.activefile.kb",
+               "memoryinactivefile" : "system.memory.inactivefile.kb",
+               "memoryunevictable" : "system.memory.unevictable.kb",
+               "memorymlocked" : "system.memory.mlocked.kb",
+               "memoryswaptotal" : "system.memory.swaptotal.kb",
+               "memoryswapfree" : "system.memory.swapfree.kb",
+               "memorydirty" : "system.memory.dirty.kb",
+               "memorypagetables" : "system.memory.pagetables.kb",
+               "memoryvmalloctotal" : "system.memory.vmalloctotal.kb",
+               "memoryvmallocused" : "system.memory.vmallocused.kb",
+               "memoryvmallocchunk" : "system.memory.vmallocchunk.kb",
+               "momoryusage" : "system.memory.usage",
+
+               "receivedbytes" : "system.nic.receivedbytes",
+               "receivedpackets" : "system.nic.receivedpackets",
+               "receivederrs" : "system.nic.receivederrs",
+               "receiveddrop" : "system.nic.receiveddrop",
+               "transmitbytes" : "system.nic.transmitbytes",
+               "transmitpackets" : "system.nic.transmitpackets",
+               "transmiterrs" : "system.nic.transmiterrs",
+               "transmitdrop" : "system.nic.transmitdrop",
+
+               "diskreadrate" : "system.disk.readrate",
+               "diskwriterate" : "system.disk.writerate",
+               "diskavgwaittime" : "system.disk.avgwaittime",
+               "diskutilization" : "system.disk.utilization",
+               "diskdisktotal" : "system.disk.disktotal",
+               "diskdiskused" : "system.disk.diskused",
+               "diskusage" : "system.disk.usage"
+       }
+}

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/overview.js
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/overview.js
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/overview.js
new file mode 100644
index 0000000..7a3b5e0
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/overview.js
@@ -0,0 +1,321 @@
+/*
+ * 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 (systemMetricApp) {
+               systemMetricApp.controller("overviewCtrl", function ($q, 
$wrapState, $scope, PageConfig, SYSTEMMETRIC, Time) {
+                       var cache = {};
+                       $scope.site = $wrapState.param.siteId;
+                       PageConfig.title = 'System Dashbord';
+
+                       $scope.coresList = aggGroup($scope.site, ["cores"], 
"count");
+
+                       var digitalKbOption = {
+                               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 / 1024 /1024, false, 0) + "Gb";
+                                                       
}).reverse().join("<br/>");
+                                       }
+                               },
+                               yAxis: [{
+                                       axisLabel: {
+                                               formatter: function (value) {
+                                                       return 
common.number.abbr(value / 1024 /1024, false, 0);
+                                               }
+                                       }
+                               }]
+                       };
+
+                       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 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);
+                                               }
+                                       }
+                               }],
+                               trans: true 
+                       };
+
+                       var averageOption = {
+                               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);
+                                                       
}).reverse().join("<br/>");
+                                       }
+                               },
+                               legend: {
+                                       x: 'center', y: 'bottom'
+                               },
+                               areaStyle: {normal: {}},
+                               yAxis: [{
+                                       axisLabel: {
+                                               formatter: function (value) {
+                                                       return 
common.number.abbr(value, false);
+                                               }
+                                       }
+                               }],
+                               avg: true
+                       };
+
+                       function countStatus(site, status, groups, filed, 
limit) {
+                               var jobCond = {
+                                       site: site,
+                                       status: status
+                               };
+                               return SYSTEMMETRIC.aggSystemInstance(jobCond, 
groups, filed, limit);
+                       }
+
+                       function aggGroup(site, groups, filed, limit) {
+                               var jobCond = {
+                                       site: site
+                               };
+                               return SYSTEMMETRIC.aggSystemInstance(jobCond, 
groups, filed, limit);
+                       }
+
+                       function generateMetric(name, flag) {
+                               var result = cache[name] || 
generateOvewviewMetric(name, flag);
+                               return result;
+                       }
+
+                       function generateOvewviewMetric(name, flag){
+                               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;
+                               var jobCond = {
+                                       site: $scope.site
+                               };
+                               return 
SYSTEMMETRIC.aggMetricsToEntities(SYSTEMMETRIC.systemMetricsAggregation(jobCond,
 name, ["host","device"], "avg(value)", intervalMin, trendStartTime, 
trendEndTime), flag)
+                               ._promise.then(function (list) {
+                                       var metricFlag = $.map(list, function 
(metrics) {
+                                               return metrics[0].flag;
+                                       });
+                                       return [metricFlag, list];
+                               });
+                       }
+
+                       function mergeMetricToOneSeries(metricTitle, metrics, 
legendName, dataOption, option) {
+                               var series = [];
+                               $.each(metrics, function (i, metricMap) {
+                                       var mergeSeries = metricMap[0];
+                                       $.each(metricMap, function (j, 
metriSeries) {
+                                               if(j > 0){
+                                                       $.each(metriSeries, 
function(n, dataNode){
+                                                               
mergeSeries[n].value[0] = mergeSeries[n].value[0] * 1 + dataNode.value * 1;
+                                                       });
+                                               }
+                                       });
+                                       if(dataOption.avg){
+                                               $.each(mergeSeries, function(m, 
item) {
+                                                       item.value[0] = 
item.value[0] / metricMap.length;
+                                               });
+                                       }
+                                       
series.push(SYSTEMMETRIC.metricsToSeries(legendName[i], mergeSeries, option, 
dataOption.trans));
+                               });
+                               return {
+                                       title: metricTitle,
+                                       series: series,
+                                       option: dataOption || {},
+                                       loading: false
+                               };
+                       }
+
+                       $scope.chartList = [
+                               {
+                                       name: "Memory Usage",
+                                       metrics: ["memfree"],
+                                       linename: ["memfree"],
+                                       option: digitalKbOption
+                               },
+                               {
+                                       name: "Loadavg",
+                                       metrics: ["1minloadavg", "5minloadavg", 
"15minloadavg"],
+                                       linename: ["1minloadavg", 
"5minloadavg", "15minloadavg"],
+                                       option: digitalOption
+                               },
+                               {
+                                       name: "Cpu Usage",
+                                       metrics: ["cputotalusage"],
+                                       linename: ["cputotalusage"],
+                                       option: averageOption
+                               },
+                               {
+                                       name: "Network IO",
+                                       metrics: ["receivedbytes", 
"transmitbytes"],
+                                       linename: ["receive", "transmit"],
+                                       option: sizeoption
+                               }
+                       ];
+                       $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 () {
+                               // status count
+                               countStatus($scope.site, "active", ["site"], 
"count")._promise.then(function (res) {
+                                       $.map(res, function (data) {
+                                               $scope.systemactivenum = 
data.value[0];
+                                       });
+                               }, function () {
+                                       $scope.systemactivenum = -1;
+                               });
+
+                               countStatus($scope.site, "warning", ["site"], 
"count")._promise.then(function (res) {
+                                       $.map(res, function (data) {
+                                               $scope.systemwarningnum = 
data.value[0];
+                                       });
+                               }, function () {
+                                       $scope.systemwarningnum = -1;
+                               });
+
+                               countStatus($scope.site, "error", ["site"], 
"count")._promise.then(function (res) {
+                                       $.map(res, function (data) {
+                                               $scope.systemerrornum = 
data.value[0];
+                                       });
+                               }, function () {
+                                       $scope.systemerrornum = -1;
+                               });
+
+                               // memoery count
+                               aggGroup($scope.site, ["site"], 
"sum(totalMemoryMB),sum(usedMemoryMB),sum(totalDiskGB),sum(usedDiskGB)")._promise.then(function
 (res) {
+                                       $.map(res, function (data) {
+                                               $scope.totalMemoryMB = 
data.value[0];
+                                               $scope.usedMemoryMB = 
data.value[1];
+                                               $scope.totalDiskGB = 
data.value[2];
+                                               $scope.usedDiskGB = 
data.value[3];
+                                       });
+                               }, function () {
+                                       $scope.totalMemoryMB = -1;
+                                       $scope.usedMemoryMB = -1;
+                                       $scope.totalDiskGB = -1;
+                                       $scope.usedDiskGB = -1;
+                               });
+
+                               // metric chart
+                               SYSTEMMETRIC.getMetricObj().then(function (res) 
{
+                                       var overviewMetricList = 
res.totaleverage;
+                                       console.log(overviewMetricList);
+                                       $.each($scope.chartList, function (i) {
+                                               var chart = $scope.chartList[i];
+                                               var metricList = chart.metrics;
+                                               $.each(metricList, function (j) 
{
+                                                       var metricKey = 
metricList[j];
+                                                       var metricspromies = 
generateMetric(overviewMetricList[metricKey], 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 (typeof rs 
!== 'undefined' && rs.length > 0) {
+                                                                       
series.push(rs);
+                                                               }
+                                                       }
+                                                       
$scope.metricList[chartname] = mergeMetricToOneSeries(chartname, series, 
chart.linename, chart.option);
+                                               });
+                                       });
+                               });
+                       };
+
+                       Time.onReload(function () {
+                               cache = {};
+                               $.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();
+                       }, $scope);
+                       $scope.refresh();
+               });
+       });
+})();

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverDetailCtrl.js
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverDetailCtrl.js
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverDetailCtrl.js
new file mode 100644
index 0000000..49f1dfa
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverDetailCtrl.js
@@ -0,0 +1,229 @@
+/*
+ * 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 (systemMetricApp) {
+               systemMetricApp.controller("serverDetailCtrl", function ($q, 
$wrapState, $scope, PageConfig, Time, SYSTEMMETRIC) {
+                       var cache = {};
+                       $scope.site = $wrapState.param.siteId;
+                       $scope.hostname = $wrapState.param.hostname;
+                       PageConfig.title = 'System Node' + "(" + 
$scope.hostname + ")";
+                       $scope.metricList = [];
+                       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);
+                                               }
+                                       }
+                               }],
+                               trans: 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);
+                                               }
+                                       }
+                               }]
+                       };
+
+                       function generateMetric(name, flag) {
+                               var result = cache[name] || 
generateChartMetric(name, flag);
+                               return result;
+                       }
+
+                       function generateChartMetric(name, flag){
+                               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;
+                               var jobCond = {
+                                       site: $scope.site,
+                                       host: $scope.hostname
+                               };
+                               return 
SYSTEMMETRIC.aggMetricsToEntities(SYSTEMMETRIC.systemMetricsAggregation(jobCond,
 name, ["device"], "avg(value)", intervalMin, trendStartTime, trendEndTime), 
flag)
+                               ._promise.then(function (list) {
+                                       var metricFlag = $.map(list, function 
(metrics) {
+                                               return metrics[0].flag;
+                                       });
+                                       return [metricFlag, list];
+                               });
+                       }
+
+                       function mergeMetricToOneSeries(metricTitle, metrics, 
legendName, dataOption, option) {
+                               var series = [];
+                               $.each(metrics, function (i, metricMap) {
+                                       if (typeof metricMap !== 'undefined') {
+                                               $.each(metricMap, function (j, 
metriSeries) {
+                                                       var linename = [];
+                                                       if(typeof 
metriSeries.group !== 'undefined'){
+                                                               
linename.push(metriSeries.group);
+                                                       }
+                                                       
linename.push(legendName[i]);
+                                                       
series.push(SYSTEMMETRIC.metricsToSeries(linename.join("."), metriSeries, 
option, dataOption.trans));
+                                               });
+                                       }
+                               });
+                               return {
+                                       title: metricTitle,
+                                       series: series,
+                                       option: dataOption || {},
+                                       loading: false
+                               };
+                       }
+
+                       $scope.chartList = [
+                               {
+                                       name: "Cpu Usage",
+                                       metrics: ["cputotalusage"],
+                                       linename: ["usage"],
+                                       option: digitalOption
+                               },
+                               {
+                                       name: "Memory Usage",
+                                       metrics: ["momoryusage"],
+                                       linename: ["usage"],
+                                       option: digitalOption
+                               },
+                               {
+                                       name: "Network IO",
+                                       metrics: ["receivedbytes", 
"transmitbytes"],
+                                       linename: ["receive", "transmit"],
+                                       option: sizeoption
+                               },
+                               {
+                                       name: "Loadavg",
+                                       metrics: ["1minloadavg", "5minloadavg", 
"15minloadavg"],
+                                       linename: ["1minloadavg", 
"5minloadavg", "15minloadavg"],
+                                       option: digitalOption
+                               }
+                               
+                       ];
+                       $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();
+                               var metricspromies = [];
+
+                               SYSTEMMETRIC.getMetricObj().then(function (res) 
{
+                                       var overviewMetricList = res.systemnode;
+                                       console.log(overviewMetricList);
+                                       $.each($scope.chartList, function (i) {
+                                               var chart = $scope.chartList[i];
+                                               var metricList = chart.metrics;
+                                               $.each(metricList, function (j) 
{
+                                                       var metricKey = 
metricList[j];
+                                                       var metricspromies = 
generateMetric(overviewMetricList[metricKey], 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 (typeof rs 
!== 'undefined' && rs.length > 0) {
+                                                                       
series.push(rs);
+                                                               }
+                                                       }
+                                                       
$scope.metricList[chartname] = mergeMetricToOneSeries(chartname, series, 
chart.linename, chart.option);
+                                               });
+                                       });
+                               });
+
+                               var condition = {
+                    site: $scope.site,
+                    role: "system",
+                    hostname : $scope.hostname
+                };
+                               SYSTEMMETRIC.hostStatus(condition, 
1)._promise.then(function (res) {
+                                       $scope.hostStatus = res;
+                               });
+                       };
+                       Time.onReload(function () {
+                               cache = {};
+                               $.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();
+                       }, $scope);
+                       $scope.refresh();
+               });
+       });
+})
+();

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverListCtrl.js
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverListCtrl.js
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverListCtrl.js
new file mode 100644
index 0000000..c495612
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/ctrl/serverListCtrl.js
@@ -0,0 +1,39 @@
+/*
+ * 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 (systemMetricApp) {
+
+               systemMetricApp.controller("serverListCtrl", function 
($wrapState, $scope, PageConfig, SYSTEMMETRIC) {
+
+                       // Initialization
+                       PageConfig.title = "System Server List";
+                       $scope.tableScope = {};
+                       $scope.site = $wrapState.param.siteId;
+                       $scope.active = SYSTEMMETRIC.STATUS_ACTIVE;
+               $scope.warning = SYSTEMMETRIC.STATUS_WARNING;
+               $scope.error = SYSTEMMETRIC.STATUS_ERROR;
+                       $scope.searchPathList = [["tags", "hostname"], ["tags", 
"rack"], ["tags", "site"], ["status"]];
+                       $scope.serverList = 
SYSTEMMETRIC.serverList($scope.site);
+
+               });
+       });
+})();

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.html
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.html 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.html
new file mode 100644
index 0000000..c21167e
--- /dev/null
+++ b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.html
@@ -0,0 +1,6 @@
+<htm>
+    <body>
+    <h1>System Metric Monitor Application!</h1>
+    <i><b>url</b>: /apps/system</i>
+    </body>
+</htm>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.js
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.js 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.js
new file mode 100644
index 0000000..34c74ac
--- /dev/null
+++ b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/index.js
@@ -0,0 +1,293 @@
+/*
+ * 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` is global function that for application to set up 
'controller', 'service', 'directive', 'route' in Eagle
+        */
+       var systemMetricApp = register(['ngRoute', 'ngAnimate', 'ui.router', 
'eagle.service']);
+
+    systemMetricApp.route("systemMetric", {
+        url: "/hadoopService/system/overview?startTime&endTime",
+        site: true,
+        templateUrl: "partials/overview.html",
+        controller: "overviewCtrl",
+        resolve: {time: true}
+    }).route("serverList", {
+        url: "/hadoopService/system/serverList",
+        site: true,
+        templateUrl: "partials/serverList.html",
+        controller: "serverListCtrl"
+    }).route("serverDetail", {
+        url: "/hadoopService/system/serverDetail/:hostname",
+        site: true,
+        templateUrl: "partials/serverDetail.html",
+        controller: "serverDetailCtrl",
+        resolve: {time: true}
+    });
+
+    systemMetricApp.portal({
+               name: "Services", icon: "heartbeat", list: [
+                       {name: "System", path: "hadoopService/system/overview"}
+               ]
+       }, true);
+
+    systemMetricApp.service("SYSTEMMETRIC", function ($q, $http, Time, Site, 
Application) {
+        var SYSTEMMETRIC = window._SYSTEMMETRIC = {};
+        SYSTEMMETRIC.STATUS_ACTIVE = "active";
+        SYSTEMMETRIC.STATUS_WARNING = "warning";
+        SYSTEMMETRIC.STATUS_ERROR = "error";
+        SYSTEMMETRIC.QUERY_SYSTEM_METRICS = 
'${baseURL}/rest/entities?query=GenericMetricService[${condition}]{*}&metricName=${metric}&pageSize=${limit}';
+        SYSTEMMETRIC.QUERY_SYSTEM_METRICS_WITHTIME = 
'${baseURL}/rest/entities?query=GenericMetricService[${condition}]{*}&metricName=${metric}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}';
+        SYSTEMMETRIC.QUERY_SYSTEM_METRICS_INTERVAL = 
'${baseURL}/rest/entities?query=GenericMetricService[${condition}]<${groups}>{${field}}${order}${top}&metricName=${metric}&pageSize=${limit}&startTime=${startTime}&endTime=${endTime}&intervalmin=${intervalMin}&timeSeries=true';
+        SYSTEMMETRIC.QUERY_SYSTEM_INSTANCE = 
'${baseURL}/rest/entities?query=SystemServiceInstance[${condition}]{*}&pageSize=${limit}';
+        SYSTEMMETRIC.QUERY_SYSTEM_INSTANCE_AGG = 
"${baseURL}/rest/entities?query=SystemServiceInstance[${condition}]<${groups}>{${field}}&pageSize=${limit}";
+        
+        var getQuery = SYSTEMMETRIC.getQuery = function (queryName, siteId) {
+            var baseURL;
+            siteId = siteId || Site.current().siteId;
+            var app = Application.find("SYSTEM_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(SYSTEMMETRIC["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(",") : "*";
+        }
+
+        SYSTEMMETRIC.metricsToSeries = function (name, metrics, option, 
transflag, rawData) {
+            if (arguments.length === 4 && typeof rawData === "object") {
+                option = rawData;
+                rawData = false;
+            }
+            var previous = 0;
+            data = $.map(metrics, function (metric, index) {
+                var temp = metric.value[0];
+                if(transflag){
+                    metric.value[0] = metric.value[0] - previous;
+                }
+                previous = temp;
+                if(transflag && index === 0){
+                    return ;
+                }
+                return rawData ? metric.value[0] : {
+                    x: metric.timestamp,
+                    y: metric.value[0]
+                };
+            });
+           
+            return $.extend({
+                name: name,
+                showSymbol: false,
+                type: "line",
+                data: data
+            }, option || {});
+        };
+
+        SYSTEMMETRIC.get = function (url, params) {
+            return $http({
+                url: url,
+                method: "GET",
+                params: params
+            });
+        };
+
+        SYSTEMMETRIC.condition = function (condition) {
+            return $.map(condition, function (value, key) {
+                return "@" + key + '="' + value + '"';
+            }).join(" AND ");
+        };
+
+        SYSTEMMETRIC.aggSystemInstance = function (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 = {
+                condition: SYSTEMMETRIC.condition(condition),
+                groups: toFields(groups),
+                field: fieldStr,
+                limit: limit || 10000
+            };
+            var metrics_url = common.template(getQuery("SYSTEM_INSTANCE_AGG"), 
config);
+            return wrapList(SYSTEMMETRIC.get(metrics_url));
+        };
+
+        SYSTEMMETRIC.systemMetricsAggregation = 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: SYSTEMMETRIC.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("SYSTEM_METRICS_INTERVAL"), config);
+            var _list = wrapList(SYSTEMMETRIC.get(metrics_url));
+            _list._aggInfo = {
+                groups: groups,
+                startTime: Time(startTime).valueOf(),
+                interval: intervalMin * 60 * 1000
+            };
+            _list._promise.then(function () {
+                _list.reverse();
+            });
+            return _list;
+        };
+
+        SYSTEMMETRIC.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 = [];
+                    _subList.group = obj.key.join(",");
+                    $.each(obj.value[0], function (index, value) {
+                        var node = {
+                            timestamp: _startTime + index * _interval,
+                            value: [value],
+                            tags: tags,
+                            flag: param
+                        };
+                        _subList.push(node);
+                    });
+                    /*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;
+            }, function () {
+                return [];
+            });
+            return _list;
+        };
+
+        SYSTEMMETRIC.hostStatus = function (condition, limit) {
+            var config = {
+                condition: SYSTEMMETRIC.condition(condition),
+                limit: limit || 10000
+            };
+
+            var metrics_url = common.template(getQuery("SYSTEM_INSTANCE"), 
config);
+            return wrapList(SYSTEMMETRIC.get(metrics_url));
+        };
+
+
+        SYSTEMMETRIC.serverList = function (siteid, status, coresCount) {
+            var condition = {
+                    site: siteid,
+                    role: "system"
+                };
+            if(typeof status !== 'undefined') {
+                condition.status = status;
+            }
+            if(typeof coresCount !== 'undefined') {
+                condition.cores = coresCount;
+            }
+            return SYSTEMMETRIC.hostStatus(condition);
+        };
+
+        SYSTEMMETRIC.getMetricObj = function () {
+            var deferred = $q.defer();
+            $http.get("apps/system/config.json").success(function (resp) {
+                deferred.resolve(resp);
+            });
+            return deferred.promise;
+        };
+
+        return SYSTEMMETRIC;
+    });
+
+
+    systemMetricApp.requireCSS("style/index.css");
+    systemMetricApp.require("widget/availabilityChart.js");
+    systemMetricApp.require("ctrl/overview.js");
+    systemMetricApp.require("ctrl/serverDetailCtrl.js");
+    systemMetricApp.require("ctrl/serverListCtrl.js");
+})();

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/overview.html
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/overview.html
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/overview.html
new file mode 100644
index 0000000..de86079
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/overview.html
@@ -0,0 +1,126 @@
+<!--
+  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">
+            System Summary Information
+        </h3>
+    </div>
+    <div class="box-body">
+        <table class="table table-striped hadoopMetric-table">
+            <tr>
+                <th width="20%">
+                    Server Node
+                </th>
+                <th width="80%">
+                    <span ng-show="!systemactivenum && !systemwarningnum && 
!systemerrornum">0</span>
+                    <span ng-show="systemactivenum===-1 || 
systemwarningnum===-1 || systemerrornum===-1">N/A</span>
+                    <a ui-sref="serverList({siteId: site})">
+                        <span ng-show="systemactivenum!==-1 && 
systemwarningnum!==-1 && 
systemerrornum!==-1">{{systemactivenum+systemwarningnum+systemerrornum}}</span>
+                    </a>
+                    <span class="label label-primary">Total</span> /
+                    <a ui-sref="serverList({siteId: site, status:'active'})">
+                        <span ng-show="systemactivenum !== 
-1">{{systemactivenum || 0}}</span>
+                    </a>
+                    <span ng-show="systemactivenum === -1">N/A</span>
+                    <span class="label label-success">Health</span> /
+                    <a ui-sref="serverList({siteId: site, status:'warning'})">
+                        <span ng-show="systemwarningnum !== 
-1">{{systemwarningnum || 0}}</span>
+                    </a>
+                    <span ng-show="systemwarningnum === -1">N/A</span>
+                    <span class="label label-warning">Warning</span> /
+                    <a ui-sref="serverList({siteId: site, status:'error'})">
+                        <span ng-show="systemerrornum !== -1">{{systemerrornum 
|| 0}}</span>
+                    </a>
+                    <span ng-show="systemerrornum === -1">N/A</span>
+                    <span class="label label-danger">Unhealth</span>
+                </th>
+            </tr>
+            <tr>
+                <th>Cores</th>
+                <th>
+                    <div ng-repeat="core in coresList">
+                        <a ui-sref="serverList({siteId: site, cores: 
core.key[0]})">
+                            <span>{{core.value[0]}}</span>
+                        </a>
+                        <span class="label label-primary">{{core.key[0]}} 
cores</span>
+                    </div>
+                    <span ng-show="!coresList">N/A</span>
+                </th>
+            </tr>
+            <tr>
+                <th>Disk Usage</th>
+                <th>
+                                       <span>
+                        <span ng-show="usedDiskGB !== -1">{{usedDiskGB | 
number : 2 || 0}} GB / </span> 
+                        <span ng-show="!totalDiskGB === -1">N/A /</span>
+                                               <span ng-show="totalDiskGB !== 
-1">{{totalDiskGB | number : 2 || 0}} GB</span>
+                        <span ng-show="!usedDiskGB === -1">N/A</span>
+                                       </span>
+                </th>
+            </tr>
+            <tr>
+                <th>Memory</th>
+                <th>
+                    <span>
+                        <span ng-show="usedMemoryMB !== -1">{{usedMemoryMB | 
number : 2 || 0}} MB / </span> 
+                        <span ng-show="!usedMemoryMB === -1">N/A /</span>
+                        <span ng-show="totalMemoryMB !== -1">{{totalMemoryMB | 
number : 2 || 0}} MB</span>
+                        <span ng-show="!totalMemoryMB === -1">N/A</span>
+                    </span>
+                </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="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" chart 
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-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>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverDetail.html
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverDetail.html
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverDetail.html
new file mode 100644
index 0000000..a3c7f47
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverDetail.html
@@ -0,0 +1,111 @@
+<!--
+  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="(hostStatus || []).length !== 0">
+                       <tr>
+                               <th width="20%">Status</th>
+                               <td width="80%" class="text-break">
+                                       <span 
ng-if="hostStatus[0].status==='active'" class="label 
label-success">Healthy</span>
+                                       <span 
ng-if="hostStatus[0].status==='warning'" class="label 
label-warning">Warning</span>
+                                       <span 
ng-if="hostStatus[0].status==='error'" class="label 
label-danger">UnHealthy</span>
+                               </td>
+                       </tr>
+                       <tr>
+                               <th>Rack</th>
+                               <td 
class="text-break">{{hostStatus[0].tags.rack}}</td>
+                       </tr>
+                       <tr>
+                               <th>Cores</th>
+                               <td class="text-break">{{hostStatus[0].cores}} 
</td>
+                       </tr>
+                       <tr>
+                               <th>Disk Usage</th>
+                               <td 
class="text-break">{{hostStatus[0].usedDiskGB | number : 2}} GB / 
{{hostStatus[0].totalDiskGB | number : 2}} GB</td>
+                       </tr>
+                       <tr>
+                               <th>Memory Usage</th>
+                               <td 
class="text-break">{{hostStatus[0].usedMemoryMB | number : 2}} MB / 
{{hostStatus[0].totalMemoryMB | number : 2}} MB</td>
+                       </tr>
+                       </tbody>
+                       <tbody ng-if="hostStatus.length === 0">
+                       <tr>
+                               <th>Status</th>
+                               <td class="text-break"><span class="label 
label-danger">Unknow</span></td>
+                       </tr>
+                       <tr>
+                               <th>Rack</th>
+                               <td class="text-break"><span class="label 
label-danger">Unknow</span></td>
+                       </tr>
+                       <tr>
+                               <th>Cores</th>
+                               <td class="text-break"><span class="label 
label-danger">Unknow</span></td>
+                       </tr>
+                       <tr>
+                               <th>Disk Usage</th>
+                               <td class="text-break"><span class="label 
label-danger">Unknow</span></td>
+                       </tr>
+                       <tr>
+                               <th>Memory Usage</th>
+                               <td class="text-break"><span class="label 
label-danger">Unknow</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>
+</div>

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverList.html
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverList.html
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverList.html
new file mode 100644
index 0000000..0f394ee
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/partials/serverList.html
@@ -0,0 +1,60 @@
+<!--
+  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="!serverList._done || isSorting" 
class="fa fa-refresh fa-spin no-animate"></span>
+               </h3>
+       </div>
+       <div class="box-body">
+               <div id="serverList" sort-table="serverList" 
is-sorting="isSorting"
+                        search-path-list="searchPathList"
+                        scope="tableScope">
+                       <table class="table table-bordered">
+                               <thead>
+                               <tr>
+                                       <th sortpath="tags.hostname">Host 
Name</th>
+                                       <th sortpath="tags.rack">Rack</th>
+                                       <th sortpath="tags.site">SiteId</th>
+                                       <th sortpath="status">Status</th>
+                                       <th sortpath="status">Cores</th>
+                                       <th sortpath="status">Disk Usage</th>
+                                       <th sortpath="status">Memory Usage</th>
+                               </tr>
+                               </thead>
+                               <tbody>
+                               <tr ng-repeat="item in serverList">
+                                       <td>
+                                               <a 
ui-sref="serverDetail({siteId: site, hostname: 
item.tags.hostname})">{{item.tags.hostname}}</a>
+                                       </td>
+                                       <td>{{item.tags.rack}}</td>
+                                       <td>{{item.tags.site}}</td>
+                                       <td>
+                                               <span 
ng-if="item.status===active" class="label label-success">Healthy</span>
+                                               <span 
ng-if="item.status===warning" class="label label-warning">Warning</span>
+                                               <span 
ng-if="item.status===error" class="label label-danger">UnHealthy</span>
+                                       </td>
+                                       <td>{{item.cores}}</td>
+                                       <td>{{item.usedDiskGB | number : 2}} GB 
/ {{item.totalDiskGB | number : 2}} GB</td>
+                                       <td>{{item.usedMemoryMB | number : 2}} 
MB / {{item.totalMemoryMB | number : 2}} MB</td>
+                               </tr>
+                               </tbody>
+                       </table>
+               </div>
+       </div>
+</div>

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/style/index.css
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/style/index.css 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/style/index.css
new file mode 100644
index 0000000..aa215ab
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/style/index.css
@@ -0,0 +1,88 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+.hadoopMetric-chart {
+       position: relative;
+       margin-bottom: 15px;
+}
+
+.hadoopMetric-chart h3 {
+       font-size: 20px;
+       margin: 10px 0 0 0;
+}
+
+.hadoopMetric-chart .hadoopMetric-chart-container {
+       height: 300px;
+       position: relative;
+}
+
+.hadoopMetric-chart .hadoopMetric-no-chart-data {
+       position:absolute;
+       left: 38%;
+       top: 40%;
+       font-size: 20px;
+       color: #999;
+}
+
+.with-border .hadoopMetric-chart {
+       padding-bottom: 15px;
+       margin-bottom: 15px;
+       border-bottom: 1px solid #f4f4f4;
+}
+
+.with-border .hadoopMetric-chart:last-child {
+       padding-bottom: 0;
+       margin-bottom: 0;
+       border-bottom: 0;
+}
+
+.hadoopMetric-widget {
+       width: 100%;
+       box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+       border-radius: 2px;
+       padding: 10px;
+}
+
+.hadoopMetric-widget h3 {
+       margin: 0;
+       padding: 0;
+}
+
+.hadoopMetric-widget .hadoopMetric-chart-container {
+       height: 100px;
+}
+
+.hadoopMetric-summary-a {
+       color: #fff;
+}
+
+.small-box.hadoopMetric-widget {
+       margin: 0;
+       height: 100%;
+       min-height: 110px;
+}
+
+.hadoopMetric-widget-detail {
+       margin-left: 12px;
+       margin-top: 10px;
+}
+
+.hadoopMetric-table {
+       text-align: right;
+}

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/widget/availabilityChart.js
----------------------------------------------------------------------
diff --git 
a/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/widget/availabilityChart.js
 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/widget/availabilityChart.js
new file mode 100644
index 0000000..2529f03
--- /dev/null
+++ 
b/eagle-metric/eagle-system-web/src/main/webapp/app/apps/system/widget/availabilityChart.js
@@ -0,0 +1,138 @@
+/*
+ * 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 (systemMetricApp) {
+               systemMetricApp.directive("systemMetricWidget", function () {
+                       return {
+                               restrict: 'AE',
+                               controller: function ($scope, $attrs, 
SYSTEMMETRIC, Application, $interval, Site, $wrapState) {
+                                       // Get site
+                                       var site = $scope.site;
+                                       var refreshInterval;
+
+                                       if(!site) {
+                                               $scope.list = 
$.map(Application.find("SYSTEM_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 = "yellow-active";
+
+                                       function countStatus(site, status, 
groups, filed, limit) {
+                                               var jobCond = {
+                                                       site: site,
+                                                       status: status
+                                               };
+                                               return 
SYSTEMMETRIC.aggSystemInstance(jobCond, groups, filed, limit);
+                                       }
+
+                                       // Ref: jpm widget if need keep refresh 
the widget
+
+                                       function refresh() {
+                                               $.each($scope.list, function 
(i, site) {
+                                                       // status count
+                                                       
countStatus(site.siteId, "active", ["site"], "count")._promise.then(function 
(res) {
+                                                               $.map(res, 
function (data) {
+                                                                       
$scope.systemactivenum = data.value[0];
+                                                               });
+                                                       }, function () {
+                                                               
$scope.systemactivenum = -1;
+                                                       });
+
+                                                       
countStatus(site.siteId, "warning", ["site"], "count")._promise.then(function 
(res) {
+                                                               $.map(res, 
function (data) {
+                                                                       
$scope.systemwarningnum = data.value[0];
+                                                               });
+                                                       }, function () {
+                                                               
$scope.systemwarningnum = -1;
+                                                       });
+
+                                                       
countStatus(site.siteId, "error", ["site"], "count")._promise.then(function 
(res) {
+                                                               $.map(res, 
function (data) {
+                                                                       
$scope.systemerrornum = data.value[0];
+                                                               });
+                                                       }, function () {
+                                                               
$scope.systemerrornum = -1;
+                                                       });
+                                               });
+                                       }
+
+                                       refresh();
+                                       refreshInterval = $interval(refresh, 30 
* 1000);
+
+                                       $scope.$on('$destroy', function () {
+                                               
$interval.cancel(refreshInterval);
+                                       });
+                               },
+                               template:
+                               '<div class="small-box hadoopMetric-widget 
bg-{{bgColor}}">' +
+                                   '<div class="inner">' +
+                                       '<h3>{{type}}</h3>' +
+                                       '<div ng-show="systemactivenum!==-1 && 
systemwarningnum!==-1 && systemerrornum!==-1" 
class="hadoopMetric-widget-detail">' +
+                                           '<a ui-sref="serverList({siteId: 
site.siteId})">' +
+                                           
'<span>{{systemactivenum+systemwarningnum+systemerrornum || 0}}</span> Node (' +
+                                           '<span>{{systemactivenum || 
0}}</span> Healthy / ' +
+                                           '<span>{{systemwarningnum || 
0}}</span> Warning / ' +
+                                           '<span>{{systemerrornum || 
0}}</span> Unhealthy)' +
+                                           '</a>' +
+                                       '</div>' +
+                                       '<div ng-show="systemactivenum===-1 || 
systemwarningnum===-1 || systemerrornum===-1" 
class="hadoopMetric-widget-detail">' +
+                                       '<span>N/A</span> Node (' +
+                                       '<span>N/A</span> Healthy / ' +
+                                       '<span>N/A</span> Warning / ' +
+                                       '<span>N/A</span> Unhealthy)' +
+                                       '</div>' +
+                                   '</div>' +
+                                   '<div class="icon">' +
+                                       '<i class="fa fa-taxi"></i>' +
+                                   '</div>' +
+                               '</div>',
+                               replace: true
+                       };
+               });
+
+               function withType(serviceType) {
+                       /**
+                        * Customize the widget content. Return false will 
prevent auto compile.
+                        * @param {{}} $element
+                        * @param {function} $element.append
+                        */
+                       return function registerWidget($element) {
+                               $element.append(
+                                       $("<div system-metric-widget 
data-type='" + serviceType + "'>")
+                               );
+                       };
+               }
+               systemMetricApp.widget("availabilitySystemChart", 
withType('System'), true);
+       });
+})();

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/eagle-system-web/src/main/webapp/package.json
----------------------------------------------------------------------
diff --git a/eagle-metric/eagle-system-web/src/main/webapp/package.json 
b/eagle-metric/eagle-system-web/src/main/webapp/package.json
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-metric/pom.xml
----------------------------------------------------------------------
diff --git a/eagle-metric/pom.xml b/eagle-metric/pom.xml
index 6bdf434..a7f8dfd 100644
--- a/eagle-metric/pom.xml
+++ b/eagle-metric/pom.xml
@@ -24,12 +24,13 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>eagle-metric-parent</artifactId>
-    <name>Eagle::App::HadoopMetric</name>
+    <name>Eagle::App::HadoopMetric::Parent</name>
     <packaging>pom</packaging>
     <modules>
         <module>eagle-hadoop-metric</module>
         <module>eagle-hdfs-web</module>
         <module>eagle-hbase-web</module>
+        <module>eagle-system-web</module>
     </modules>
 
     <dependencies>

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-server/pom.xml
----------------------------------------------------------------------
diff --git a/eagle-server/pom.xml b/eagle-server/pom.xml
index 8db44cd..7df204c 100644
--- a/eagle-server/pom.xml
+++ b/eagle-server/pom.xml
@@ -351,12 +351,17 @@
                     <version>${project.version}</version>
                 </dependency>
 
+                <!-- add metirc webUI -->
                 <dependency>
                     <groupId>org.apache.eagle</groupId>
                     <artifactId>eagle-hadoop-metric</artifactId>
                     <version>${project.version}</version>
                 </dependency>
-
+                <dependency>
+                    <groupId>org.apache.eagle</groupId>
+                    <artifactId>eagle-hbase-web</artifactId>
+                    <version>${project.version}</version>
+                </dependency>
                 <dependency>
                     <groupId>org.apache.eagle</groupId>
                     <artifactId>eagle-hdfs-web</artifactId>
@@ -364,9 +369,10 @@
                 </dependency>
                 <dependency>
                     <groupId>org.apache.eagle</groupId>
-                    <artifactId>eagle-hbase-web</artifactId>
+                    <artifactId>eagle-system-web</artifactId>
                     <version>${project.version}</version>
                 </dependency>
+
             </dependencies>
         </profile>
     </profiles>

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-topology-assembly/src/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
----------------------------------------------------------------------
diff --git 
a/eagle-topology-assembly/src/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
 
b/eagle-topology-assembly/src/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
index 54650bd..e6a3723 100644
--- 
a/eagle-topology-assembly/src/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
+++ 
b/eagle-topology-assembly/src/resources/META-INF/services/org.apache.eagle.app.spi.ApplicationProvider
@@ -32,4 +32,5 @@ org.apache.eagle.jpm.spark.history.SparkHistoryJobAppProvider
 
 ## Metric
 org.apache.eagle.metric.HdfsMetricWebApplicationProvider
-org.apache.eagle.metric.HBaseMetricWebApplicationProvider
\ No newline at end of file
+org.apache.eagle.metric.HBaseMetricWebApplicationProvider
+org.apache.eagle.metric.SystemMetricWebApplicationProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-topology-check/eagle-topology-app/pom.xml
----------------------------------------------------------------------
diff --git a/eagle-topology-check/eagle-topology-app/pom.xml 
b/eagle-topology-check/eagle-topology-app/pom.xml
index dc74b9f..55cb525 100644
--- a/eagle-topology-check/eagle-topology-app/pom.xml
+++ b/eagle-topology-check/eagle-topology-app/pom.xml
@@ -47,6 +47,11 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.eagle</groupId>
+            <artifactId>eagle-data-process</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.json</groupId>
             <artifactId>json</artifactId>
         </dependency>

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckApp.java
----------------------------------------------------------------------
diff --git 
a/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckApp.java
 
b/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckApp.java
index 87ff27a..df14bfa 100644
--- 
a/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckApp.java
+++ 
b/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckApp.java
@@ -24,6 +24,8 @@ import com.typesafe.config.Config;
 import org.apache.eagle.app.StormApplication;
 import org.apache.eagle.app.environment.impl.StormEnvironment;
 import org.apache.eagle.app.messaging.StormStreamSink;
+import org.apache.eagle.dataproc.impl.storm.kafka.KafkaSpoutProvider;
+import org.apache.eagle.topology.extractor.system.SystemCheckPersistBolt;
 import org.apache.eagle.topology.storm.HealthCheckParseBolt;
 import org.apache.eagle.topology.storm.TopologyCheckAppSpout;
 import org.apache.eagle.topology.storm.TopologyDataPersistBolt;
@@ -31,12 +33,15 @@ import 
org.apache.eagle.topology.storm.TopologyDataPersistBolt;
 public class TopologyCheckApp extends StormApplication {
 
     private static final String TOPOLOGY_HEALTH_CHECK_STREAM = 
"topology_health_check_stream";
+    private static final String SYSTEM_COLLECTOR_CONFIG_PREFIX = 
"dataSourceConfig.system";
 
     @Override
     public StormTopology execute(Config config, StormEnvironment environment) {
         TopologyCheckAppConfig topologyCheckAppConfig = 
TopologyCheckAppConfig.newInstance(config);
 
         String spoutName = 
TopologyCheckAppConfig.TOPOLOGY_DATA_FETCH_SPOUT_NAME;
+        String systemSpoutName = 
TopologyCheckAppConfig.SYSTEM_DATA_FETCH_SPOUT_NAME;
+        String systemPersistBoltName = 
TopologyCheckAppConfig.SYSTEM_ENTITY_PERSIST_BOLT_NAME;
         String persistBoltName = 
TopologyCheckAppConfig.TOPOLOGY_ENTITY_PERSIST_BOLT_NAME;
         String parseBoltName = TopologyCheckAppConfig.PARSE_BOLT_NAME;
         String kafkaSinkBoltName = TopologyCheckAppConfig.SINK_BOLT_NAME;
@@ -62,11 +67,27 @@ public class TopologyCheckApp extends StormApplication {
 
         StormStreamSink<?> sinkBolt = 
environment.getStreamSink(TOPOLOGY_HEALTH_CHECK_STREAM, config);
         topologyBuilder.setBolt(
-                kafkaSinkBoltName,
-                sinkBolt,
-                topologyCheckAppConfig.dataExtractorConfig.numKafkaSinkBolt
+            kafkaSinkBoltName,
+            sinkBolt,
+            topologyCheckAppConfig.dataExtractorConfig.numKafkaSinkBolt
         
).setNumTasks(topologyCheckAppConfig.dataExtractorConfig.numKafkaSinkBolt).shuffleGrouping(parseBoltName);
 
+        // system check data collector
+        if (topologyCheckAppConfig.systemConfig.systemInstanceEnable) {
+            topologyBuilder.setSpout(
+                systemSpoutName,
+                new 
KafkaSpoutProvider(SYSTEM_COLLECTOR_CONFIG_PREFIX).getSpout(config),
+                topologyCheckAppConfig.dataExtractorConfig.numDataFetcherSpout
+            
).setNumTasks(topologyCheckAppConfig.dataExtractorConfig.numDataFetcherSpout);
+
+            // system check data persist
+            topologyBuilder.setBolt(
+                systemPersistBoltName,
+                new SystemCheckPersistBolt(topologyCheckAppConfig),
+                topologyCheckAppConfig.dataExtractorConfig.numEntityPersistBolt
+            
).setNumTasks(topologyCheckAppConfig.dataExtractorConfig.numEntityPersistBolt).shuffleGrouping(systemSpoutName);
+        }
+
         return topologyBuilder.createTopology();
     }
 }

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckAppConfig.java
----------------------------------------------------------------------
diff --git 
a/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckAppConfig.java
 
b/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckAppConfig.java
index 7a339a6..b67867b 100644
--- 
a/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckAppConfig.java
+++ 
b/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/TopologyCheckAppConfig.java
@@ -31,6 +31,8 @@ import java.util.List;
 public class TopologyCheckAppConfig implements Serializable {
 
     public static final String TOPOLOGY_DATA_FETCH_SPOUT_NAME = 
"topologyDataFetcherSpout";
+    public static final String SYSTEM_DATA_FETCH_SPOUT_NAME = 
"systemDataFetcherSpout";
+    public static final String SYSTEM_ENTITY_PERSIST_BOLT_NAME = 
"systemEntityPersistBolt";
     public static final String TOPOLOGY_ENTITY_PERSIST_BOLT_NAME = 
"topologyEntityPersistBolt";
     public static final String PARSE_BOLT_NAME = "parserBolt";
     public static final String SINK_BOLT_NAME = "sinkBolt";
@@ -44,6 +46,7 @@ public class TopologyCheckAppConfig implements Serializable {
     public HBaseConfig hBaseConfig;
     public HdfsConfig hdfsConfig;
     public MRConfig mrConfig;
+    public SystemConfig systemConfig;
     public List<TopologyConstants.TopologyType> topologyTypes;
 
     private Config config;
@@ -113,6 +116,15 @@ public class TopologyCheckAppConfig implements 
Serializable {
             hdfsConfig = new HdfsConfig();
             hdfsConfig.namenodeUrls = 
config.getString("dataSourceConfig.hdfs.namenodeUrl").split(",\\s*");
         }
+
+        if (config.hasPath("dataSourceConfig.system.enabled") && 
config.getBoolean("dataSourceConfig.system.enabled")) {
+            systemConfig = new SystemConfig();
+            systemConfig.systemInstanceKafkaTopic = 
getOptionalConfig("dataSourceConfig.system.topic", null);
+            systemConfig.systemInstanceKafkaSchemeCls = 
getOptionalConfig("dataSourceConfig.system.schemeCls", null);
+            systemConfig.systemInstanceZkQuorum = 
getOptionalConfig("dataSourceConfig.system.zkConnection", null);
+            systemConfig.systemInstanceSendBatchSize = 
config.getInt("dataSourceConfig.system.dataSendBatchSize");
+            systemConfig.systemInstanceEnable = 
config.getBoolean("dataSourceConfig.system.enabled");
+        }
     }
 
     public static class DataExtractorConfig implements Serializable {
@@ -144,6 +156,14 @@ public class TopologyCheckAppConfig implements 
Serializable {
         public String[] namenodeUrls;
     }
 
+    public static class SystemConfig implements Serializable {
+        public String systemInstanceKafkaTopic;
+        public String systemInstanceZkQuorum;
+        public String systemInstanceKafkaSchemeCls;
+        public int systemInstanceSendBatchSize;
+        public boolean systemInstanceEnable;
+    }
+
     private String getOptionalConfig(String key, String defaultValue) {
         if (this.config.hasPath(key)) {
             return config.getString(key);

http://git-wip-us.apache.org/repos/asf/eagle/blob/877d3e77/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/extractor/system/SystemCheckPersistBolt.java
----------------------------------------------------------------------
diff --git 
a/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/extractor/system/SystemCheckPersistBolt.java
 
b/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/extractor/system/SystemCheckPersistBolt.java
new file mode 100644
index 0000000..255f7ca
--- /dev/null
+++ 
b/eagle-topology-check/eagle-topology-app/src/main/java/org/apache/eagle/topology/extractor/system/SystemCheckPersistBolt.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.eagle.topology.extractor.system;
+
+import backtype.storm.task.OutputCollector;
+import backtype.storm.task.TopologyContext;
+import backtype.storm.topology.OutputFieldsDeclarer;
+import backtype.storm.topology.base.BaseRichBolt;
+import backtype.storm.tuple.Fields;
+import backtype.storm.tuple.Tuple;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.eagle.log.entity.GenericServiceAPIResponseEntity;
+import org.apache.eagle.service.client.IEagleServiceClient;
+import org.apache.eagle.service.client.impl.BatchSender;
+import org.apache.eagle.service.client.impl.EagleServiceClientImpl;
+import org.apache.eagle.topology.TopologyCheckAppConfig;
+import org.apache.eagle.topology.TopologyConstants;
+import org.apache.eagle.topology.entity.SystemServiceTopologyAPIEntity;
+import org.apache.eagle.topology.resolver.TopologyRackResolver;
+import org.apache.eagle.topology.resolver.impl.DefaultTopologyRackResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.eagle.topology.TopologyConstants.*;
+
+/**
+ * system check persist bolt.
+ */
+public class SystemCheckPersistBolt extends BaseRichBolt {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SystemCheckPersistBolt.class);
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    static {
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
false);
+    }
+
+    private final int batchSize;
+    private OutputCollector collector;
+    private TopologyCheckAppConfig config;
+    private IEagleServiceClient client;
+    private BatchSender batchSender;
+    private TopologyRackResolver rackResolver;
+    private String site;
+
+    public SystemCheckPersistBolt(TopologyCheckAppConfig config) {
+        this.config = config;
+        this.site = config.dataExtractorConfig.site;
+        this.batchSize = config.systemConfig.systemInstanceSendBatchSize > 0 ? 
config.systemConfig.systemInstanceSendBatchSize : 1;
+    }
+
+    @Override
+    public void prepare(Map map, TopologyContext topologyContext, 
OutputCollector outputCollector) {
+        this.collector = outputCollector;
+        this.client = new EagleServiceClientImpl(config.getConfig());
+        if (this.batchSize > 0) {
+            this.batchSender = client.batch(this.batchSize);
+        }
+        this.rackResolver = new DefaultTopologyRackResolver();
+        if (config.dataExtractorConfig.resolverCls != null) {
+            try {
+                rackResolver = 
config.dataExtractorConfig.resolverCls.newInstance();
+                rackResolver.prepare(config);
+            } catch (InstantiationException | IllegalAccessException e) {
+                LOG.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    @Override
+    public void declareOutputFields(OutputFieldsDeclarer declarer) {
+        declarer.declare(new Fields(TopologyConstants.SERVICE_NAME_FIELD, 
TopologyConstants.TOPOLOGY_DATA_FIELD));
+    }
+
+    @Override
+    public void execute(Tuple tuple) {
+        long excuteTimestamp = System.currentTimeMillis();
+        if (tuple == null) {
+            return;
+        }
+        // transform tuple to SystemServiceTopologyAPIEntity
+        String metrix = tuple.getString(0);
+        try {
+            SystemServiceTopologyAPIEntity entity = createEntity(metrix, 
excuteTimestamp);
+            if (batchSize <= 1) {
+                GenericServiceAPIResponseEntity<String> response = 
this.client.create(Collections.singletonList(entity));
+                if (!response.isSuccess()) {
+                    LOG.error("Service side error: {}", 
response.getException());
+                    collector.reportError(new 
IllegalStateException(response.getException()));
+                }
+            } else {
+                batchSender.send(entity);
+            }
+            this.collector.ack(tuple);
+        } catch (Exception e) {
+            LOG.error(e.getMessage(), e);
+            this.collector.fail(tuple);
+        }
+
+    }
+
+    private SystemServiceTopologyAPIEntity createEntity(String metrix, long 
excuteTimestamp) throws IOException {
+        SystemServiceTopologyAPIEntity entity = mapper.readValue(metrix, 
SystemServiceTopologyAPIEntity.class);
+        Map<String, String> tags = new HashMap<String, String>();
+        entity.setTags(tags);
+        tags.put(SITE_TAG, this.site);
+        tags.put(ROLE_TAG, TopologyConstants.SYSTEM_ROLE);
+        tags.put(HOSTNAME_TAG, entity.getHost());
+        String rack = rackResolver.resolve(entity.getHost());
+        tags.put(RACK_TAG, rack);
+        entity.setLastUpdateTime(excuteTimestamp);
+        entity.setTimestamp(excuteTimestamp);
+        // status
+        entity.setStatus("active");
+        return entity;
+    }
+}

Reply via email to