ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jai...@apache.org
Subject [2/3] ambari git commit: AMBARI-10648. Integrate Heatmap pages with widget and widget layout API. (jaimin)
Date Wed, 22 Apr 2015 20:48:40 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/mixins/common/widget_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widget_mixin.js b/ambari-web/app/mixins/common/widget_mixin.js
deleted file mode 100644
index e59fb20..0000000
--- a/ambari-web/app/mixins/common/widget_mixin.js
+++ /dev/null
@@ -1,426 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-
-App.WidgetMixin = Ember.Mixin.create({
-
-  /**
-   * @type {RegExp}
-   * @const
-   */
-  EXPRESSION_REGEX: /\$\{([\w\s\.\,\+\-\*\/\(\)\:\=\[\]]*)\}/g,
-
-  /**
-   * @type {RegExp}
-   * @const
-   */
-  MATH_EXPRESSION_REGEX: /^[\d\s\+\-\*\/\(\)\.]+$/,
-
-  /**
-   * @type {RegExp}
-   * @const
-   */
-  VALUE_NAME_REGEX: /[\w\.\,\:\=\[\]]+/g,
-
-  /**
-   * common metrics container
-   * @type {Array}
-   */
-  metrics: [],
-
-  /**
-   * @type {boolean}
-   */
-  isLoaded: false,
-
-  /**
-   * @type {App.Widget}
-   * @default null
-   */
-  content: null,
-
-  beforeRender: function () {
-    this.loadMetrics();
-  },
-
-
-  /**
-   * load metrics
-   */
-  loadMetrics: function () {
-    var requestData = this.getRequestData(this.get('content.metrics')),
-      request,
-      requestCounter = 0,
-      self = this;
-
-    for (var i in requestData) {
-      request = requestData[i];
-      requestCounter++;
-      if (request.host_component_criteria) {
-        this.getHostComponentMetrics(request).always(function () {
-          requestCounter--;
-          if (requestCounter === 0) self.onMetricsLoaded();
-        });
-      } else {
-        this.getServiceComponentMetrics(request).complete(function () {
-          requestCounter--;
-          if (requestCounter === 0) self.onMetricsLoaded();
-        });
-      }
-    }
-  },
-
-  /**
-   * get data formatted for request
-   * @param {Array} metrics
-   */
-  getRequestData: function (metrics) {
-    var requestsData = {};
-    if (metrics) {
-      metrics.forEach(function (metric, index) {
-        var key;
-        if (metric.host_component_criteria) {
-          this.tweakHostComponentCriteria(metric);
-          key = metric.service_name + '_' + metric.component_name + '_' + metric.host_component_criteria;
-        } else {
-          key = metric.service_name + '_' + metric.component_name;
-        }
-        var requestMetric = $.extend({}, metric);
-
-        if (requestsData[key]) {
-          requestsData[key]["metric_paths"].push(requestMetric["metric_path"]);
-        } else {
-          requestMetric["metric_paths"] = [requestMetric["metric_path"]];
-          delete requestMetric["metric_path"];
-          requestsData[key] = requestMetric;
-        }
-      }, this);
-    }
-    return requestsData;
-  },
-
-  /**
-   * Tweak necessary host component criteria
-   * NameNode HA host component criteria is applicable only in HA mode
-   */
-  tweakHostComponentCriteria: function (metric) {
-    switch (metric.component_name) {
-      case 'NAMENODE':
-        if (metric.host_component_criteria === 'host_components/metrics/dfs/FSNamesystem/HAState=active') {
-          //if (metric.host_component_criteria)
-          var hdfs = App.HDFSService.find().objectAt(0);
-          var activeNNHostName = !hdfs.get('snameNode') && hdfs.get('activeNameNode');
-          if (!activeNNHostName) {
-            metric.host_component_criteria = 'host_components/HostRoles/component_name=NAMENODE';
-          }
-        }
-        break;
-    }
-  },
-
-  /**
-   * make GET call to server in order to fetch service-component metrics
-   * @param {object} request
-   * @returns {$.ajax}
-   */
-  getServiceComponentMetrics: function (request) {
-    return App.ajax.send({
-      name: 'widgets.serviceComponent.metrics.get',
-      sender: this,
-      data: {
-        serviceName: request.service_name,
-        componentName: request.component_name,
-        metricPaths: request.metric_paths.join(',')
-      },
-      success: 'getMetricsSuccessCallback'
-    });
-  },
-
-  /**
-   * make GET call to server in order to fetch service-component metrics
-   * @param {object} request
-   * @returns {$.Deferred}
-   */
-  getHostComponentMetrics: function (request) {
-    var dfd;
-    var self = this;
-    dfd = $.Deferred();
-    this.getHostComponentName(request).done(function (data) {
-      if (data) {
-        request.host_name = data.host_components[0].HostRoles.host_name;
-        App.ajax.send({
-          name: 'widgets.hostComponent.metrics.get',
-          sender: self,
-          data: {
-            componentName: request.component_name,
-            hostName: request.host_name,
-            metricPaths: request.metric_paths.join(',')
-          }
-        }).done(function(metricData) {
-          self.getMetricsSuccessCallback(metricData);
-          dfd.resolve();
-        }).fail(function(data){
-          dfd.reject();
-        });
-      }
-    }).fail(function(data){
-      dfd.reject();
-    });
-    return dfd.promise();
-  },
-
-  /**
-   * make GET call to server in order to fetch host-component names
-   * @param {object} request
-   * @returns {$.ajax}
-   */
-  getHostComponentName: function (request) {
-    return App.ajax.send({
-      name: 'widgets.hostComponent.get.hostName',
-      sender: this,
-      data: {
-        serviceName: request.service_name,
-        componentName: request.component_name,
-        metricPaths: request.metric_paths.join(','),
-        hostComponentCriteria: request.host_component_criteria
-      }
-    });
-  },
-
-  /**
-   * callback on getting aggregated metrics and host component metrics
-   * @param data
-   */
-  getMetricsSuccessCallback: function (data) {
-    var metrics = [];
-
-    this.get('content.metrics').forEach(function (_metric) {
-      if (!Em.isNone(Em.get(data, _metric.metric_path.replace(/\//g, '.')))) {
-        _metric.data = Em.get(data, _metric.metric_path.replace(/\//g, '.'));
-        this.get('metrics').pushObject(_metric);
-      }
-    }, this);
-  },
-
-
-  /**
-   * callback on metrics loaded
-   */
-  onMetricsLoaded: function () {
-    var self = this;
-    this.set('isLoaded', true);
-    this.drawWidget();
-    setTimeout(function () {
-      self.loadMetrics();
-    }, App.contentUpdateInterval);
-  },
-
-
-  /**
-   * draw widget
-   */
-  drawWidget: function () {
-    if (this.get('isLoaded')) {
-      this.calculateValues();
-      this.set('value', this.get('content.values')[0] && this.get('content.values')[0].computedValue);
-    }
-  },
-
-  /**
-   * calculate series datasets for graph widgets
-   */
-  calculateValues: function () {
-    var metrics = this.get('metrics');
-    var displayUnit = this.get('content.properties.display_unit');
-
-    this.get('content.values').forEach(function (value) {
-      var computeExpression = this.computeExpression(this.extractExpressions(value), metrics);
-      value.computedValue = value.value.replace(this.get('EXPRESSION_REGEX'), function (match) {
-        return (!Em.isNone(computeExpression[match])) ? computeExpression[match] + (displayUnit || "") : Em.I18n.t('common.na');
-      });
-    }, this);
-  },
-
-
-  /**
-   * extract expressions
-   * Example:
-   *  input: "${a/b} equal ${b+a}"
-   *  expressions: ['a/b', 'b+a']
-   *
-   * @param {object} input
-   * @returns {Array}
-   */
-  extractExpressions: function (input) {
-    var pattern = this.get('EXPRESSION_REGEX'),
-      expressions = [],
-      match;
-
-    while (match = pattern.exec(input.value)) {
-      expressions.push(match[1]);
-    }
-    return expressions;
-  },
-
-
-  /**
-   * compute expression
-   * @param expressions
-   * @param metrics
-   * @returns {object}
-   */
-  computeExpression: function (expressions, metrics) {
-    var result = {};
-
-    expressions.forEach(function (_expression) {
-      var validExpression = true;
-      var value = "";
-
-      //replace values with metrics data
-      var beforeCompute = _expression.replace(this.get('VALUE_NAME_REGEX'), function (match) {
-        if (metrics.someProperty('name', match)) {
-          return metrics.findProperty('name', match).data;
-        } else {
-          validExpression = false;
-          console.warn('Metrics not found to compute expression');
-        }
-      });
-
-      if (validExpression) {
-        //check for correct math expression
-        validExpression = this.get('MATH_EXPRESSION_REGEX').test(beforeCompute);
-        !validExpression && console.warn('Value is not correct mathematical expression');
-      }
-
-      result['${' + _expression + '}'] = (validExpression) ? Number(window.eval(beforeCompute)).toString() : value;
-    }, this);
-    return result;
-  },
-
-  /*
-   * make call when clicking on "remove icon" on widget
-   */
-  hideWidget: function (event) {
-    this.get('controller').hideWidget(
-      {
-        context: Em.Object.create({
-          id: event.context
-        })
-      }
-    );
-  },
-
-  /*
-   * make call when clicking on "clone icon" on widget
-   */
-  cloneWidget: function (event) {
-    var self = this;
-    return App.showConfirmationPopup(
-      function() {
-        self.postWidgetDefinition();
-      },
-      Em.I18n.t('widget.clone.body').format(self.get('content.displayName')),
-      null,
-      null,
-      Em.I18n.t('common.clone')
-    );
-  },
-
-  /**
-   * collect all needed data to create new widget
-   * @returns {{WidgetInfo: {widget_name: *, display_name: *, widget_type: *, description: *, scope: *, metrics: *, values: *, properties: *}}}
-   */
-  collectWidgetData: function () {
-    return {
-      WidgetInfo: {
-        widget_name: this.get('content.widgetName'),
-        display_name: this.get('content.displayName'),
-        widget_type: this.get('content.widgetType'),
-        description: this.get('content.widgetDescription'),
-        scope: this.get('content.scope'),
-        "metrics": this.get('content.metrics').map(function (metric) {
-          return {
-            "name": metric.name,
-            "service_name": metric.serviceName,
-            "component_name": metric.componentName,
-            "metric_path": metric.metric_path,
-            "category": metric.category
-          }
-        }),
-        values: this.get('content.values'),
-        properties: this.get('content.properties')
-      }
-    };
-  },
-
-  /**
-   * post widget definition to server
-   * @returns {$.ajax}
-   */
-  postWidgetDefinition: function () {
-    return App.ajax.send({
-      name: 'widgets.wizard.add',
-      sender: this,
-      data: {
-        data: this.collectWidgetData()
-      },
-      success: 'postWidgetDefinitionSuccessCallback'
-    });
-  },
-
-  postWidgetDefinitionSuccessCallback: function() {
-
-  },
-
-  /*
-   * make call when clicking on "edit icon" on widget
-   */
-  editWidget: function (event) {
-    this.get('controller').editWidget(this.get('content'));
-  }
-
-});
-App.WidgetPreviewMixin = Ember.Mixin.create({
-  beforeRender: Em.K,
-  isLoaded: true,
-  metrics: [],
-  content: Em.Object.create({
-    widgetName: 'mock-widget',
-    values: []
-  }),
-  drawWidget: function () {
-    this.loadMetrics();
-    this.get('content').setProperties({
-      'values': this.get('controller.widgetValues'),
-      'properties': this.get('controller.widgetProperties'),
-      'displayName': this.get('controller.widgetName')
-    });
-    this._super();
-  }.observes('controller.widgetProperties', 'controller.widgetValues', 'controller.widgetMetrics', 'controller.widgetName'),
-  loadMetrics: function () {
-    var metrics = [];
-    this.get('controller.widgetMetrics').forEach(function (metric) {
-      metrics.push({
-        name: metric.name,
-        data: this.get('MOCK_VALUE')
-      });
-    }, this);
-    this.set('metrics', metrics);
-  }
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/mixins/common/widgets/widget_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widgets/widget_mixin.js b/ambari-web/app/mixins/common/widgets/widget_mixin.js
new file mode 100644
index 0000000..8e5e110
--- /dev/null
+++ b/ambari-web/app/mixins/common/widgets/widget_mixin.js
@@ -0,0 +1,506 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.WidgetMixin = Ember.Mixin.create({
+
+  /**
+   * @type {RegExp}
+   * @const
+   */
+  EXPRESSION_REGEX: /\$\{([\w\s\.\,\+\-\*\/\(\)\:\=\[\]]*)\}/g,
+
+  /**
+   * @type {RegExp}
+   * @const
+   */
+  MATH_EXPRESSION_REGEX: /^[\d\s\+\-\*\/\(\)\.]+$/,
+
+  /**
+   * @type {RegExp}
+   * @const
+   */
+  VALUE_NAME_REGEX: /[\w\.\,\:\=\[\]]+/g,
+
+  /**
+   * common metrics container
+   * @type {Array}
+   */
+  metrics: [],
+
+  /**
+   * @type {boolean}
+   */
+  isLoaded: false,
+
+  /**
+   * @type {App.Widget}
+   * @default null
+   */
+  content: null,
+
+  beforeRender: function () {
+    this.get('metrics').clear();
+    this.loadMetrics();
+  },
+
+
+  /**
+   * load metrics
+   */
+  loadMetrics: function () {
+    var requestData = this.getRequestData(this.get('content.metrics')),
+      request,
+      requestCounter = 0,
+      self = this;
+
+    for (var i in requestData) {
+      request = requestData[i];
+      requestCounter++;
+      if (this.get('content.widgetType') === 'HEATMAP'){
+        if (request.service_name === 'STACK') {
+          this.getHostsMetrics(request).complete(function () {
+            requestCounter--;
+            if (requestCounter === 0) self.onMetricsLoaded();
+          });
+        } else {
+          this.getHostComponentsMetrics(request).complete(function () {
+            requestCounter--;
+            if (requestCounter === 0) self.onMetricsLoaded();
+          });
+        }
+      } else if (request.host_component_criteria) {
+        this.getHostComponentMetrics(request).always(function () {
+          requestCounter--;
+          if (requestCounter === 0) self.onMetricsLoaded();
+        });
+      } else {
+        this.getServiceComponentMetrics(request).complete(function () {
+          requestCounter--;
+          if (requestCounter === 0) self.onMetricsLoaded();
+        });
+      }
+    }
+  },
+
+  /**
+   * get data formatted for request
+   * @param {Array} metrics
+   */
+  getRequestData: function (metrics) {
+    var requestsData = {};
+    if (metrics) {
+      metrics.forEach(function (metric, index) {
+        var key;
+        if (metric.host_component_criteria) {
+          this.tweakHostComponentCriteria(metric);
+          key = metric.service_name + '_' + metric.component_name + '_' + metric.host_component_criteria;
+        } else {
+          key = metric.service_name + '_' + metric.component_name;
+        }
+        var requestMetric = $.extend({}, metric);
+
+        if (requestsData[key]) {
+          requestsData[key]["metric_paths"].push(requestMetric["metric_path"]);
+        } else {
+          requestMetric["metric_paths"] = [requestMetric["metric_path"]];
+          delete requestMetric["metric_path"];
+          requestsData[key] = requestMetric;
+        }
+      }, this);
+    }
+    return requestsData;
+  },
+
+  /**
+   * Tweak necessary host component criteria
+   * NameNode HA host component criteria is applicable only in HA mode
+   */
+  tweakHostComponentCriteria: function (metric) {
+    switch (metric.component_name) {
+      case 'NAMENODE':
+        if (metric.host_component_criteria === 'host_components/metrics/dfs/FSNamesystem/HAState=active') {
+          //if (metric.host_component_criteria)
+          var hdfs = App.HDFSService.find().objectAt(0);
+          var activeNNHostName = !hdfs.get('snameNode') && hdfs.get('activeNameNode');
+          if (!activeNNHostName) {
+            metric.host_component_criteria = 'host_components/HostRoles/component_name=NAMENODE';
+          }
+        }
+        break;
+    }
+  },
+
+  /**
+   * make GET call to server in order to fetch service-component metrics
+   * @param {object} request
+   * @returns {$.ajax}
+   */
+  getServiceComponentMetrics: function (request) {
+    return App.ajax.send({
+      name: 'widgets.serviceComponent.metrics.get',
+      sender: this,
+      data: {
+        serviceName: request.service_name,
+        componentName: request.component_name,
+        metricPaths: request.metric_paths.join(',')
+      },
+      success: 'getMetricsSuccessCallback'
+    });
+  },
+
+  /**
+   * make GET call to server in order to fetch specifc host-component metrics
+   * @param {object} request
+   * @returns {$.Deferred}
+   */
+  getHostComponentMetrics: function (request) {
+    var dfd;
+    var self = this;
+    dfd = $.Deferred();
+    this.getHostComponentName(request).done(function (data) {
+      if (data) {
+        request.host_name = data.host_components[0].HostRoles.host_name;
+        App.ajax.send({
+          name: 'widgets.hostComponent.metrics.get',
+          sender: self,
+          data: {
+            componentName: request.component_name,
+            hostName: request.host_name,
+            metricPaths: request.metric_paths.join(',')
+          }
+        }).done(function(metricData) {
+          self.getMetricsSuccessCallback(metricData);
+          dfd.resolve();
+        }).fail(function(data){
+          dfd.reject();
+        });
+      }
+    }).fail(function(data){
+      dfd.reject();
+    });
+    return dfd.promise();
+  },
+
+
+
+  /**
+   * make GET call to server in order to fetch host-component names
+   * @param {object} request
+   * @returns {$.ajax}
+   */
+  getHostComponentName: function (request) {
+    return App.ajax.send({
+      name: 'widgets.hostComponent.get.hostName',
+      sender: this,
+      data: {
+        serviceName: request.service_name,
+        componentName: request.component_name,
+        metricPaths: request.metric_paths.join(','),
+        hostComponentCriteria: request.host_component_criteria
+      }
+    });
+  },
+
+
+  /**
+   * callback on getting aggregated metrics and host component metrics
+   * @param data
+   */
+  getMetricsSuccessCallback: function (data) {
+    var metrics = [];
+
+    this.get('content.metrics').forEach(function (_metric) {
+      if (!Em.isNone(Em.get(data, _metric.metric_path.replace(/\//g, '.')))) {
+        _metric.data = Em.get(data, _metric.metric_path.replace(/\//g, '.'));
+        this.get('metrics').pushObject(_metric);
+      }
+    }, this);
+  },
+
+  /**
+   * make GET call to get host component metrics accross
+   * @param {object} request
+   * @return {$.ajax}
+   */
+  getHostComponentsMetrics: function(request) {
+    request.metric_paths.forEach(function(_metric,index){
+      request.metric_paths[index] = "host_components/" + _metric;
+    });
+    return App.ajax.send({
+      name: 'widgets.serviceComponent.metrics.get',
+      sender: this,
+      data: {
+        serviceName: request.service_name,
+        componentName: request.component_name,
+        metricPaths: request.metric_paths.join(',')
+      },
+      success: 'getHostComponentsMetricsSuccessCallback'
+    });
+  },
+
+
+  getHostComponentsMetricsSuccessCallback: function(data) {
+    var metrics = this.get('content.metrics');
+    data.host_components.forEach(function(item){
+      metrics.forEach(function (_metric) {
+        if (!Em.isNone(Em.get(item, _metric.metric_path.replace(/\//g, '.')))) {
+          var metric = $.extend({},_metric,true);
+          metric.data =  Em.get(item, _metric.metric_path.replace(/\//g, '.'));
+          metric.hostName = item.HostRoles.host_name;
+          this.get('metrics').pushObject(metric);
+        }
+      }, this);
+    },this);
+  },
+
+  getHostsMetrics: function(request) {
+    return App.ajax.send({
+      name: 'widgets.hosts.metrics.get',
+      sender: this,
+      data: {
+        metricPaths: request.metric_paths.join(',')
+      },
+      success: 'getHostsMetricsSuccessCallback'
+    });
+  },
+
+  getHostsMetricsSuccessCallback: function(data) {
+    var metrics = this.get('content.metrics');
+    data.items.forEach(function(item){
+      metrics.forEach(function (_metric,index) {
+        if (!Em.isNone(Em.get(item, _metric.metric_path.replace(/\//g, '.')))) {
+          var metric = $.extend({},_metric,true);
+          metric.data =  Em.get(item, _metric.metric_path.replace(/\//g, '.'));
+          metric.hostName = item.Hosts.host_name;
+          this.get('metrics').pushObject(metric);
+        }
+      }, this);
+    },this);
+  },
+
+  /**
+   * callback on metrics loaded
+   */
+  onMetricsLoaded: function () {
+    var self = this;
+    this.set('isLoaded', true);
+    this.drawWidget();
+    setTimeout(function () {
+      self.loadMetrics();
+    }, App.contentUpdateInterval);
+  },
+
+
+  /**
+   * draw widget
+   */
+  drawWidget: function () {
+    if (this.get('isLoaded')) {
+      this.calculateValues();
+      this.set('value', this.get('content.values')[0] && this.get('content.values')[0].computedValue);
+    }
+  },
+
+  /**
+   * calculate series datasets for graph widgets
+   */
+  calculateValues: function () {
+    var metrics = this.get('metrics');
+    var displayUnit = this.get('content.properties.display_unit');
+
+    this.get('content.values').forEach(function (value) {
+      var computeExpression = this.computeExpression(this.extractExpressions(value), metrics);
+      value.computedValue = value.value.replace(this.get('EXPRESSION_REGEX'), function (match) {
+        return (!Em.isNone(computeExpression[match])) ? computeExpression[match] + (displayUnit || "") : Em.I18n.t('common.na');
+      });
+    }, this);
+  },
+
+
+  /**
+   * extract expressions
+   * Example:
+   *  input: "${a/b} equal ${b+a}"
+   *  expressions: ['a/b', 'b+a']
+   *
+   * @param {object} input
+   * @returns {Array}
+   */
+  extractExpressions: function (input) {
+    var pattern = this.get('EXPRESSION_REGEX'),
+      expressions = [],
+      match;
+
+    while (match = pattern.exec(input.value)) {
+      expressions.push(match[1]);
+    }
+    return expressions;
+  },
+
+
+  /**
+   * compute expression
+   * @param expressions
+   * @param metrics
+   * @returns {object}
+   */
+  computeExpression: function (expressions, metrics) {
+    var result = {};
+
+    expressions.forEach(function (_expression) {
+      var validExpression = true;
+      var value = "";
+
+      //replace values with metrics data
+      var beforeCompute = _expression.replace(this.get('VALUE_NAME_REGEX'), function (match) {
+        if (window.isNaN(match)) {
+          if (metrics.someProperty('name', match)) {
+            return metrics.findProperty('name', match).data;
+          } else {
+            validExpression = false;
+            console.error('Metrics with name "' + match + '" not found to compute expression');
+          }
+        } else {
+          return match;
+        }
+      });
+
+      //check for correct math expression
+      if (!(validExpression && this.get('MATH_EXPRESSION_REGEX').test(beforeCompute))) {
+        validExpression = false;
+        console.error('Value for metric is not correct mathematical expression: ' + beforeCompute);
+      }
+
+      result['${' + _expression + '}'] = (validExpression) ? Number(window.eval(beforeCompute)).toString() : value;
+    }, this);
+    return result;
+  },
+
+  /*
+   * make call when clicking on "remove icon" on widget
+   */
+  hideWidget: function (event) {
+    this.get('controller').hideWidget(
+      {
+        context: Em.Object.create({
+          id: event.context
+        })
+      }
+    );
+  },
+
+  /*
+   * make call when clicking on "clone icon" on widget
+   */
+  cloneWidget: function (event) {
+    var self = this;
+    return App.showConfirmationPopup(
+      function() {
+        self.postWidgetDefinition();
+      },
+      Em.I18n.t('widget.clone.body').format(self.get('content.displayName')),
+      null,
+      null,
+      Em.I18n.t('common.clone')
+    );
+  },
+
+  /**
+   * collect all needed data to create new widget
+   * @returns {{WidgetInfo: {widget_name: *, display_name: *, widget_type: *, description: *, scope: *, metrics: *, values: *, properties: *}}}
+   */
+  collectWidgetData: function () {
+    return {
+      WidgetInfo: {
+        widget_name: this.get('content.widgetName'),
+        display_name: this.get('content.displayName'),
+        widget_type: this.get('content.widgetType'),
+        description: this.get('content.widgetDescription'),
+        scope: this.get('content.scope'),
+        "metrics": this.get('content.metrics').map(function (metric) {
+          return {
+            "name": metric.name,
+            "service_name": metric.serviceName,
+            "component_name": metric.componentName,
+            "metric_path": metric.metric_path,
+            "category": metric.category
+          }
+        }),
+        values: this.get('content.values'),
+        properties: this.get('content.properties')
+      }
+    };
+  },
+
+  /**
+   * post widget definition to server
+   * @returns {$.ajax}
+   */
+  postWidgetDefinition: function () {
+    return App.ajax.send({
+      name: 'widgets.wizard.add',
+      sender: this,
+      data: {
+        data: this.collectWidgetData()
+      },
+      success: 'postWidgetDefinitionSuccessCallback'
+    });
+  },
+
+  postWidgetDefinitionSuccessCallback: function() {
+
+  },
+
+  /*
+   * make call when clicking on "edit icon" on widget
+   */
+  editWidget: function (event) {
+    this.get('controller').editWidget(this.get('content'));
+  }
+
+});
+App.WidgetPreviewMixin = Ember.Mixin.create({
+  beforeRender: Em.K,
+  isLoaded: true,
+  metrics: [],
+  content: Em.Object.create({
+    widgetName: 'mock-widget',
+    values: []
+  }),
+  drawWidget: function () {
+    this.loadMetrics();
+    this.get('content').setProperties({
+      'values': this.get('controller.widgetValues'),
+      'properties': this.get('controller.widgetProperties'),
+      'displayName': this.get('controller.widgetName')
+    });
+    this._super();
+  }.observes('controller.widgetProperties', 'controller.widgetValues', 'controller.widgetMetrics', 'controller.widgetName'),
+  loadMetrics: function () {
+    var metrics = [];
+    this.get('controller.widgetMetrics').forEach(function (metric) {
+      metrics.push({
+        name: metric.name,
+        data: this.get('MOCK_VALUE')
+      });
+    }, this);
+    this.set('metrics', metrics);
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/mixins/common/widgets/widget_section.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widgets/widget_section.js b/ambari-web/app/mixins/common/widgets/widget_section.js
new file mode 100644
index 0000000..c8b76e9
--- /dev/null
+++ b/ambari-web/app/mixins/common/widgets/widget_section.js
@@ -0,0 +1,146 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.WidgetSectionMixin = Ember.Mixin.create({
+  /**
+   * UI default layout name
+   */
+  defaultLayoutName: function () {
+    var heatmapType;
+    if (this.get('content.serviceName')) {
+      heatmapType = this.get('content.serviceName').toLowerCase();
+    } else {
+      heatmapType = "system";
+    }
+    return "default_" + heatmapType + this.layoutNameSuffix;
+  }.property('content.serviceName'),
+
+  /**
+   * UI section name
+   */
+  sectionName: function () {
+    if (this.get('content.serviceName')) {
+      return this.get('content.serviceName') + this.sectionNameSuffix;
+    } else {
+      return "SYSTEM"  + this.sectionNameSuffix
+    }
+  }.property('content.serviceName'),
+
+
+
+  /**
+   * Does Service has widget descriptor defined in the stack
+   * @type {boolean}
+   */
+  isServiceWithEnhancedWidgets: function () {
+    var isServiceWithWidgetdescriptor;
+    var serviceName = this.get('content.serviceName');
+    if (serviceName) {
+      isServiceWithWidgetdescriptor = App.StackService.find().findProperty('serviceName', serviceName).get('isServiceWithWidgets');
+    } else if (this.get('sectionName') === 'SYSTEM_HEATMAPS') {
+      isServiceWithWidgetdescriptor = true;
+    }
+    return isServiceWithWidgetdescriptor && App.supports.customizedWidgets;
+  }.property('content.serviceName'),
+
+  /**
+   *  @Type {App.WidgetLayout}
+   */
+  activeWidgetLayout: {},
+
+  /**
+   * @type {Em.A}
+   */
+  widgets: function () {
+    if (this.get('isWidgetsLoaded')) {
+      if (this.get('activeWidgetLayout.widgets')) {
+        return this.get('activeWidgetLayout.widgets').toArray();
+      } else {
+        return  [];
+      }
+    }
+  }.property('isWidgetsLoaded'),
+
+  /**
+   * load widgets defined by user
+   * @returns {$.ajax}
+   */
+  loadActiveWidgetLayout: function () {
+    this.set('activeWidgetLayout', {});
+    this.set('isWidgetsLoaded', false);
+    if (this.get('isServiceWithEnhancedWidgets')) {
+      return App.ajax.send({
+        name: 'widget.layout.get',
+        sender: this,
+        data: {
+          layoutName: this.get('defaultLayoutName'),
+          serviceName: this.get('content.serviceName')
+        },
+        success: 'loadActiveWidgetLayoutSuccessCallback'
+      });
+    } else {
+      this.set('isWidgetsLoaded', true);
+    }
+  },
+
+
+  /**
+   * success callback of <code>loadActiveWidgetLayout()</code>
+   * @param {object|null} data
+   */
+  loadActiveWidgetLayoutSuccessCallback: function (data) {
+    if (data.items[0]) {
+      App.widgetMapper.map(data.items[0].WidgetLayoutInfo);
+      App.widgetLayoutMapper.map(data);
+      this.set('activeWidgetLayout', App.WidgetLayout.find().findProperty('layoutName', this.get('defaultLayoutName')));
+      this.set('isWidgetsLoaded', true);
+    }
+  },
+
+  /**
+   * save layout after re-order widgets
+   * return {$.ajax}
+   */
+  saveWidgetLayout: function (widgets) {
+    var activeLayout = this.get('activeWidgetLayout');
+    var data = {
+      "WidgetLayoutInfo": {
+        "display_name": activeLayout.get("displayName"),
+        "id": activeLayout.get("id"),
+        "layout_name": activeLayout.get("layoutName"),
+        "scope": activeLayout.get("scope"),
+        "section_name": activeLayout.get("sectionName"),
+        "widgets": widgets.map(function (widget) {
+          return {
+            "id": widget.get('id')
+          }
+        })
+      }
+    };
+    return App.ajax.send({
+      name: 'widget.layout.edit',
+      sender: this,
+      data: {
+        layoutId: activeLayout.get("id"),
+        data: data
+      }
+    });
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/models/widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/widget.js b/ambari-web/app/models/widget.js
index 3ef265c..f0b604d 100644
--- a/ambari-web/app/models/widget.js
+++ b/ambari-web/app/models/widget.js
@@ -64,6 +64,8 @@ App.Widget = DS.Model.extend({
         return App.NumberWidgetView;
       case 'GAUGE':
         return App.GaugeWidgetView;
+      case 'HEATMAP':
+        return App.HeatmapWidgetView;
       default:
         return Em.View;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/routes/main.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/main.js b/ambari-web/app/routes/main.js
index 81f5171..a311d49 100644
--- a/ambari-web/app/routes/main.js
+++ b/ambari-web/app/routes/main.js
@@ -142,8 +142,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
         route: '/heatmap',
         connectOutlets: function (router, context) {
           router.get('mainController').dataLoading().done(function () {
-            router.get('mainChartsHeatmapController').loadRacks();
-            router.get('mainChartsController').connectOutlet('mainChartsHeatmap');
+            router.get('mainChartsHeatmapController').loadRacks().done(function(data){
+              router.get('mainChartsHeatmapController').loadRacksSuccessCallback(data);
+              router.get('mainChartsController').connectOutlet('mainChartsHeatmap');
+            });
           });
         }
       }),
@@ -685,8 +687,10 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
           var item = router.get('mainServiceItemController.content');
           if (item.get('isLoaded')) {
             router.get('mainController').dataLoading().done(function () {
-              router.get('mainServiceInfoHeatmapController').loadRacks();
-              router.get('mainServiceItemController').connectOutlet('mainServiceInfoHeatmap', item);
+              router.get('mainServiceInfoHeatmapController').loadRacks().done(function(data) {
+                router.get('mainServiceInfoHeatmapController').loadRacksSuccessCallback(data);
+                router.get('mainServiceItemController').connectOutlet('mainServiceInfoHeatmap', item);
+              });
             });
           }
         }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/templates/common/widget/heatmap_widget.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/widget/heatmap_widget.hbs b/ambari-web/app/templates/common/widget/heatmap_widget.hbs
new file mode 100644
index 0000000..215fb1c
--- /dev/null
+++ b/ambari-web/app/templates/common/widget/heatmap_widget.hbs
@@ -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.
+}}
+
+<div class="heatmap-widget">
+  <div class="span10 heatmap-content">
+    <h4 id="heatmap-metric-loading">
+	        <span id="heatmap-metric-title">
+						{{view.content.displayName}} &nbsp;
+            {{#unless view.isLoaded}}
+              <i class='icon-spinner icon-spin icon-small'></i>
+            {{/unless}}
+					</span>
+    </h4>
+
+    <div class="row-fluid">
+      {{#each rack in view.racks}}
+        <div {{bindAttr class="controller.rackClass"}}>
+          {{view App.MainChartsHeatmapRackView rackBinding="rack" }}
+        </div>
+      {{/each}}
+    </div>
+    {{view App.MainChartsHeatmapHostDetailView}}
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/templates/main/charts/heatmap.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/charts/heatmap.hbs b/ambari-web/app/templates/main/charts/heatmap.hbs
index 89a9a37..05b8da0 100644
--- a/ambari-web/app/templates/main/charts/heatmap.hbs
+++ b/ambari-web/app/templates/main/charts/heatmap.hbs
@@ -19,59 +19,35 @@
 <div class="heatmap">
 
   <div class="container-fluid">
-	  <div class="row-fluid">
-	    <div class="span2 legend-column">
-		     <div class="btn-group" id="select-metric-btn-group">
-				  <button class="btn heatmap-select-metric-btn">{{t charts.heatmap.selectMetric}}</button>
-				  <button class="btn dropdown-toggle heatmap-toggle-metrics-btn" data-toggle="dropdown">
-				    <span class="caret"></span>
-				  </button>
-				  <ul class="dropdown-menu">
-				    {{#each metric in controller.allMetrics}}
-							<li class="heatmap-metrics-dropdown-links">
-                <a tabindex="-1" {{action showHeatMapMetric metric target="controller"}}>{{metric.name}}</a>
-							</li>
-            {{/each}}
-				  </ul>
-				</div>
+    <div class="row-fluid">
+      <div class="span2 legend-column">
+
+        {{view view.dropdownView}}
+
         {{#if controller.selectedMetric}}
-					<table class="legend">
-					  {{#each slot in controller.selectedMetric.slotDefinitions}}
-					    <tr>
-					      <td>
-					        <div class="tile" {{bindAttr style="slot.cssStyle"}}></div>
-					      </td>
-					      <td>{{slot.label}}</td>
-					    </tr>
-					  {{/each}}
-					</table>
-	        {{t common.maximum}}:
-	        <div id="inputMaximum" class="control-group">
-	          {{view Ember.TextField type="text" maxlength="8" valueBinding="controller.inputMaximum" class="span6"}}
-	          {{controller.selectedMetric.units}}
-	        </div>
+          <table class="legend">
+            {{#each slot in controller.selectedMetric.slotDefinitions}}
+              <tr>
+                <td>
+                  <div class="tile" {{bindAttr style="slot.cssStyle"}}></div>
+                </td>
+                <td>{{slot.label}}</td>
+              </tr>
+            {{/each}}
+          </table>
+          {{t common.maximum}}:
+          <div id="inputMaximum" class="control-group">
+            {{view Ember.TextField type="text" maxlength="8" valueBinding="controller.inputMaximum" class="span6"}}
+            {{controller.selectedMetric.units}}
+          </div>
         {{/if}}
-	    </div>
-	    <div class="span10 heatmap-content">
-	      <h4 id="heatmap-metric-loading">
-	        <span id="heatmap-metric-title">
-						{{controller.selectedMetric.name}} &nbsp;
-						{{#if controller.selectedMetric.loading}}
-              <i class='icon-spinner icon-spin icon-small'></i>
-						{{/if}}
-					</span>
-	      </h4>
-	      <div class="row-fluid">
-
-				  {{#each rack in controller.racks}}
-				    <div {{bindAttr class="controller.rackClass"}}>
-				      {{view App.MainChartsHeatmapRackView rackBinding="rack" }}
-				    </div>
-				  {{/each}}
-			  </div>
-			  {{view App.MainChartsHeatmapHostDetailView}}
-	    </div>
-	  </div>
+      </div>
+      {{#if controller.activeWidget}}
+        <div class="active-widget" {{bindAttr id="activeWidget.id"}}>
+          {{view activeWidget.viewClass contentBinding="activeWidget" racksBinding = "racks" idBinding="activeWidget.id"}}
+        </div>
+      {{/if}}
+    </div>
   </div>
 
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/templates/main/charts/heatmap_dropdown.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/charts/heatmap_dropdown.hbs b/ambari-web/app/templates/main/charts/heatmap_dropdown.hbs
new file mode 100644
index 0000000..8904d49
--- /dev/null
+++ b/ambari-web/app/templates/main/charts/heatmap_dropdown.hbs
@@ -0,0 +1,38 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<div class="btn-group">
+  <button class="btn heatmap-select-metric-btn">{{t charts.heatmap.selectMetric}}</button>
+  <button class="btn dropdown-toggle heatmap-toggle-metrics-btn" data-toggle="dropdown">
+    <span class="caret"></span>
+  </button>
+  <ul class="dropdown-menu">
+    {{#each category in controller.heatmapCategories}}
+      <li class="dropdown-submenu">
+        <a tabindex="-1" >{{category.displayName}}</a>
+        <ul class="dropdown-menu">
+          {{#each heatmap in category.heatmaps}}
+            <li>
+              <a tabindex="-1" {{action showHeatMapMetric heatmap target="controller"}}>{{heatmap.display_name}}</a>
+            </li>
+          {{/each}}
+        </ul>
+      </li>
+    {{/each}}
+  </ul>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/templates/main/service/info/heatmap_dropdown.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/info/heatmap_dropdown.hbs b/ambari-web/app/templates/main/service/info/heatmap_dropdown.hbs
new file mode 100644
index 0000000..8986c45
--- /dev/null
+++ b/ambari-web/app/templates/main/service/info/heatmap_dropdown.hbs
@@ -0,0 +1,32 @@
+{{!
+* 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="btn-group" id="select-metric-btn-group">
+  <button class="btn heatmap-select-metric-btn">{{t charts.heatmap.selectMetric}}</button>
+  <button class="btn dropdown-toggle heatmap-toggle-metrics-btn" data-toggle="dropdown">
+    <span class="caret"></span>
+  </button>
+
+  <ul class="dropdown-menu">
+    {{#each heatmap in controller.allHeatmaps}}
+      <li class="heatmap-metrics-dropdown-links">
+        <a tabindex="-1" {{action showHeatMapMetric heatmap target="controller"}}>{{heatmap.display_name}}</a>
+      </li>
+    {{/each}}
+  </ul>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 366cf5d..5328272 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -2406,6 +2406,11 @@ var urls = {
     }
   },
 
+  'widgets.get': {
+    real: '/clusters/{clusterName}/widgets?{urlParams}',
+    mock: '/data/widget_layouts/all_heatmaps.json'
+  },
+
   'widgets.all.shared.get': {
     real: '/clusters/{clusterName}/widgets?WidgetInfo/scope=CLUSTER&fields=*',
     mock: '/data/widget_layouts/all_shared_widgets.json'
@@ -2472,6 +2477,11 @@ var urls = {
     mock: '/data/metrics/{serviceName}/Append_num_ops.json'
   },
 
+  'widgets.hosts.metrics.get': {
+    real: '/clusters/{clusterName}/hosts?fields={metricPaths}',
+    mock: '/data/metrics/{serviceName}/Append_num_ops.json'
+  },
+
   'widgets.wizard.metrics.get': {
     real: '{stackVersionURL}/services?artifacts/Artifacts/artifact_name=metrics_descriptor&StackServices/service_name.in({serviceNames})&fields=artifacts/*',
     mock: '/data/metrics/HBASE/definition.json'

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/utils/heatmap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/heatmap.js b/ambari-web/app/utils/heatmap.js
index d8672b1..9d8c178 100644
--- a/ambari-web/app/utils/heatmap.js
+++ b/ambari-web/app/utils/heatmap.js
@@ -39,7 +39,7 @@ module.exports = {
               value = transformValueFunction(value);
             }
             var hostName = hc.HostRoles.host_name;
-            hostToValueMap[hostName] = value;
+            hostToValueMap[hostName] = value.toFixed(1);
           }
         });
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index b0e1cac..b39eb2f 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -76,6 +76,7 @@ require('views/common/widget/graph_widget_view');
 require('views/common/widget/template_widget_view');
 require('views/common/widget/gauge_widget_view');
 require('views/common/widget/number_widget_view');
+require('views/common/widget/heatmap_widget_view');
 require('views/common/assign_master_components_view');
 require('views/login');
 require('views/main');

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views/common/widget/heatmap_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/heatmap_widget_view.js b/ambari-web/app/views/common/widget/heatmap_widget_view.js
new file mode 100644
index 0000000..4153f30
--- /dev/null
+++ b/ambari-web/app/views/common/widget/heatmap_widget_view.js
@@ -0,0 +1,116 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.HeatmapWidgetView = Em.View.extend(App.WidgetMixin, {
+  templateName: require('templates/common/widget/heatmap_widget'),
+
+  /**
+   * common metrics container
+   * @type {Array}
+   */
+  metrics: [],
+
+  /**
+   *  racks container  binded in the template
+   *  @type {Array}
+   */
+  racks: [],
+
+  /**
+   * draw widget
+   */
+  drawWidget: function () {
+    if (this.get('isLoaded')) {
+      var hostToValueMap = this.calculateValues();
+      var hostNames = [];
+      if (this.get('racks').everyProperty('isLoaded', true)) {
+        this.get('racks').forEach(function (rack) {
+          hostNames = hostNames.concat(rack.hosts.mapProperty('hostName'));
+        });
+      }
+
+      var metricObject = App.MainChartHeatmapMetric.create({
+        name: this.get('content.displayName'),
+        units: this.get('content.properties.display_unit'),
+        maximumValue: this.get('content.properties.max_limit'),
+        hostNames: hostNames,
+        hostToValueMap: hostToValueMap
+      });
+
+      this.set('controller.selectedMetric', metricObject);
+
+      this.set('controller.inputMaximum', metricObject.get('maximumValue'));
+    }
+  },
+
+  /**
+   * calculate value for heatmap widgets
+   */
+  calculateValues: function () {
+    var metrics = this.get('metrics');
+    var hostToValueMap = this.computeExpression(this.extractExpressions(this.get('content.values')[0]), metrics);
+    return hostToValueMap;
+  },
+
+
+  /**
+   * compute expression
+   * @param expressions
+   * @param metrics
+   * @returns {object}
+   */
+  computeExpression: function (expressions, metrics) {
+    var hostToValueMap = {};
+    var hostNames = metrics.mapProperty('hostName');
+    hostNames.forEach(function (_hostName) {
+      expressions.forEach(function (_expression) {
+        var validExpression = true;
+
+        //replace values with metrics data
+        var beforeCompute = _expression.replace(this.get('VALUE_NAME_REGEX'), function (match) {
+          var _metric;
+          if (window.isNaN(match)) {
+            _metric = metrics.filterProperty('name', match).findProperty('hostName', _hostName);
+            if (_metric) {
+              return _metric.data;
+            } else {
+              validExpression = false;
+              console.error('Metrics with name "' + match + '" not found to compute expression');
+            }
+          } else {
+            return match;
+          }
+        });
+
+        if (validExpression && this.get('MATH_EXPRESSION_REGEX').test(beforeCompute)) {
+          var value = Number(window.eval(beforeCompute)).toString();
+          if (value == "NaN")  {
+            value = 0
+          }
+          hostToValueMap[_hostName] = value;
+        } else {
+          console.error('Value for metric is not correct mathematical expression: ' + beforeCompute);
+        }
+      }, this);
+    }, this);
+
+    return hostToValueMap;
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views/main/charts/heatmap.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/charts/heatmap.js b/ambari-web/app/views/main/charts/heatmap.js
index 5147123..431a7e2 100644
--- a/ambari-web/app/views/main/charts/heatmap.js
+++ b/ambari-web/app/views/main/charts/heatmap.js
@@ -22,7 +22,10 @@ App.MainChartsHeatmapView = Em.View.extend({
   didInsertElement: function () {
     this._super();
     // set default metric
-    this.set('controller.selectedMetric', this.get('controller.allMetrics')[0]);
+    this.get('controller').loadPageData();
     $("#heatmapDetailsBlock").hide();
-  }
+  },
+  dropdownView: Em.View.extend({
+    templateName: require('templates/main/charts/heatmap_dropdown')
+  })
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views/main/charts/heatmap/heatmap_host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/charts/heatmap/heatmap_host.js b/ambari-web/app/views/main/charts/heatmap/heatmap_host.js
index 6f54221..d42255e 100644
--- a/ambari-web/app/views/main/charts/heatmap/heatmap_host.js
+++ b/ambari-web/app/views/main/charts/heatmap/heatmap_host.js
@@ -21,6 +21,11 @@ var App = require('app');
 
 App.MainChartsHeatmapHostView = Em.View.extend({
   templateName: require('templates/main/charts/heatmap/heatmap_host'),
+
+  didInsertElement: function() {
+    $("#heatmapDetailsBlock").hide();
+  },
+
   /** @private */
   hostClass: 'hostBlock',
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views/main/charts/heatmap/heatmap_rack.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/charts/heatmap/heatmap_rack.js b/ambari-web/app/views/main/charts/heatmap/heatmap_rack.js
index 35653cd..aa7c95c 100644
--- a/ambari-web/app/views/main/charts/heatmap/heatmap_rack.js
+++ b/ambari-web/app/views/main/charts/heatmap/heatmap_rack.js
@@ -29,31 +29,12 @@ App.MainChartsHeatmapRackView = Em.View.extend({
   /** loaded hosts of rack */
   hosts: [],
 
-  willDestroyElement: function () {
-    this.get('hosts').clear();
-  },
-
-  /**
-   * get hosts from the root controller
-   */
-  getHosts: function () {
-    var controller = this.get('controller');
-    var rackId = this.get('rack.rackId');
-    var rackMap = controller.get('rackMap');
-    this.pushHostsToRack(rackMap[rackId].hosts);
-  },
-
-  getHostsSuccessCallback: function (data, opt, params) {
-    this.pushHostsToRack(data);
-    this.displayHosts();
-  },
-
   /**
    * display hosts of rack
    */
   displayHosts: function () {
     var rackHosts = this.get('rack.hosts');
-    var rackCount = this.get('controller.modelRacks.length');
+    var rackCount = this.get('controller.racks.length');
 
     if (this.get('hosts.length') === 0) {
       if (rackHosts.length > 100 && rackCount == 1) {
@@ -72,77 +53,7 @@ App.MainChartsHeatmapRackView = Em.View.extend({
     }
   },
 
-  getHostsErrorCallback: function (request, ajaxOptions, error, opt, params) {
-    this.set('rack.isLoaded', true);
-  },
-  /**
-   * push hosts to rack
-   * @param data
-   */
-  pushHostsToRack: function (hosts) {
-    var newHostsData = hosts;
-    var rackHosts = this.get('rack.hosts');
-
-    if (rackHosts.length > 0) {
-      this.updateLoadedHosts(rackHosts, newHostsData);
-    } else {
-      this.set('rack.hosts', newHostsData);
-    }
-  },
-
-  updateLoadedHosts: function (rackHosts, newHostsData) {
-    var rackHostsMap = {};
-    var isNewHosts = false;
-
-    //create map
-    rackHosts.forEach(function (host) {
-      rackHostsMap[host.hostName] = host;
-    });
-
-    newHostsData.forEach(function (item) {
-      var currentHostInfo = rackHostsMap[item.hostName];
-
-      if (currentHostInfo) {
-        ['diskTotal', 'diskFree', 'cpuSystem', 'cpuUser', 'memTotal', 'memFree', 'hostComponents'].forEach(function (property) {
-          currentHostInfo[property] = item[property];
-        });
-        delete rackHostsMap[item.hostName];
-      } else {
-        isNewHosts = true;
-      }
-    }, this);
-
-    //if hosts were deleted or added then reload hosts view
-    if (!App.isEmptyObject(rackHostsMap) || isNewHosts) {
-      this.redrawHostsView(newHostsData)
-    }
-  },
-
-  /**
-   * reload hosts rack
-   * @param newHostsData
-   */
-  redrawHostsView: function (newHostsData) {
-    this.set('rack.isLoaded', false);
-    this.get('hosts').clear();
-    this.set('rack.hosts', newHostsData);
-  },
-
-  /**
-   * call metrics update after hosts of rack are loaded
-   */
-  updateMetrics: function(){
-    if (this.get('rack.isLoaded')) {
-      this.get('controller').loadMetrics();
-    }
-  }.observes('rack.isLoaded'),
-
   didInsertElement: function () {
-    this.set('rack.isLoaded', false);
-    if (this.get('rack.hosts.length') > 0) {
-      this.displayHosts();
-    }
-    this.getHosts();
     this.get('controller').addRackView(this);
   },
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views/main/service/info/heatmap_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/info/heatmap_view.js b/ambari-web/app/views/main/service/info/heatmap_view.js
index fb6a2d1..580adb7 100644
--- a/ambari-web/app/views/main/service/info/heatmap_view.js
+++ b/ambari-web/app/views/main/service/info/heatmap_view.js
@@ -17,5 +17,7 @@
  */
 var App = require('app');
 App.MainServiceInfoHeatmapView = App.MainChartsHeatmapView.extend({
-  templateName: require('templates/main/charts/heatmap')
+  dropdownView: Em.View.extend({
+    templateName: require('templates/main/service/info/heatmap_dropdown')
+  })
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/app/views/main/service/info/summary.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/info/summary.js b/ambari-web/app/views/main/service/info/summary.js
index 60734f1..22af416 100644
--- a/ambari-web/app/views/main/service/info/summary.js
+++ b/ambari-web/app/views/main/service/info/summary.js
@@ -598,7 +598,7 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
           var widgets = misc.sortByOrder($("#widget_layout .widget").map(function () {
             return this.id;
           }), self.get('controller.widgets'));
-          self.get('controller').saveReorderedLayout(widgets);
+          self.get('controller').saveWidgetLayout(widgets);
         },
         activate: function (event, ui) {
           self.set('isMoving', true);

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_cpuWaitIO_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_cpuWaitIO_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_cpuWaitIO_test.js
deleted file mode 100644
index 6f46539..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_cpuWaitIO_test.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_cpuWaitIO');
-
-describe('App.MainChartHeatmapCpuWaitIOMetric', function () {
-
-  var tests = [
-    {
-      json: {
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "cpu" : {
-                "cpu_wio" : 0.4
-              }
-            }
-          }
-        ]
-      },
-      m: 'One host',
-      e: {'dev01.hortonworks.com': '40.0'}
-    },
-    {
-      json: {
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "cpu" : {
-                "cpu_wio" : 0.4
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "cpu" : {
-                "cpu_wio" : 0.34566
-              }
-            }
-          }
-        ]
-      },
-      m: 'Two hosts',
-      e: {'dev01.hortonworks.com': '40.0', 'dev02.hortonworks.com': '34.6'}
-    },
-    {
-      json: {
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "cpu" : {
-                "cpu_wio" : 0.4
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "cpu" : {
-              }
-            }
-          }
-        ]
-      },
-      m: 'Two hosts, One without metric',
-      e: {'dev01.hortonworks.com': '40.0'}
-    }
-  ];
-
-  describe('#metricMapper()', function() {
-    var mainChartHeatmapCpuWaitIOMetric = App.MainChartHeatmapCpuWaitIOMetric.create();
-
-    tests.forEach(function(test) {
-      it(test.m, function() {
-        var r = mainChartHeatmapCpuWaitIOMetric.metricMapper(test.json);
-        expect(r).to.eql(test.e);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_bytesread_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_bytesread_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_bytesread_test.js
deleted file mode 100644
index cebf41d..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_bytesread_test.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_dfs');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_bytesread');
-
-describe('App.MainChartHeatmapDFSBytesReadMetric', function () {
-
-  var tests = [
-    {i: 0, e: 0},
-    {i: 0.5 * 1024* 1024, e: 0.5},
-    {i: 1024* 1024, e: 1},
-    {i: 10.5 * 1024 * 1024,e: 10.5}
-  ];
-
-  describe('#transformValue()', function() {
-    var mainChartHeatmapDFSBytesReadMetric = App.MainChartHeatmapDFSBytesReadMetric.create();
-
-    tests.forEach(function(test) {
-      it(test.i + ' bytes to ' + test.e + ' MB', function() {
-        var r = mainChartHeatmapDFSBytesReadMetric.transformValue(test.i);
-        expect(r).to.eql(test.e);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_byteswritten_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_byteswritten_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_byteswritten_test.js
deleted file mode 100644
index 240802e..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_byteswritten_test.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_dfs');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_byteswritten');
-
-describe('App.MainChartHeatmapDFSBytesWrittenMetric', function () {
-
-  var tests = [
-    {i: 0, e: 0},
-    {i: 0.5 * 1024* 1024, e: 0.5},
-    {i: 1024* 1024, e: 1},
-    {i: 10.5 * 1024 * 1024,e: 10.5}
-  ];
-
-  describe('#transformValue()', function() {
-    var mainChartHeatmapDFSBytesWrittenMetric = App.MainChartHeatmapDFSBytesWrittenMetric.create();
-
-    tests.forEach(function(test) {
-      it(test.i + ' bytes to ' + test.e + ' MB', function() {
-        var r = mainChartHeatmapDFSBytesWrittenMetric.transformValue(test.i);
-        expect(r).to.eql(test.e);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_test.js
deleted file mode 100644
index 06d62c2..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_dfs_test.js
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_dfs');
-
-describe('App.MainChartHeatmapDFSMetrics', function () {
-
-  var tests = [
-    {
-      json: {
-        "host_components" : [
-          {
-            "HostRoles" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "jvm" : {
-                "gcTimeMillis" : 285
-              }
-            }
-          }
-        ]
-      },
-      result: {'dev01.hortonworks.com': 285},
-      m: 'One host_component'
-    },
-    {
-      json: {
-        "host_components" : [
-          {
-            "HostRoles" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "jvm" : {
-                "gcTimeMillis" : 285
-              }
-            }
-          },
-          {
-            "HostRoles" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "jvm" : {
-                "gcTimeMillis" : 124
-              }
-            }
-          }
-        ]
-      },
-      result: {'dev01.hortonworks.com': 285, 'dev02.hortonworks.com': 124},
-      m: 'Two host_components'
-    },
-    {
-      json: {
-        "host_components" : [
-          {
-            "HostRoles" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "jvm" : {
-                "gcTimeMillis" : 285
-              }
-            }
-          },
-          {
-            "HostRoles" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "jvm" : {
-
-              }
-            }
-          }
-        ]
-      },
-      result: {'dev01.hortonworks.com': 285},
-      m: 'Two host_components, one without metric'
-    }
-  ];
-
-  describe('#metricMapper()', function() {
-    var mainChartHeatmapDFSMetrics = App.MainChartHeatmapDFSMetrics.create();
-    mainChartHeatmapDFSMetrics.set('defaultMetric', 'metrics.jvm.gcTimeMillis');
-
-    tests.forEach(function(test) {
-      it(test.m, function() {
-        var r = mainChartHeatmapDFSMetrics.metricMapper(test.json);
-        expect(r).to.eql(test.result);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceused_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceused_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceused_test.js
deleted file mode 100644
index 312c9c6..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceused_test.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_diskspaceused');
-
-describe('App.MainChartHeatmapDiskSpaceUsedMetric', function () {
-
-  var tests = Em.A([
-    {
-      json:{
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "disk" : {
-                "disk_free" : 89.973,
-                "disk_total" : 101.515
-              }
-            }
-          }
-        ]
-      },
-      m: 'One host',
-      e: {'dev01.hortonworks.com': 11.37}
-    },
-    {
-      json:{
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "disk" : {
-                "disk_free" : 89.973,
-                "disk_total" : 101.515
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "disk" : {
-                "disk_free" : 89.973,
-                "disk_total" : 101.515
-              }
-            }
-          }
-        ]
-      },
-      m: 'Two hosts',
-      e: {'dev01.hortonworks.com': 11.37, 'dev02.hortonworks.com': 11.37}
-    },
-    {
-      json:{
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "disk" : {
-                "disk_free" : 89.973,
-                "disk_total" : 101.515
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-
-            }
-          }
-        ]
-      },
-      m: 'Two hosts, One without metric',
-      e: {'dev01.hortonworks.com': 11.37}
-    }
-  ]);
-
-  describe('#metricMapper()', function() {
-    var mainChartHeatmapDiskSpaceUsedMetric = App.MainChartHeatmapDiskSpaceUsedMetric.create();
-
-    tests.forEach(function(test) {
-      it(test.m, function() {
-        var r = mainChartHeatmapDiskSpaceUsedMetric.metricMapper(test.json);
-        expect(r).to.eql(test.e);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_hbase_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_hbase_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_hbase_test.js
deleted file mode 100644
index a1f0325..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_hbase_test.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_hbase');
-
-describe('App.MainChartHeatmapHbaseMetrics', function () {
-
-  var tests = [
-    {
-      json: {
-        "host_components" : [
-          {
-            "HostRoles" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "hbase" : {
-                "regionserver" : {
-                  "readRequestsCount" : 0.0
-                }
-              }
-            }
-          }
-        ]
-      },
-      result: {'dev01.hortonworks.com': 0},
-      m: 'One host_component'
-    },
-    {
-      json: {
-        "host_components" : [
-          {
-            "HostRoles" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "hbase" : {
-                "regionserver" : {
-                  "readRequestsCount" : 0.0
-                }
-              }
-            }
-          },
-          {
-            "HostRoles" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "hbase" : {
-                "regionserver" : {
-                  "readRequestsCount" : 1.0
-                }
-              }
-            }
-          }
-        ]
-      },
-      result: {'dev01.hortonworks.com': 0, 'dev02.hortonworks.com': 1},
-      m: 'Two host_components'
-    },
-    {
-      json: {
-        "host_components" : [
-          {
-            "HostRoles" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "hbase" : {
-                "regionserver" : {
-                  "readRequestsCount" : 0.0
-                }
-              }
-            }
-          },
-          {
-            "HostRoles" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "hbase" : {
-                "regionserver" : {
-
-                }
-              }
-            }
-          }
-        ]
-      },
-      result: {'dev01.hortonworks.com': 0},
-      m: 'Two host_components, one without metric'
-    }
-  ];
-
-  describe('#metricMapper()', function() {
-    var mainChartHeatmapHbaseMetrics = App.MainChartHeatmapHbaseMetrics.create();
-    mainChartHeatmapHbaseMetrics.set('defaultMetric', 'metrics.hbase.regionserver.readRequestsCount');
-
-    tests.forEach(function(test) {
-      it(test.m, function() {
-        var r = mainChartHeatmapHbaseMetrics.metricMapper(test.json);
-        expect(r).to.eql(test.result);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test.js
deleted file mode 100644
index 8886d8a..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused_test.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-require('messages');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_memoryused');
-
-describe('App.MainChartHeatmapMemoryUsedMetric', function () {
-
-  var tests = [
-    {
-      json:{
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "memory" : {
-                "mem_buffers" : 109888.0,
-                "mem_cached" : 1965624.0,
-                "mem_free" : 261632.0,
-                "mem_shared" : 0.0,
-                "mem_total" : 6123776.0,
-                "swap_free" : 4126820.0,
-                "swap_total" : 4128760.0
-              }
-            }
-          }
-        ]
-      },
-      m: 'One host',
-      e: {'dev01.hortonworks.com': '63.6'}
-    },
-    {
-      json:{
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "memory" : {
-                "mem_buffers" : 109888.0,
-                "mem_cached" : 1965624.0,
-                "mem_free" : 261632.0,
-                "mem_shared" : 0.0,
-                "mem_total" : 6123776.0,
-                "swap_free" : 4126820.0,
-                "swap_total" : 4128760.0
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "memory" : {
-                "mem_buffers" : 109888.0,
-                "mem_cached" : 1965624.0,
-                "mem_free" : 261632.0,
-                "mem_shared" : 0.0,
-                "mem_total" : 6123776.0,
-                "swap_free" : 4126820.0,
-                "swap_total" : 4128760.0
-              }
-            }
-          }
-        ]
-      },
-      m: 'Two hosts',
-      e: {'dev01.hortonworks.com': '63.6', 'dev02.hortonworks.com': '63.6'}
-    },
-    {
-      json:{
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "memory" : {
-                "mem_buffers" : 109888.0,
-                "mem_cached" : 1965624.0,
-                "mem_free" : 261632.0,
-                "mem_shared" : 0.0,
-                "mem_total" : 6123776.0,
-                "swap_free" : 4126820.0,
-                "swap_total" : 4128760.0
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-
-            }
-          }
-        ]
-      },
-      m: 'Two hosts, One without metric',
-      e: {'dev01.hortonworks.com': '63.6'}
-    }
-  ];
-
-  describe('#metricMapper()', function() {
-    var mainChartHeatmapMemoryUsedMetric = App.MainChartHeatmapMemoryUsedMetric.create();
-
-    tests.forEach(function(test) {
-      it(test.m, function() {
-        var r = mainChartHeatmapMemoryUsedMetric.metricMapper(test.json);
-        expect(r).to.eql(test.e);
-      });
-    });
-
-  });
-
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun_test.js
deleted file mode 100644
index f23d02c..0000000
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_processrun_test.js
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * 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.
- */
-
-var App = require('app');
-
-require('controllers/main/charts/heatmap_metrics/heatmap_metric');
-require('controllers/main/charts/heatmap_metrics/heatmap_metric_processrun');
-
-describe('App.MainChartHeatmapProcessRunMetric', function () {
-
-  var tests = [
-    {
-      json: {
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "process" : {
-                "proc_run" : 0.0
-              }
-            }
-          }
-        ]
-      },
-      m: 'One host',
-      result: {'dev01.hortonworks.com': '0.0'}
-    },
-    {
-      json: {
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "process" : {
-                "proc_run" : 0.1
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "process" : {
-                "proc_run" : 0.46
-              }
-            }
-          }
-        ]
-      },
-      m: 'Two hosts',
-      result: {'dev01.hortonworks.com': '0.1', 'dev02.hortonworks.com': '0.5'}
-    },
-    {
-      json: {
-        "items" : [
-          {
-            "Hosts" : {
-              "host_name" : "dev01.hortonworks.com"
-            },
-            "metrics" : {
-              "process" : {
-                "proc_run" : 0.99
-              }
-            }
-          },
-          {
-            "Hosts" : {
-              "host_name" : "dev02.hortonworks.com"
-            },
-            "metrics" : {
-              "process" : {
-              }
-            }
-          }
-        ]
-      },
-      m: 'Two hosts, One without metric',
-      result: {'dev01.hortonworks.com': '1.0'}
-    }
-  ];
-
-  describe('#metricMapper()', function() {
-    var mainChartHeatmapProcessRunMetric = App.MainChartHeatmapProcessRunMetric.create();
-
-    tests.forEach(function(test) {
-      it(test.m, function() {
-        var r = mainChartHeatmapProcessRunMetric.metricMapper(test.json);
-        expect(r).to.eql(test.result);
-      });
-    });
-  });
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/9639005f/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js
index fb134e4..77fef25 100644
--- a/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js
+++ b/ambari-web/test/controllers/main/charts/heatmap_metrics/heatmap_metric_test.js
@@ -43,30 +43,6 @@ describe('MainChartHeatmapMetric', function () {
     });
   });
 
-  describe('#refreshHostSlots', function() {
-    beforeEach(function() {
-      sinon.stub(App.ajax, 'send', Em.K);
-    });
-
-    afterEach(function() {
-      App.ajax.send.restore();
-    });
-
-    it('Should load proper URL', function() {
-      mainChartHeatmapMetric.set('ajaxData', {
-        serviceName: 'SERVICE',
-        componentName: 'COMPONENT'
-      });
-      mainChartHeatmapMetric.set('defaultMetric', 'default.metric');
-      mainChartHeatmapMetric.refreshHostSlots();
-      expect(App.ajax.send.getCall(0).args[0].data).to.eql({
-        "metricName": "default/metric",
-        "serviceName": "SERVICE",
-        "componentName": "COMPONENT"
-      });
-      expect(App.ajax.send.getCall(0).args[0].name).to.equal('hosts.metrics');
-    });
-  });
 
   describe('#slotDefinitions', function () {
     beforeEach(function () {
@@ -304,23 +280,4 @@ describe('MainChartHeatmapMetric', function () {
     });
   });
 
-  describe('#refreshHostSlotsSuccessCallback()', function () {
-    it('launch metricMapper with recieved data', function () {
-      sinon.stub(mainChartHeatmapMetric, 'metricMapper').returns({'host1': 1});
-      mainChartHeatmapMetric.refreshHostSlotsSuccessCallback({data: 'data'});
-      expect(mainChartHeatmapMetric.get('hostToValueMap')).to.eql({'host1': 1});
-      expect(mainChartHeatmapMetric.get('loading')).to.be.false;
-      expect(mainChartHeatmapMetric.metricMapper.calledWith({data: 'data'})).to.be.true;
-      mainChartHeatmapMetric.metricMapper.restore();
-    });
-  });
-
-  describe('#refreshHostSlotsErrorCallback()', function () {
-    it('', function () {
-      mainChartHeatmapMetric.set('loading', undefined);
-      mainChartHeatmapMetric.refreshHostSlotsErrorCallback({data: 'data'});
-      expect(mainChartHeatmapMetric.get('loading')).to.be.false;
-    });
-  });
-
 });


Mime
View raw message