You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2016/02/05 22:56:29 UTC

[14/40] ambari git commit: AMBARI-14893. Add Grafana-based Ambari Metrics Dashboard Builder. (Prajwal Rao via yusaku)

AMBARI-14893. Add Grafana-based Ambari Metrics Dashboard Builder. (Prajwal Rao via yusaku)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/8ff0b5ed
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/8ff0b5ed
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/8ff0b5ed

Branch: refs/heads/branch-dev-patch-upgrade
Commit: 8ff0b5ed82784a202978017345348d0d130b64af
Parents: d0bec5c
Author: Yusaku Sako <yu...@hortonworks.com>
Authored: Thu Feb 4 11:05:37 2016 -0800
Committer: Yusaku Sako <yu...@hortonworks.com>
Committed: Thu Feb 4 11:05:37 2016 -0800

----------------------------------------------------------------------
 ambari-metrics/ambari-metrics-grafana/README.md | 243 +++++++++++++++
 .../ambari-metrics/datasource.js                | 304 +++++++++++++++++++
 .../ambari-metrics/directives.js                |  36 +++
 .../ambari-metrics/partials/config.html         |  19 ++
 .../ambari-metrics/partials/query.editor.html   | 133 ++++++++
 .../ambari-metrics/partials/query.options.html  |  42 +++
 .../ambari-metrics/plugin.json                  |  14 +
 .../ambari-metrics/queryCtrl.js                 | 131 ++++++++
 ambari-metrics/pom.xml                          |   1 +
 9 files changed, 923 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/README.md
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/README.md b/ambari-metrics/ambari-metrics-grafana/README.md
new file mode 100644
index 0000000..0a138e8
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/README.md
@@ -0,0 +1,243 @@
+<!--
+  ~ 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.
+  -->
+# AMS (Ambari Metrics Service) Datasource Plugin for Grafana
+
+Use **ambari-metrics** to visualize metrics exposed via AMS in Grafana. 
+
+### If you already have Ambari Metrics UI as a part of your AMS Install, [go here](#createdash) to get started
+
+
+**ToC**
+ - [Install Grafana](#installg)
+ - [Install Datasource Plugin](#installam)
+ - [Access Grafana](#accessgraf)
+ - [Add Datasource to Grafana](#addds)
+  	- [Test Datasource](#testds)
+ - [Create Dashboard](#createdash)
+ - [Add a Graph](#addgraph)
+ - [Save Dashboard](#savedash)
+ - [Time Ranges](#timerange)
+ - [Edit Panel/Graph](#editpanel)
+
+
+----------
+![enter image description here](screenshots/full-dashboard.png)
+
+----------
+<a name="installg"></a>
+### Install Grafana
+
+
+You can install Grafana on any host.  It does not need to be co-located with Ambari Metrics Collector.  The only requirement is that it has network access to Ambari Metrics Collector.
+
+**Install on CentOS/Red Hat:**
+```
+sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-2.6.0-1.x86_64.rpm
+```
+
+**Install on Ubuntu/Debian:**
+```
+wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.6.0_amd64.deb
+sudo apt-get install -y adduser libfontconfig
+sudo dpkg -i grafana_2.6.0_amd64.deb
+```
+
+**Install on SUSE/SLES:**
+```
+sudo rpm -i --nodeps grafana-2.6.0-1.x86_64.rpm
+```
+<a name="installam"></a> 
+### Deploy ambari-metrics 
+
+**On your Grafana Server**
+
+```
+cp -R ambari/ambari-metrics/ambari-metrics-grafana/ambari-metrics /usr/share/grafana/public/app/plugins/datasource
+```
+
+### Start Grafana
+
+```
+sudo service grafana-server start
+```
+
+<a name="accessgraf"></a> 
+### Access Grafana
+
+```
+http://GRAFANA_HOST:3000 
+```
+
+---
+
+<a name="addds"></a>
+## Add Ambari Metrics Datasource in Grafana UI
+
+**Add a Datasource**
+>	- Click on "Datasources"
+> 	- Click on "Add New" at the top 
+
+![add-datasource](screenshots/1-add-datasource.png)
+
+**Add a Datasource (continued)**
+
+> 	1. Name of your Datasource
+>	2. Type = AmbariMetrics
+> 	3. Host+Port of your AMS installation. (usually host:6188) 
+>  		- No trailing slashes
+> 		- Nothing else needs to be changed
+>  		- Click on Save.
+
+![datasource-details](screenshots/2-datasource-details.png)
+
+
+<a name="testds"></a>
+**Test your Datasource**
+
+>	To make sure it's all working, click on **Test Connection** and you should see a message that says "Data source is working". 
+
+
+![test-datasource](screenshots/3-test-datasource.png)
+
+---
+
+<a name="createdash"></a>
+## Creating a Dashboard
+
+**To create a dashboard**
+
+>	- Click on Dashboards on the left
+>	- Click on "Home"
+>	- Click on New at the bottom of the dropdown 
+
+![Dashboard Dropdown](screenshots/4-dashboard-dropdown.png)
+
+
+
+**To add a panel to your newly created dashboard**
+
+>	- Click on the green button on the left(see image below)
+>	- This will expand a flyout menu that will allow you to add a panel
+>	- Choose Graph / Table / Single Stat
+
+![Add Panel](screenshots/5-dashboard-graph-menu.png)
+
+![Types of Panels](screenshots/6-graph-panels.png)
+
+
+---
+
+<a name="addgraph"></a>
+**To add a Graph**
+
+
+>	- Choose the Datasource you created earlier
+>	- Once you've chosen the datasource, you should see the query editor show you some options
+
+![Add a Graph](screenshots/7-choose-datasource.png)
+
+
+
+>	- Choose the component you wish to see metrics for
+
+![Add a Graph](screenshots/8-choose-component.png)
+
+
+
+>	- Based on the component chosen, you should now see a list of metrics for it
+
+![Add a Graph](screenshots/9-choose-metric.png)
+
+
+
+>	- Choose hostname from the list of hosts if you wish to see metrics for a specific host.
+>		- if hostname isn't chosen, metrics will be shown on a service component level. 
+
+![Add a Graph](screenshots/10-choose-hostname.png)
+
+
+> 	- By default the aggregator is avg. You can change it via the dropdown
+> 	- You can choose to enable Rate by selecting the checkbox.
+> 	- You can specify precision by checking the box and then selecting "days, hours, minutes or seconds"
+
+![Select Options](screenshots/11-choose-agg-rate-precision.png)
+
+
+**To change the title of the Panel**
+
+>	- Click on the "General" tab
+>	- Enter the name to change the title of the panel
+
+![Change Panel Title](screenshots/12-change-panel-title.png)
+
+**To change the Units for your metric**
+
+>	- You can edit the units of your graph by clicking on **Axes & Grid** tab and clicking on "unit" as shown.
+
+![Change Units](screenshots/15-change-units.png)
+
+**To customise your graphs**
+
+> 	- You can customise your graph by clicking on the **Display Styles** tab.
+> 	- For ex: you can change the color of a specific metric by choosing a series specific override at the bottom.
+
+![series specific override](screenshots/17-series-specific-override.png)
+
+
+<a name="savedash"></a>
+**To Save the Dashboard**
+
+> 	- Click on the save icon next to the dashboard list dropdown on the top to save your dashboard.
+
+![Save Dashboard](screenshots/13-save-dashboard.png)
+
+<a name="editpanel"></a>
+**To Edit a Graph**
+
+> 	- Click on the title of your graph/panel and click on edit.
+
+![Edit Graph](screenshots/19-edit-graph.png)
+
+
+---
+<a name="timerange"></a>
+### Time Ranges
+
+**To change the Time Range**
+
+>	- To change the timerange click on the top right of your UI.
+>		- This setting affects all your graphs inside the dashboard. If you wish to customise time for a specific graph [look here](#timeshift)
+> 	- You can use the quick ranges provided or choose a time range of your choice. You can also choose a refresh duration for your dashboard or leave it at "off" to manually refresh.
+
+![Timerange](screenshots/14-change-timerange.png)
+
+<a name="timeshift"></a>
+**To change the time range of one graph only**
+
+>	- Use this in case you wish to change the time range for a specific graph without affecting the other graphs in your dashboard
+>		- Click on the **Time Range** tab of your Graph
+>		- You can then enter a value in the "Override Relative time" input box
+>		- You will be able to confirm that this change has occured by looking at the top right of your graph which will show the override message.
+>		- You can choose to hide this message if you wish to do so (by checking the "hide time override info")
+
+![Timerange Override](screenshots/18-override-time.png)
+
+
+---
+
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/datasource.js
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/datasource.js b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/datasource.js
new file mode 100644
index 0000000..374501c
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/datasource.js
@@ -0,0 +1,304 @@
+/**
+ * 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.
+ */
+define([
+      'angular',
+      'lodash',
+      'jquery',
+      './directives',
+      './queryCtrl'
+    ],
+    function (angular, _) {
+      'use strict';
+
+      var module = angular.module('grafana.services');
+
+      module.factory('AmbariMetricsDatasource', function ($q, backendSrv) {
+        /**
+         * AMS Datasource Constructor
+         */
+        function AmbariMetricsDatasource(datasource) {
+          this.name = datasource.name;
+          this.url = datasource.url;
+          this.initMetricAppidMapping();
+        }
+        var allMetrics = [];
+        var appIds = [];
+        AmbariMetricsDatasource.prototype.initMetricAppidMapping = function () {
+          backendSrv.get(this.url + '/ws/v1/timeline/metrics/metadata')
+            .then(function (items) {
+              allMetrics = [];
+              appIds = [];
+              var fake = "timeline_metric_store_watcher"; delete items[fake];
+              for (var key in items) {
+                if (items.hasOwnProperty(key)) {
+                  items[key].forEach(function (_item) {
+                    allMetrics.push({
+                      metric: _item.metricname,
+                      app: key
+                    });
+                  });
+                }
+                appIds = _.keys(items);
+              }
+            });
+        };
+
+        /**
+         * AMS Datasource  Authentication
+         */
+        AmbariMetricsDatasource.prototype.doAmbariRequest = function (options) {
+          if (this.basicAuth || this.withCredentials) {
+            options.withCredentials = true;
+          }
+          if (this.basicAuth) {
+            options.headers = options.headers || {};
+            options.headers.Authorization = this.basicAuth;
+          }
+
+          options.url = this.url + options.url;
+          options.inspect = {type: 'discovery'};
+
+          return backendSrv.datasourceRequest(options);
+        };
+
+        /**
+         * AMS Datasource  Query
+         */
+        AmbariMetricsDatasource.prototype.query = function (options) {
+          var emptyData = function (metric) {
+            return {
+              data: {
+                target: metric,
+                datapoints: []
+              }
+            };
+          };
+          var self = this;
+          var getMetricsData = function (target) {
+            return function (res) {
+              console.log('processing metric ' + target.metric);
+              if (!res.metrics[0] || target.hide) {
+                return $q.when(emptyData(target.metric));
+              }
+              var series = [];
+              var metricData = res.metrics[0].metrics;
+              var timeSeries = {};
+              if (target.hosts === undefined || target.hosts.trim() === "") {
+                timeSeries = {
+                  target: target.metric,
+                  datapoints: []
+                };
+              } else {
+                timeSeries = {
+                  target: target.metric + ' on ' + target.hosts,
+                  datapoints: []
+                };
+              }
+              for (var k in metricData){
+                if (metricData.hasOwnProperty(k)) {
+                  timeSeries.datapoints.push([metricData[k], (k - k % 1000)]);
+                }
+              }
+              series.push(timeSeries);
+              return $q.when({data: series});
+            };
+
+          };
+          var precisionSetting = '';
+          var getHostAppIdData = function(target) {
+            if (target.shouldAddPrecision) {
+              precisionSetting = '&precision=' + target.precision;
+            } else {
+              precisionSetting = '';
+            }
+            if (target.shouldAddPrecision && target.shouldComputeRate) {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._rate._"
+                + target.aggregator + "&hostname=" + target.hosts + '&appId=' + target.app + '&startTime=' + from
+                + '&endTime=' + to + precisionSetting).then(
+                  getMetricsData(target)
+                );
+            } else if (target.shouldComputeRate) {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._rate._"
+                + target.aggregator + "&hostname=" + target.hosts + '&appId=' + target.app + '&startTime=' + from
+                + '&endTime=' + to).then(
+                  getMetricsData(target)
+                );
+            } else if (target.shouldAddPrecision){
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._"
+                + target.aggregator + "&hostname=" + target.hosts + '&appId=' + target.app + '&startTime=' + from
+                + '&endTime=' + to + precisionSetting).then(
+                  getMetricsData(target)
+                );
+            } else {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._"
+                + target.aggregator + "&hostname=" + target.hosts + '&appId=' + target.app + '&startTime=' + from
+                + '&endTime=' + to).then(
+                getMetricsData(target)
+              );
+            }
+          };
+
+          var getServiceAppIdData = function(target) {
+            if (target.shouldAddPrecision) { precisionSetting = '&precision=' + target.precision;
+            } else { precisionSetting = ''; }
+            if (target.shouldAddPrecision && target.shouldComputeRate) {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._rate._"
+                + target.aggregator + '&appId=' + target.app + '&startTime=' + from + '&endTime=' + to + precisionSetting)
+              .then(
+                getMetricsData(target)
+              );
+            } else if (target.shouldAddPrecision) {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._"
+                + target.aggregator + '&appId=' + target.app + '&startTime=' + from + '&endTime=' + to + precisionSetting)
+              .then(
+                getMetricsData(target)
+              );
+            } else if (target.shouldComputeRate) {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._rate._"
+                + target.aggregator + '&appId=' + target.app + '&startTime=' + from + '&endTime=' + to).then(
+                getMetricsData(target)
+              );
+            } else {
+              return backendSrv.get(self.url + '/ws/v1/timeline/metrics?metricNames=' + target.metric + "._"
+                + target.aggregator + '&appId=' + target.app + '&startTime=' + from + '&endTime=' + to).then(
+                getMetricsData(target)
+              );
+            }
+          };
+
+          // Time Ranges
+          var from = Math.floor(options.range.from.valueOf() / 1000);
+          var to = Math.floor(options.range.to.valueOf() / 1000);
+
+          var metricsPromises = _.map(options.targets, function(target) {
+            console.debug('target app=' + target.app + ',' +
+              'target metric=' + target.metric + ' on host=' + target.hosts);
+            if (!!target.hosts) {
+              return getHostAppIdData(target);
+            } else {
+              return getServiceAppIdData(target);
+            }
+          });
+          return $q.all(metricsPromises).then(function(metricsDataArray) {
+            var data = _.map(metricsDataArray, function(metricsData) {
+              return metricsData.data;
+            });
+            var metricsDataResult = {data: _.flatten(data)};
+            return $q.when(metricsDataResult);
+          });
+        };
+
+        /**
+         * AMS Datasource  List Series.
+         */
+        AmbariMetricsDatasource.prototype.listSeries = function (query) {
+          // wrap in regex
+          if (query && query.length > 0 && query[0] !== '/') {
+            query = '/' + query + '/';
+          }
+          return $q.when([]);
+        };
+
+        /**
+         * AMS Datasource  - Test Data Source Connection.
+         *
+         * Added Check to see if Datasource is working. Throws up an error in the
+         * Datasources page if incorrect info is passed on.
+         */
+        AmbariMetricsDatasource.prototype.testDatasource = function () {
+          return backendSrv.datasourceRequest({
+            url: this.url + '/ws/v1/timeline/metrics/metadata',
+            method: 'GET'
+          }).then(function(response) {
+            console.log(response);
+            if (response.status === 200) {
+              return { status: "success", message: "Data source is working", title: "Success" };
+            }
+          });
+        };
+
+        /**
+         * AMS Datasource - Suggest AppId.
+         *
+         * Read AppIds from cache.
+         */
+        AmbariMetricsDatasource.prototype.suggestApps = function (query) {
+          console.log(query);
+
+          appIds = appIds.sort();
+          var appId = _.map(appIds, function (k) {
+            return {text: k};
+          });
+          return $q.when(appId);
+        };
+
+        /**
+         * AMS Datasource - Suggest Metrics.
+         *
+         * Read Metrics based on AppId chosen.
+         */
+        AmbariMetricsDatasource.prototype.suggestMetrics = function (query, app) {
+          if (!app) {
+            return $q.when([]);
+          }
+          var metrics = allMetrics.filter(function(item) {
+            return (item.app === app);
+          });
+          var keys = [];
+          _.forEach(metrics, function (k) { keys.push(k.metric); });
+          keys = _.map(keys,function(m) {
+            return {text: m};
+          });
+          keys = _.sortBy(keys, function (i) { return i.text.toLowerCase(); });
+          return $q.when(keys);
+        };
+
+        /**
+         * AMS Datasource - Suggest Hosts.
+         *
+         * Query Hosts on the cluster.
+         */
+        AmbariMetricsDatasource.prototype.suggestHosts = function (query) {
+          console.log(query);
+          return this.doAmbariRequest({method: 'GET', url: '/ws/v1/timeline/metrics/hosts'})
+            .then(function (results) {
+              var fake = "fakehostname"; delete results.data[fake];
+              return _.map(Object.keys(results.data), function (hostName) {
+                return {text: hostName};
+              });
+            });
+        };
+
+        /**
+         * AMS Datasource Aggregators.
+         */
+        var aggregatorsPromise = null;
+        AmbariMetricsDatasource.prototype.getAggregators = function () {
+          if (aggregatorsPromise) {
+            return aggregatorsPromise;
+          }
+          aggregatorsPromise = $q.when([
+            'avg', 'sum', 'min', 'max'
+          ]);
+          return aggregatorsPromise;
+        };
+
+        return AmbariMetricsDatasource;
+      });
+    }
+);

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/directives.js
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/directives.js b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/directives.js
new file mode 100644
index 0000000..aade7d7
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/directives.js
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ */
+ define([
+  'angular',
+],
+function (angular) {
+  'use strict';
+  var module = angular.module('grafana.directives');
+
+  module.directive('metricQueryEditorAmbarimetrics', function() {
+    return {
+      controller: 'AmbariMetricsQueryCtrl',
+      templateUrl: 'app/plugins/datasource/ambari-metrics/partials/query.editor.html',
+    };
+  });
+
+  module.directive('metricQueryOptionsAmbarimetrics', function() {
+    return {templateUrl: 'app/plugins/datasource/ambari-metrics/partials/query.options.html'};
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/config.html
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/config.html b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/config.html
new file mode 100644
index 0000000..360c15c
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/config.html
@@ -0,0 +1,19 @@
+<!--
+  ~ 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 ng-include="httpConfigPartialSrc"></div>
+<br>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.editor.html
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.editor.html b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.editor.html
new file mode 100644
index 0000000..d4dffb4
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.editor.html
@@ -0,0 +1,133 @@
+<!--
+  ~ 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="tight-form">
+    <ul class="tight-form-list pull-right">
+        <li class="tight-form-item small" ng-show="target.datasource">
+            <em>{{target.datasource}}</em>
+        </li>
+        <li class="tight-form-item">
+            <div class="dropdown">
+                <a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1">
+                    <i class="fa fa-bars"></i>
+                </a>
+                <ul class="dropdown-menu pull-right" role="menu">
+                    <li role="menuitem"><a tabindex="1" ng-click="toggleQueryMode()">Switch editor mode</a></li>
+                    <li role="menuitem"><a tabindex="1" ng-click="duplicateDataQuery(target)">Duplicate</a></li>
+                    <li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index-1)">Move up</a></li>
+                    <li role="menuitem"><a tabindex="1" ng-click="moveDataQuery($index, $index+1)">Move down</a></li>
+                </ul>
+            </div>
+        </li>
+        <li class="tight-form-item last">
+            <a class="pointer" tabindex="1" ng-click="removeDataQuery(target)">
+                <i class="fa fa-remove"></i>
+            </a>
+        </li>
+    </ul>
+
+    <ul class="tight-form-list">
+        <li class="tight-form-item" style="min-width: 15px; text-align: center">
+            {{target.refId}}
+        </li>
+        <li>
+            <a  class="tight-form-item"
+                ng-click="target.hide = !target.hide; get_data();"
+                role="menuitem">
+                <i class="fa fa-eye"></i>
+            </a>
+        </li>
+    </ul>
+
+    <ul class="tight-form-list" role="menu">
+
+        <li class="tight-form-item" style="width: 86px">
+        Component
+    </li>
+        <li>
+            <input type="text" class="input-large tight-form-input" ng-model="target.app"
+                   spellcheck='false' bs-typeahead="suggestApps" placeholder="Component Name" data-min-length=0 data-items=100
+                   ng-blur="targetBlur()">
+            </input>
+            <a bs-tooltip="target.errors.metric" style="color: rgb(229, 189, 28)" ng-show="target.errors.metric">
+                <i class="fa fa-warning"></i>
+            </a>
+        </li>
+
+        <li class="tight-form-item" style="width: 86px">
+            Metric
+        </li>
+        <li>
+            <input type="text" class="input-large tight-form-input" ng-model="target.metric"
+                   spellcheck='false' bs-typeahead="suggestMetrics" placeholder="metric name" data-min-length=0 data-items=100
+                   ng-blur="targetBlur()">
+            </input>
+            <a bs-tooltip="target.errors.metric" style="color: rgb(229, 189, 28)" ng-show="target.errors.metric">
+                <i class="fa fa-warning"></i>
+            </a>
+        </li>
+
+
+        <li class="tight-form-item" style="width: 86px">
+            Hosts <a bs-tooltip="'if host is selected, aggregator is ignored.'" data-placement="bottom"><i class="fa fa-info-circle"></i></a>
+        </li>
+        <li>
+            <input type="text" class="input-large tight-form-input" ng-model="target.hosts"
+                   spellcheck='false' bs-typeahead="suggestHosts" placeholder="host name" data-min-length=0 data-items=100
+                   ng-blur="targetBlur()">
+            </input>
+            <a bs-tooltip="target.errors.metric" style="color: rgb(229, 189, 28)" ng-show="target.errors.metric">
+                <i class="fa fa-warning"></i>
+            </a>
+        </li>
+
+
+
+        <li class="tight-form-item">
+            Aggregator
+        </li>
+        <li>
+            <select ng-model="target.aggregator" class="tight-form-input input-small"
+                    ng-options="agg for agg in aggregators"
+                    ng-change="targetBlur()">
+            </select>
+            <a bs-tooltip="target.errors.aggregator" style="color: rgb(229, 189, 28)" ng-show="target.errors.aggregator">
+                <i class="fa fa-warning"></i>
+            </a>
+        </li>
+    </ul>
+
+    <div class="clearfix"></div>
+</div>
+
+<div class="tight-form">
+    <ul class="tight-form-list" role="menu">
+        <li class="tight-form-item tight-form-align" style="width: 86px">
+            <editor-checkbox text="Rate" model="target.shouldComputeRate" change="targetBlur()"></editor-checkbox>
+        </li>
+
+        <li class="tight-form-item tight-form-align">
+            <editor-checkbox text="Precision" model="target.shouldAddPrecision" change="targetBlur()"></editor-checkbox>
+        </li>
+        <li ng-show="target.shouldAddPrecision">
+            <select ng-model="target.precision" class="tight-form-input input-small"
+                    ng-options="precision for precision in precisions"
+                    ng-change="targetBlur()">
+            </select>
+        </li>
+    <div class="clearfix"></div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.options.html
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.options.html b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.options.html
new file mode 100644
index 0000000..e58ca64
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/partials/query.options.html
@@ -0,0 +1,42 @@
+<!--
+  ~ 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.
+  -->
+<section class="grafana-metric-options">
+	<div class="tight-form last">
+		<ul class="tight-form-list">
+			<li class="tight-form-item tight-form-item-icon">
+				<i class="fa fa-info-circle"></i>
+			</li>
+			<li class="tight-form-item">
+				<a ng-click="toggleEditorHelp(1);" bs-tooltip="'click to show helpful info'" data-placement="bottom">
+					Single Stats
+				</a>
+			</li>
+		</ul>
+		<div class="clearfix"></div>
+	</div>
+</section>
+
+<div class="editor-row">
+	<div class="pull-left" style="margin-top: 30px;">
+		<div class="grafana-info-box span6" ng-if="editorHelpIndex === 1">
+			<h5>Single Stats</h5>
+			<blockquote>To get the current value of the metric selected, Click on the <strong>Options</strong> tab above
+				and set the <strong>Big Value's</strong> value to <strong>"current"</strong>.</blockquote>
+		</div>
+	</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/plugin.json
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/plugin.json b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/plugin.json
new file mode 100644
index 0000000..5226ae7
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/plugin.json
@@ -0,0 +1,14 @@
+{
+  "pluginType": "datasource",
+  "name": "AmbariMetrics",
+
+  "type": "ambarimetrics",
+  "serviceName": "AmbariMetricsDatasource",
+
+  "module": "app/plugins/datasource/ambari-metrics/datasource",
+    "partials": {
+    "config": "app/plugins/datasource/ambari-metrics/partials/config.html"
+  },
+
+  "metrics": true
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/ambari-metrics-grafana/ambari-metrics/queryCtrl.js
----------------------------------------------------------------------
diff --git a/ambari-metrics/ambari-metrics-grafana/ambari-metrics/queryCtrl.js b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/queryCtrl.js
new file mode 100644
index 0000000..d6e93a8
--- /dev/null
+++ b/ambari-metrics/ambari-metrics-grafana/ambari-metrics/queryCtrl.js
@@ -0,0 +1,131 @@
+/**
+ * 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.
+ */
+define([
+      'angular',
+      'lodash'
+    ],
+    function (angular, _) {
+      'use strict';
+
+      var module = angular.module('grafana.controllers');
+
+      module.controller('AmbariMetricsQueryCtrl', function($scope) {
+
+        $scope.init = function() {
+          $scope.target.errors = validateTarget($scope.target);
+          $scope.aggregators = ['avg', 'sum', 'min', 'max'];
+          $scope.precisions = ['seconds', 'minutes', 'hours', 'days'];
+
+          if (!$scope.target.aggregator) {
+            $scope.target.aggregator = 'avg';
+          }
+
+          if (!$scope.target.downsampleAggregator) {
+            $scope.target.downsampleAggregator = 'avg';
+          }
+
+          $scope.datasource.getAggregators().then(function(aggs) {
+            $scope.aggregators = aggs;
+          });
+        };
+
+        $scope.targetBlur = function() {
+          $scope.target.errors = validateTarget($scope.target);
+
+          // this does not work so good
+          if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) {
+            $scope.oldTarget = angular.copy($scope.target);
+            $scope.get_data();
+          }
+        };
+
+        $scope.getTextValues = function(metricFindResult) {
+          return _.map(metricFindResult, function(value) { return value.text; });
+        };
+
+        $scope.suggestApps = function(query, callback) {
+          $scope.datasource.suggestApps(query)
+            .then($scope.getTextValues)
+            .then(callback);
+        };
+
+        $scope.suggestHosts = function(query, callback) {
+          $scope.datasource.suggestHosts(query)
+            .then($scope.getTextValues)
+            .then(callback);
+        };
+
+        $scope.suggestMetrics = function(query, callback) {
+          $scope.datasource.suggestMetrics(query, $scope.target.app)
+            .then($scope.getTextValues)
+            .then(callback);
+        };
+
+        $scope.suggestTagKeys = function(query, callback) {
+          $scope.datasource.metricFindQuery('tag_names(' + $scope.target.metric + ')')
+              .then($scope.getTextValues)
+              .then(callback);
+        };
+
+        $scope.suggestTagValues = function(query, callback) {
+          $scope.datasource.metricFindQuery('tag_values(' + $scope.target.metric + ',' + $scope.target.currentTagKey + ')')
+              .then($scope.getTextValues)
+              .then(callback);
+        };
+
+        $scope.addTag = function() {
+          if (!$scope.addTagMode) {
+            $scope.addTagMode = true;
+            return;
+          }
+
+          if (!$scope.target.tags) {
+            $scope.target.tags = {};
+          }
+
+          $scope.target.errors = validateTarget($scope.target);
+
+          if (!$scope.target.errors.tags) {
+            $scope.target.tags[$scope.target.currentTagKey] = $scope.target.currentTagValue;
+            $scope.target.currentTagKey = '';
+            $scope.target.currentTagValue = '';
+            $scope.targetBlur();
+          }
+
+          $scope.addTagMode = false;
+        };
+
+        $scope.removeTag = function(key) {
+          delete $scope.target.tags[key];
+          $scope.targetBlur();
+        };
+
+        function validateTarget(target) {
+          var errs = {};
+
+          if (target.tags && _.has(target.tags, target.currentTagKey)) {
+            errs.tags = "Duplicate tag key '" + target.currentTagKey + "'.";
+          }
+
+          return errs;
+        }
+
+        $scope.init();
+      });
+
+    });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8ff0b5ed/ambari-metrics/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-metrics/pom.xml b/ambari-metrics/pom.xml
index 3ca8d54..9a39122 100644
--- a/ambari-metrics/pom.xml
+++ b/ambari-metrics/pom.xml
@@ -276,6 +276,7 @@
             <exclude>pass.txt</exclude>
             <exclude>derby.log</exclude>
             <exclude>**/*.nuspec</exclude>
+            <exclude>**/*.json</exclude>
           </excludes>
         </configuration>
         <executions>