You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jl...@apache.org on 2017/10/11 05:25:01 UTC

[05/31] ambari git commit: AMBARI-22168 Move service metrics to separate tab. (atkach)

AMBARI-22168 Move service metrics to separate tab. (atkach)


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

Branch: refs/heads/branch-feature-AMBARI-14714
Commit: 6eb273e19a81773c27f235631c54a3e142277f08
Parents: e83c86d
Author: Andrii Tkach <at...@apache.org>
Authored: Mon Oct 9 14:11:36 2017 +0300
Committer: Andrii Tkach <at...@apache.org>
Committed: Mon Oct 9 15:01:28 2017 +0300

----------------------------------------------------------------------
 ambari-web/app/assets/test/tests.js             |   2 +
 ambari-web/app/controllers.js                   |   1 +
 .../app/controllers/main/service/info/metric.js | 468 +++++++++++++++++++
 .../controllers/main/service/info/summary.js    | 449 +-----------------
 .../service/widgets/create/wizard_controller.js |   2 +-
 ambari-web/app/messages.js                      |   1 +
 .../app/styles/enhanced_service_dashboard.less  |  26 +-
 .../app/templates/main/service/info/metrics.hbs | 104 +++++
 .../app/templates/main/service/info/summary.hbs |  84 ----
 ambari-web/app/templates/main/service/item.hbs  |   5 +-
 ambari-web/app/views.js                         |   1 +
 ambari-web/app/views/main/service/info/menu.js  |   7 +
 .../app/views/main/service/info/metrics_view.js | 290 ++++++++++++
 .../app/views/main/service/info/summary.js      | 315 ++-----------
 ambari-web/app/views/main/service/item.js       |   6 +
 .../main/service/info/metric_test.js            | 110 +++++
 .../main/service/info/summary_test.js           |  76 ---
 .../main/service/info/metrics_view_test.js      | 334 +++++++++++++
 .../views/main/service/info/summary_test.js     | 281 +----------
 19 files changed, 1400 insertions(+), 1162 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/assets/test/tests.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js
index 03b4657..7c636d4 100644
--- a/ambari-web/app/assets/test/tests.js
+++ b/ambari-web/app/assets/test/tests.js
@@ -125,6 +125,7 @@ var files = [
   'test/controllers/main/service/item_test',
   'test/controllers/main/service/info/config_test',
   'test/controllers/main/service/info/summary_test',
+  'test/controllers/main/service/info/metric_test',
   'test/controllers/main/service_test',
   'test/controllers/main/admin_test',
   'test/controllers/main/views_controller_test',
@@ -340,6 +341,7 @@ var files = [
   'test/views/main/service/service_test',
   'test/views/main/service/info/config_test',
   'test/views/main/service/info/summary_test',
+  'test/views/main/service/info/metrics_view_test',
   'test/views/main/service/info/menu_test',
   'test/views/main/service/info/component_list_view_test',
   'test/views/main/service/info/metrics/ambari_metrics/regionserver_base_test',

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers.js b/ambari-web/app/controllers.js
index 81e5eb7..f7d77be 100644
--- a/ambari-web/app/controllers.js
+++ b/ambari-web/app/controllers.js
@@ -142,6 +142,7 @@ require('controllers/main/charts');
 require('controllers/main/charts/heatmap_metrics/heatmap_metric');
 require('controllers/main/charts/heatmap');
 require('controllers/main/service/info/heatmap');
+require('controllers/main/service/info/metric');
 require('controllers/main/views_controller');
 require('controllers/main/views/details_controller');
 require('controllers/wizard/step0_controller');

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers/main/service/info/metric.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/metric.js b/ambari-web/app/controllers/main/service/info/metric.js
new file mode 100644
index 0000000..9dfc32c
--- /dev/null
+++ b/ambari-web/app/controllers/main/service/info/metric.js
@@ -0,0 +1,468 @@
+/**
+ * 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.MainServiceInfoMetricsController = Em.Controller.extend(App.WidgetSectionMixin, {
+  name: 'mainServiceInfoMetricsController',
+
+  layoutNameSuffix: "_dashboard",
+
+  sectionNameSuffix: "_SUMMARY",
+
+  /**
+   * Some widget has type `GRAPH`
+   *
+   * @type {boolean}
+   */
+  someWidgetGraphExists: Em.computed.someBy('widgets', 'widgetType', 'GRAPH'),
+
+  /**
+   * @type {boolean}
+   */
+  showTimeRangeControl: Em.computed.or('!isServiceWithEnhancedWidgets', 'someWidgetGraphExists'),
+
+  /**
+   * @type {boolean}
+   */
+  isWidgetLayoutsLoaded: false,
+
+  /**
+   * @type {boolean}
+   */
+  isAllSharedWidgetsLoaded: false,
+
+  /**
+   * @type {boolean}
+   */
+  isMineWidgetsLoaded: false,
+
+  /**
+   * load widget layouts across all users in CLUSTER scope
+   * @returns {$.ajax}
+   */
+  loadWidgetLayouts: function () {
+    this.set('isWidgetLayoutsLoaded', false);
+    return App.ajax.send({
+      name: 'widgets.layouts.get',
+      sender: this,
+      data: {
+        sectionName: this.get('sectionName')
+      },
+      success: 'loadWidgetLayoutsSuccessCallback'
+    });
+  },
+
+  loadWidgetLayoutsSuccessCallback: function (data) {
+    App.widgetLayoutMapper.map(data);
+    this.set('isWidgetLayoutsLoaded', true);
+  },
+
+
+  /**
+   * load all shared widgets to show on widget browser
+   * @returns {$.ajax}
+   */
+  loadAllSharedWidgets: function () {
+    this.set('isAllSharedWidgetsLoaded', false);
+    return App.ajax.send({
+      name: 'widgets.all.shared.get',
+      sender: this,
+      success: 'loadAllSharedWidgetsSuccessCallback'
+    });
+  },
+
+  /**
+   * success callback of <code>loadAllSharedWidgets</code>
+   * @param {object|null} data
+   */
+  loadAllSharedWidgetsSuccessCallback: function (data) {
+    var widgetIds = this.get('widgets').mapProperty('id');
+    if (data.items[0] && data.items.length) {
+      this.set("allSharedWidgets",
+        data.items.filter(function (widget) {
+          return widget.WidgetInfo.widget_type != "HEATMAP";
+        }).map(function (widget) {
+          var widgetType = widget.WidgetInfo.widget_type;
+          var widgetName = widget.WidgetInfo.widget_name;
+          var widgetId =  widget.WidgetInfo.id;
+          return Em.Object.create({
+            id: widgetId,
+            widgetName: widgetName,
+            description: widget.WidgetInfo.description,
+            widgetType: widgetType,
+            iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png",
+            serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'),
+            added: widgetIds.contains(widgetId),
+            isShared: widget.WidgetInfo.scope == "CLUSTER"
+          });
+        })
+      );
+    }
+    this.set('isAllSharedWidgetsLoaded', true);
+  },
+
+  allSharedWidgets: [],
+  mineWidgets: [],
+
+  /**
+   * load all mine widgets of current user to show on widget browser
+   * @returns {$.ajax}
+   */
+  loadMineWidgets: function () {
+    this.set('isMineWidgetsLoaded', false);
+    return App.ajax.send({
+      name: 'widgets.all.mine.get',
+      sender: this,
+      data: {
+        loginName: App.router.get('loginName')
+      },
+      success: 'loadMineWidgetsSuccessCallback'
+    });
+  },
+
+  /**
+   * success callback of <code>loadMineWidgets</code>
+   * @param {object|null} data
+   */
+  loadMineWidgetsSuccessCallback: function (data) {
+    var widgetIds = this.get('widgets').mapProperty('id');
+    if (data.items[0] && data.items.length) {
+      this.set("mineWidgets",
+        data.items.filter(function (widget) {
+          return widget.WidgetInfo.widget_type != "HEATMAP";
+        }).map(function (widget) {
+          var widgetType = widget.WidgetInfo.widget_type;
+          var widgetName = widget.WidgetInfo.widget_name;
+          var widgetId =  widget.WidgetInfo.id;
+          return Em.Object.create({
+            id: widget.WidgetInfo.id,
+            widgetName: widgetName,
+            description: widget.WidgetInfo.description,
+            widgetType: widgetType,
+            iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png",
+            serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'),
+            added: widgetIds.contains(widgetId),
+            isShared: widget.WidgetInfo.scope == "CLUSTER"
+          });
+        })
+      );
+    } else {
+      this.set("mineWidgets", []);
+    }
+    this.set('isMineWidgetsLoaded', true);
+  },
+
+  /**
+   * add widgets, on click handler for "Add"
+   */
+  addWidget: function (event) {
+    var widgetToAdd = event.context;
+    var activeLayout = this.get('activeWidgetLayout');
+    var widgetIds = activeLayout.get('widgets').map(function(widget) {
+      return {
+        "id": widget.get("id")
+      }
+    });
+    widgetIds.pushObject({
+      "id": widgetToAdd.id
+    });
+    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": widgetIds
+      }
+    };
+
+    widgetToAdd.set('added', !widgetToAdd.added);
+    return App.ajax.send({
+      name: 'widget.layout.edit',
+      sender: this,
+      data: {
+        layoutId: activeLayout.get("id"),
+        data: data
+      },
+      success: 'updateActiveLayout'
+    });
+  },
+
+  /**
+   * hide widgets, on click handler for "Added"
+   */
+  hideWidget: function (event) {
+    var widgetToHide = event.context;
+    var activeLayout = this.get('activeWidgetLayout');
+    var widgetIds = activeLayout.get('widgets').map(function (widget) {
+      return {
+        "id": widget.get("id")
+      }
+    });
+    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": widgetIds.filter(function (widget) {
+          return widget.id !== widgetToHide.id;
+        })
+      }
+    };
+
+    widgetToHide.set('added', !widgetToHide.added);
+    return App.ajax.send({
+      name: 'widget.layout.edit',
+      sender: this,
+      data: {
+        layoutId: activeLayout.get("id"),
+        data: data
+      },
+      success: 'hideWidgetSuccessCallback'
+    });
+
+  },
+
+  /**
+   * @param {object|null} data
+   * @param {object} opt
+   * @param {object} params
+   */
+  hideWidgetSuccessCallback: function (data, opt, params) {
+    params.data.WidgetLayoutInfo.widgets = params.data.WidgetLayoutInfo.widgets.map(function (widget) {
+      return {
+        WidgetInfo: {
+          id: widget.id
+        }
+      }
+    });
+    App.widgetLayoutMapper.map({items: [params.data]});
+    this.propertyDidChange('widgets');
+  },
+
+  /**
+   * update current active widget layout
+   */
+  updateActiveLayout: function () {
+    this.getActiveWidgetLayout();
+  },
+
+  /**
+   * delete widgets, on click handler for "Delete"
+   */
+  deleteWidget: function (event) {
+    var widget = event.context;
+    var self = this;
+    var confirmMsg =  widget.get('isShared') ? Em.I18n.t('dashboard.widgets.browser.action.delete.shared.bodyMsg').format(widget.widgetName) :  Em.I18n.t('dashboard.widgets.browser.action.delete.mine.bodyMsg').format(widget.widgetName);
+    var bodyMessage = Em.Object.create({
+      confirmMsg: confirmMsg,
+      confirmButton: Em.I18n.t('dashboard.widgets.browser.action.delete.btnMsg')
+    });
+    return App.showConfirmationFeedBackPopup(function (query) {
+      return App.ajax.send({
+        name: 'widget.action.delete',
+        sender: self,
+        data: {
+          id: widget.id
+        },
+        success: 'updateWidgetBrowser'
+      });
+
+    }, bodyMessage);
+  },
+
+  /**
+   * update widget browser content after deleted some widget
+   */
+  updateWidgetBrowser: function () {
+    this.loadAllSharedWidgets();
+    this.loadMineWidgets();
+  },
+
+  /**
+   * Share widgets, on click handler for "Share"
+   */
+  shareWidget: function (event) {
+    var widget = event.context;
+    var self = this;
+    var bodyMessage = Em.Object.create({
+      confirmMsg: Em.I18n.t('dashboard.widgets.browser.action.share.confirmation'),
+      confirmButton: Em.I18n.t('dashboard.widgets.browser.action.share')
+    });
+    return App.showConfirmationFeedBackPopup(function (query) {
+      return App.ajax.send({
+        name: 'widgets.wizard.edit',
+        sender: self,
+        data: {
+          data: {
+            "WidgetInfo": {
+              "widget_name": widget.get("widgetName"),
+              "scope": "CLUSTER"
+            }
+          },
+          widgetId: widget.get("id")
+        },
+        success: 'updateWidgetBrowser'
+      });
+    }, bodyMessage);
+  },
+
+  /**
+   * create widget
+   */
+  createWidget: function () {
+    App.router.send('createServiceWidget', Em.Object.create({
+      layout: this.get('activeWidgetLayout'),
+      serviceName: this.get('content.serviceName')
+    }));
+  },
+
+  /**
+   * edit widget
+   * @param {App.Widget} content
+   */
+  editWidget: function (content) {
+    content.set('serviceName', this.get('content.serviceName'));
+    App.router.send('editServiceWidget', content);
+  },
+
+  /**
+   * launch Widgets Browser popup
+   * @method showPopup
+   * @return {App.ModalPopup}
+   */
+  goToWidgetsBrowser: function () {
+    var self = this;
+
+    return App.ModalPopup.show({
+      header: Em.I18n.t('dashboard.widgets.browser.header'),
+
+      classNames: ['common-modal-wrapper', 'widgets-browser-popup'],
+      modalDialogClasses: ['modal-lg'],
+      onPrimary: function () {
+        this.hide();
+        self.set('isAllSharedWidgetsLoaded', false);
+        self.set('allSharedWidgets', []);
+        self.set('isMineWidgetsLoaded', false);
+        self.set('mineWidgets', []);
+      },
+      autoHeight: false,
+      isHideBodyScroll: false,
+      footerClass: Ember.View.extend({
+        templateName: require('templates/common/modal_popups/widget_browser_footer'),
+        isShowMineOnly: false,
+        onPrimary: function() {
+          this.get('parentView').onPrimary();
+        }
+      }),
+      isShowMineOnly: false,
+      bodyClass: Ember.View.extend({
+        templateName: require('templates/common/modal_popups/widget_browser_popup'),
+        controller: self,
+        willInsertElement: function () {
+          this.get('controller').loadAllSharedWidgets();
+          this.get('controller').loadMineWidgets();
+        },
+
+        isLoaded: Em.computed.and('controller.isAllSharedWidgetsLoaded', 'controller.isMineWidgetsLoaded'),
+
+        isWidgetEmptyList: Em.computed.empty('filteredContent'),
+
+        activeService: '',
+        activeStatus: '',
+
+        content: function () {
+          if (this.get('parentView.isShowMineOnly')) {
+            return this.get('controller.mineWidgets');
+          } else {
+            // merge my widgets and all shared widgets, no duplicated is allowed
+            var content = [];
+            var widgetMap = {};
+            var allWidgets = this.get('controller.allSharedWidgets').concat(this.get('controller.mineWidgets'));
+            allWidgets.forEach(function(widget) {
+              if (!widgetMap[widget.get("id")]) {
+                content.pushObject(widget);
+                widgetMap[widget.get("id")] = true;
+              }
+            });
+            return content;
+          }
+        }.property('controller.allSharedWidgets.length', 'controller.isAllSharedWidgetsLoaded',
+          'controller.mineWidgets.length', 'controller.isMineWidgetsLoaded', 'parentView.isShowMineOnly'),
+
+        /**
+         * displaying content filtered by service name and status.
+         */
+        filteredContent: function () {
+          var activeService = this.get('activeService') ? this.get('activeService') : this.get('controller.content.serviceName');
+          var result = [];
+          this.get('content').forEach(function (widget) {
+            if (widget.get('serviceName').indexOf(activeService) >= 0) {
+              result.pushObject(widget);
+            }
+          });
+          return result;
+        }.property('content', 'activeService', 'activeStatus'),
+
+        /**
+         * service name filter
+         */
+        services: function () {
+          var view = this;
+          var services = App.Service.find().filter(function(item){
+            var stackService =  App.StackService.find().findProperty('serviceName', item.get('serviceName'));
+            return stackService.get('isServiceWithWidgets');
+          });
+          return services.map(function (service) {
+            return Em.Object.create({
+              value: service.get('serviceName'),
+              label: service.get('displayName'),
+              isActive: function () {
+                var activeService = view.get('activeService') ? view.get('activeService') : view.get('controller.content.serviceName');
+                return this.get('value') == activeService;
+              }.property('value', 'view.activeService')
+            })
+          });
+        }.property('activeService'),
+
+        filterByService: function (event) {
+          this.set('activeService', event.context);
+        },
+
+        createWidget: function () {
+          this.get('parentView').onPrimary();
+          this.get('controller').createWidget();
+        },
+
+        ensureTooltip: function () {
+          Em.run.later(this, function () {
+            App.tooltip($("[rel='shared-icon-tooltip']"));
+          }, 1000);
+        }.observes('activeService', 'parentView.isShowMineOnly'),
+
+        didInsertElement: function () {
+          this.ensureTooltip();
+        }
+      })
+    });
+  }
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers/main/service/info/summary.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/summary.js b/ambari-web/app/controllers/main/service/info/summary.js
index d696334..3d7483a 100644
--- a/ambari-web/app/controllers/main/service/info/summary.js
+++ b/ambari-web/app/controllers/main/service/info/summary.js
@@ -17,7 +17,7 @@
 
 var App = require('app');
 
-App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMixin, {
+App.MainServiceInfoSummaryController = Em.Controller.extend({
   name: 'mainServiceInfoSummaryController',
 
   selectedFlumeAgent: null,
@@ -40,10 +40,6 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix
    */
   isPreviousRangerConfigsCallFailed: false,
 
-  layoutNameSuffix: "_dashboard",
-
-  sectionNameSuffix: "_SUMMARY",
-
   /**
    * HiveServer2 JDBC connection endpoint data
    * @type {array}
@@ -112,18 +108,6 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix
   ],
 
   /**
-   * Some widget has type `GRAPH`
-   *
-   * @type {boolean}
-   */
-  someWidgetGraphExists: Em.computed.someBy('widgets', 'widgetType', 'GRAPH'),
-
-  /**
-   * @type {boolean}
-   */
-  showTimeRangeControl: Em.computed.or('!isServiceWithEnhancedWidgets', 'someWidgetGraphExists'),
-
-  /**
    * Set initial Ranger plugins data
    * @method setRangerPlugins
    */
@@ -425,437 +409,6 @@ App.MainServiceInfoSummaryController = Em.Controller.extend(App.WidgetSectionMix
     });
   },
 
-
-  /**
-   * @type {boolean}
-   */
-  isWidgetLayoutsLoaded: false,
-
-  /**
-   * @type {boolean}
-   */
-  isAllSharedWidgetsLoaded: false,
-
-  /**
-   * @type {boolean}
-   */
-  isMineWidgetsLoaded: false,
-
-
-  /**
-   * load widget layouts across all users in CLUSTER scope
-   * @returns {$.ajax}
-   */
-  loadWidgetLayouts: function () {
-    this.set('isWidgetLayoutsLoaded', false);
-    return App.ajax.send({
-      name: 'widgets.layouts.get',
-      sender: this,
-      data: {
-        sectionName: this.get('sectionName')
-      },
-      success: 'loadWidgetLayoutsSuccessCallback'
-    });
-  },
-
-  loadWidgetLayoutsSuccessCallback: function (data) {
-    App.widgetLayoutMapper.map(data);
-    this.set('isWidgetLayoutsLoaded', true);
-  },
-
-
-  /**
-   * load all shared widgets to show on widget browser
-   * @returns {$.ajax}
-   */
-  loadAllSharedWidgets: function () {
-    this.set('isAllSharedWidgetsLoaded', false);
-    return App.ajax.send({
-      name: 'widgets.all.shared.get',
-      sender: this,
-      success: 'loadAllSharedWidgetsSuccessCallback'
-    });
-  },
-
-  /**
-   * success callback of <code>loadAllSharedWidgets</code>
-   * @param {object|null} data
-   */
-  loadAllSharedWidgetsSuccessCallback: function (data) {
-    var widgetIds = this.get('widgets').mapProperty('id');
-    if (data.items[0] && data.items.length) {
-      this.set("allSharedWidgets",
-        data.items.filter(function (widget) {
-          return widget.WidgetInfo.widget_type != "HEATMAP";
-        }).map(function (widget) {
-          var widgetType = widget.WidgetInfo.widget_type;
-          var widgetName = widget.WidgetInfo.widget_name;
-          var widgetId =  widget.WidgetInfo.id;
-          return Em.Object.create({
-            id: widgetId,
-            widgetName: widgetName,
-            description: widget.WidgetInfo.description,
-            widgetType: widgetType,
-            iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png",
-            serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'),
-            added: widgetIds.contains(widgetId),
-            isShared: widget.WidgetInfo.scope == "CLUSTER"
-          });
-        })
-      );
-    }
-    this.set('isAllSharedWidgetsLoaded', true);
-  },
-
-  allSharedWidgets: [],
-  mineWidgets: [],
-
-  /**
-   * load all mine widgets of current user to show on widget browser
-   * @returns {$.ajax}
-   */
-  loadMineWidgets: function () {
-    this.set('isMineWidgetsLoaded', false);
-    return App.ajax.send({
-      name: 'widgets.all.mine.get',
-      sender: this,
-      data: {
-        loginName: App.router.get('loginName')
-      },
-      success: 'loadMineWidgetsSuccessCallback'
-    });
-  },
-
-  /**
-   * success callback of <code>loadMineWidgets</code>
-   * @param {object|null} data
-   */
-  loadMineWidgetsSuccessCallback: function (data) {
-    var widgetIds = this.get('widgets').mapProperty('id');
-    if (data.items[0] && data.items.length) {
-      this.set("mineWidgets",
-        data.items.filter(function (widget) {
-          return widget.WidgetInfo.widget_type != "HEATMAP";
-        }).map(function (widget) {
-          var widgetType = widget.WidgetInfo.widget_type;
-          var widgetName = widget.WidgetInfo.widget_name;
-          var widgetId =  widget.WidgetInfo.id;
-          return Em.Object.create({
-            id: widget.WidgetInfo.id,
-            widgetName: widgetName,
-            description: widget.WidgetInfo.description,
-            widgetType: widgetType,
-            iconPath: "/img/widget-" + widgetType.toLowerCase() + ".png",
-            serviceName: JSON.parse(widget.WidgetInfo.metrics).mapProperty('service_name').uniq().join('-'),
-            added: widgetIds.contains(widgetId),
-            isShared: widget.WidgetInfo.scope == "CLUSTER"
-          });
-        })
-      );
-    } else {
-      this.set("mineWidgets", []);
-    }
-    this.set('isMineWidgetsLoaded', true);
-  },
-
-  /**
-   * add widgets, on click handler for "Add"
-   */
-  addWidget: function (event) {
-    var widgetToAdd = event.context;
-    var activeLayout = this.get('activeWidgetLayout');
-    var widgetIds = activeLayout.get('widgets').map(function(widget) {
-      return {
-        "id": widget.get("id")
-      }
-    });
-    widgetIds.pushObject({
-      "id": widgetToAdd.id
-    });
-    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": widgetIds
-      }
-    };
-
-    widgetToAdd.set('added', !widgetToAdd.added);
-    return App.ajax.send({
-      name: 'widget.layout.edit',
-      sender: this,
-      data: {
-        layoutId: activeLayout.get("id"),
-        data: data
-      },
-      success: 'updateActiveLayout'
-    });
-  },
-
-  /**
-   * hide widgets, on click handler for "Added"
-   */
-  hideWidget: function (event) {
-    var widgetToHide = event.context;
-    var activeLayout = this.get('activeWidgetLayout');
-    var widgetIds = activeLayout.get('widgets').map(function (widget) {
-      return {
-        "id": widget.get("id")
-      }
-    });
-    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": widgetIds.filter(function (widget) {
-          return widget.id !== widgetToHide.id;
-        })
-      }
-    };
-
-    widgetToHide.set('added', !widgetToHide.added);
-    return App.ajax.send({
-      name: 'widget.layout.edit',
-      sender: this,
-      data: {
-        layoutId: activeLayout.get("id"),
-        data: data
-      },
-      success: 'hideWidgetSuccessCallback'
-    });
-
-  },
-
-  /**
-   * @param {object|null} data
-   * @param {object} opt
-   * @param {object} params
-   */
-  hideWidgetSuccessCallback: function (data, opt, params) {
-    params.data.WidgetLayoutInfo.widgets = params.data.WidgetLayoutInfo.widgets.map(function (widget) {
-      return {
-        WidgetInfo: {
-          id: widget.id
-        }
-      }
-    });
-    App.widgetLayoutMapper.map({items: [params.data]});
-    this.propertyDidChange('widgets');
-  },
-
-  /**
-   * update current active widget layout
-   */
-  updateActiveLayout: function () {
-    this.getActiveWidgetLayout();
-  },
-
-  /**
-   * delete widgets, on click handler for "Delete"
-   */
-  deleteWidget: function (event) {
-    var widget = event.context;
-    var self = this;
-    var confirmMsg =  widget.get('isShared') ? Em.I18n.t('dashboard.widgets.browser.action.delete.shared.bodyMsg').format(widget.widgetName) :  Em.I18n.t('dashboard.widgets.browser.action.delete.mine.bodyMsg').format(widget.widgetName);
-    var bodyMessage = Em.Object.create({
-      confirmMsg: confirmMsg,
-      confirmButton: Em.I18n.t('dashboard.widgets.browser.action.delete.btnMsg')
-    });
-    return App.showConfirmationFeedBackPopup(function (query) {
-      return App.ajax.send({
-        name: 'widget.action.delete',
-        sender: self,
-        data: {
-          id: widget.id
-        },
-        success: 'updateWidgetBrowser'
-      });
-
-    }, bodyMessage);
-  },
-
-  /**
-   * update widget browser content after deleted some widget
-   */
-  updateWidgetBrowser: function () {
-    this.loadAllSharedWidgets();
-    this.loadMineWidgets();
-  },
-
-  /**
-   * Share widgets, on click handler for "Share"
-   */
-  shareWidget: function (event) {
-    var widget = event.context;
-    var self = this;
-    var bodyMessage = Em.Object.create({
-      confirmMsg: Em.I18n.t('dashboard.widgets.browser.action.share.confirmation'),
-      confirmButton: Em.I18n.t('dashboard.widgets.browser.action.share')
-    });
-    return App.showConfirmationFeedBackPopup(function (query) {
-      return App.ajax.send({
-        name: 'widgets.wizard.edit',
-        sender: self,
-        data: {
-          data: {
-            "WidgetInfo": {
-              "widget_name": widget.get("widgetName"),
-              "scope": "CLUSTER"
-            }
-          },
-          widgetId: widget.get("id")
-        },
-        success: 'updateWidgetBrowser'
-      });
-    }, bodyMessage);
-  },
-
-  /**
-   * create widget
-   */
-  createWidget: function () {
-    App.router.send('createServiceWidget', Em.Object.create({
-      layout: this.get('activeWidgetLayout'),
-      serviceName: this.get('content.serviceName')
-    }));
-  },
-
-  /**
-   * edit widget
-   * @param {App.Widget} content
-   */
-  editWidget: function (content) {
-    content.set('serviceName', this.get('content.serviceName'));
-    App.router.send('editServiceWidget', content);
-  },
-
-  /**
-   * launch Widgets Browser popup
-   * @method showPopup
-   * @return {App.ModalPopup}
-   */
-  goToWidgetsBrowser: function () {
-    var self = this;
-
-    return App.ModalPopup.show({
-      header: Em.I18n.t('dashboard.widgets.browser.header'),
-
-      classNames: ['common-modal-wrapper', 'widgets-browser-popup'],
-      modalDialogClasses: ['modal-lg'],
-      onPrimary: function () {
-        this.hide();
-        self.set('isAllSharedWidgetsLoaded', false);
-        self.set('allSharedWidgets', []);
-        self.set('isMineWidgetsLoaded', false);
-        self.set('mineWidgets', []);
-      },
-      autoHeight: false,
-      isHideBodyScroll: false,
-      footerClass: Ember.View.extend({
-        templateName: require('templates/common/modal_popups/widget_browser_footer'),
-        isShowMineOnly: false,
-        onPrimary: function() {
-          this.get('parentView').onPrimary();
-        }
-      }),
-      isShowMineOnly: false,
-      bodyClass: Ember.View.extend({
-        templateName: require('templates/common/modal_popups/widget_browser_popup'),
-        controller: self,
-        willInsertElement: function () {
-          this.get('controller').loadAllSharedWidgets();
-          this.get('controller').loadMineWidgets();
-        },
-
-        isLoaded: Em.computed.and('controller.isAllSharedWidgetsLoaded', 'controller.isMineWidgetsLoaded'),
-
-        isWidgetEmptyList: Em.computed.empty('filteredContent'),
-
-        activeService: '',
-        activeStatus: '',
-
-        content: function () {
-          if (this.get('parentView.isShowMineOnly')) {
-            return this.get('controller.mineWidgets');
-          } else {
-            // merge my widgets and all shared widgets, no duplicated is allowed
-            var content = [];
-            var widgetMap = {};
-            var allWidgets = this.get('controller.allSharedWidgets').concat(this.get('controller.mineWidgets'));
-            allWidgets.forEach(function(widget) {
-              if (!widgetMap[widget.get("id")]) {
-                content.pushObject(widget);
-                widgetMap[widget.get("id")] = true;
-              }
-            });
-            return content;
-          }
-        }.property('controller.allSharedWidgets.length', 'controller.isAllSharedWidgetsLoaded',
-          'controller.mineWidgets.length', 'controller.isMineWidgetsLoaded', 'parentView.isShowMineOnly'),
-
-        /**
-         * displaying content filtered by service name and status.
-         */
-        filteredContent: function () {
-          var activeService = this.get('activeService') ? this.get('activeService') : this.get('controller.content.serviceName');
-          var result = [];
-          this.get('content').forEach(function (widget) {
-            if (widget.get('serviceName').indexOf(activeService) >= 0) {
-              result.pushObject(widget);
-            }
-          });
-          return result;
-        }.property('content', 'activeService', 'activeStatus'),
-
-        /**
-         * service name filter
-         */
-        services: function () {
-          var view = this;
-          var services = App.Service.find().filter(function(item){
-            var stackService =  App.StackService.find().findProperty('serviceName', item.get('serviceName'));
-            return stackService.get('isServiceWithWidgets');
-          });
-          return services.map(function (service) {
-            return Em.Object.create({
-              value: service.get('serviceName'),
-              label: service.get('displayName'),
-              isActive: function () {
-                var activeService = view.get('activeService') ? view.get('activeService') : view.get('controller.content.serviceName');
-                return this.get('value') == activeService;
-              }.property('value', 'view.activeService')
-            })
-          });
-        }.property('activeService'),
-
-        filterByService: function (event) {
-          this.set('activeService', event.context);
-        },
-
-        createWidget: function () {
-          this.get('parentView').onPrimary();
-          this.get('controller').createWidget();
-        },
-
-        ensureTooltip: function () {
-          Em.run.later(this, function () {
-            App.tooltip($("[rel='shared-icon-tooltip']"));
-          }, 1000);
-        }.observes('activeService', 'parentView.isShowMineOnly'),
-
-        didInsertElement: function () {
-          this.ensureTooltip();
-        }
-      })
-    });
-  },
-
   goToView: function(event) {
     App.router.route(event.context.get('internalAmbariUrl'));
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js b/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
index e833ead..a46c5e4 100644
--- a/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
+++ b/ambari-web/app/controllers/main/service/widgets/create/wizard_controller.js
@@ -417,7 +417,7 @@ App.WidgetWizardController = App.WizardController.extend({
     var self = this;
     var successCallBack = function() {
       self.get('popup').hide();
-      App.router.transitionTo('main.services.service.summary', service);
+      App.router.transitionTo('main.services.service.metrics', service);
       App.get('router.updateController').updateAll();
     };
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 7cde3d1..3c4f038 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -2199,6 +2199,7 @@ Em.I18n.translations = {
   'services.service.info.menu.summary':'Summary',
   'services.service.info.menu.configs':'Configs',
   'services.service.info.menu.heatmaps':'Heatmaps',
+  'services.service.info.menu.metrics':'Metrics',
   'services.service.info.summary.hostsRunningMonitor':'{0}/{1}',
   'services.service.info.summary.serversHostCount':'{0} more',
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/styles/enhanced_service_dashboard.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/enhanced_service_dashboard.less b/ambari-web/app/styles/enhanced_service_dashboard.less
index 34a4763..00b46a8 100644
--- a/ambari-web/app/styles/enhanced_service_dashboard.less
+++ b/ambari-web/app/styles/enhanced_service_dashboard.less
@@ -26,6 +26,10 @@
 
   clear: both;
 
+  .service-widgets-box {
+    padding: 10px 1.1% 10px 1.1%;
+  }
+
   #add-widget-action-box {
     background-color: @add-widget-btn-color;
     width: 97%;
@@ -69,7 +73,7 @@
     width: 93%;
   }
   .span2p4 {
-    width: 22.7%;
+    width: 24.4%;
     height: 100%;
     background-color: white;
     margin: 5px 0 5px 5px;
@@ -188,6 +192,26 @@
   }
 }
 
+@media (min-width: 1200px) {
+
+  .service-metrics-block .service-widgets-box {
+    padding: 10px 1.3% 10px 1.3%;
+  }
+
+  #widget_layout .span2p4 {
+    width: 24.5%;
+    *width: 24.5%;
+  }
+}
+
+@media (min-width: 1500px) {
+
+  #widget_layout .span2p4 {
+    width: 24.6%;
+    *width: 24.6%;
+  }
+}
+
 #widget-preview {
   max-width: 200px;
   margin: auto;

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/templates/main/service/info/metrics.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/info/metrics.hbs b/ambari-web/app/templates/main/service/info/metrics.hbs
new file mode 100644
index 0000000..6834c06
--- /dev/null
+++ b/ambari-web/app/templates/main/service/info/metrics.hbs
@@ -0,0 +1,104 @@
+{{!
+* 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.
+}}
+
+{{#if view.serviceHasMetrics}}
+  <div class="service-metrics-block">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <div class="row">
+          <div class="col-md-7 col-lg-7">
+            <h4 class="panel-title">{{t services.service.metrics}}</h4>
+          </div>
+          <div class="col-md-5 col-lg-5">
+            {{#if showTimeRangeControl}}
+              {{view view.timeRangeListView}}
+            {{/if}}
+            {{#if isServiceWithEnhancedWidgets}}
+              {{#if isAmbariMetricsInstalled}}
+                <div class="btn-group pull-right actions">
+                  <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+                    {{t common.actions}} &nbsp;<span class="caret"></span>
+                  </button>
+                  <ul class="dropdown-menu">
+                    {{#each option in view.widgetActions}}
+                      <li {{bindAttr class="option.layouts:dropdown-submenu"}}>
+                        {{#if option.isAction}}
+                          <a href="#" {{action doWidgetAction option.action target="view"}}>
+                            <i {{bindAttr class="option.class"}}></i>
+                            {{option.label}}
+                          </a>
+                          {{#if option.layouts}}
+                            <ul class="dropdown-menu">
+                              {{#each layout in option.layouts}}
+                                <li>
+                                  <a href="javascript:void(0);">
+                                    {{layout.layoutName}}
+                                  </a>
+                                </li>
+                              {{/each}}
+                            </ul>
+                          {{/if}}
+                        {{/if}}
+                      </li>
+                    {{/each}}
+                  </ul>
+                </div>
+              {{/if}}
+            {{/if}}
+          </div>
+        </div>
+      </div>
+      <div class="panel-body service-widgets-box">
+        {{#if isServiceWithEnhancedWidgets}}
+          <div id="widget_layout" class="thumbnails">
+            {{#each widget in controller.widgets}}
+              <div class="widget span2p4" {{bindAttr id="widget.id"}}>
+                {{view widget.viewClass contentBinding="widget" idBinding="widget.id"}}
+              </div>
+            {{/each}}
+            {{#if isAmbariMetricsInstalled}}
+              <div class="span2p4">
+                <button id="add-widget-action-box" {{action "goToWidgetsBrowser" controller.content
+                                                            target="controller"}}
+                        rel="add-widget-tooltip" {{translateAttr
+                  data-original-title="dashboard.widgets.addButton.tooltip"}}>
+                  <i class="glyphicon glyphicon-plus"></i></button>
+              </div>
+            {{/if}}
+          </div>
+        {{/if}}
+        <table class="graphs">
+          {{#each graphs in view.serviceMetricGraphs}}
+            <tr>
+              {{#each graph in graphs}}
+                <td>
+                  <div>
+                    {{view graph}}
+                  </div>
+                </td>
+              {{/each}}
+            </tr>
+          {{/each}}
+        </table>
+      </div>
+    </div>
+  </div>
+{{/if}}
+
+
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/templates/main/service/info/summary.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/info/summary.hbs b/ambari-web/app/templates/main/service/info/summary.hbs
index 075cae0..b0c9e7f 100644
--- a/ambari-web/app/templates/main/service/info/summary.hbs
+++ b/ambari-web/app/templates/main/service/info/summary.hbs
@@ -134,90 +134,6 @@
       </div>
     </div>
   </div>
-  {{! widgets in the metrics panel are loaded seperately from summary page text information
-      and does not get block due to any global API poller information }}
-  {{#if view.isServiceMetricLoaded}}
-    <div class="service-metrics-block">
-      <div class="panel panel-default">
-        <div class="panel-heading">
-          <div class="row">
-            <div class="col-md-7 col-lg-7">
-              <h4 class="panel-title">{{t services.service.metrics}}</h4>
-            </div>
-            <div class="col-md-5 col-lg-5">
-              {{#if showTimeRangeControl}}
-                {{view view.timeRangeListView}}
-              {{/if}}
-              {{#if isServiceWithEnhancedWidgets}}
-                {{#if isAmbariMetricsInstalled}}
-                  <div class="btn-group pull-right actions">
-                    <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
-                      {{t common.actions}} &nbsp;<span class="caret"></span>
-                    </button>
-                    <ul class="dropdown-menu">
-                      {{#each option in view.widgetActions}}
-                        <li {{bindAttr class="option.layouts:dropdown-submenu"}}>
-                          {{#if option.isAction}}
-                            <a href="#" {{action doWidgetAction option.action target="view"}}>
-                              <i {{bindAttr class="option.class"}}></i>
-                              {{option.label}}
-                            </a>
-                            {{#if option.layouts}}
-                              <ul class="dropdown-menu">
-                                {{#each layout in option.layouts}}
-                                  <li>
-                                    <a href="javascript:void(0);">
-                                      {{layout.layoutName}}
-                                    </a>
-                                  </li>
-                                {{/each}}
-                              </ul>
-                            {{/if}}
-                          {{/if}}
-                        </li>
-                      {{/each}}
-                    </ul>
-                  </div>
-                {{/if}}
-              {{/if}}
-            </div>
-          </div>
-        </div>
-        <div class="panel-body">
-          {{#if isServiceWithEnhancedWidgets}}
-            <div id="widget_layout" class="thumbnails">
-              {{#each widget in controller.widgets}}
-                <div class="widget span2p4" {{bindAttr id="widget.id"}}>
-                  {{view widget.viewClass contentBinding="widget" idBinding="widget.id"}}
-                </div>
-              {{/each}}
-              {{#if isAmbariMetricsInstalled}}
-                <div class="span2p4">
-                  <button id="add-widget-action-box"
-                    {{action "goToWidgetsBrowser" controller.content target="controller"}}
-                          rel="add-widget-tooltip" {{translateAttr data-original-title="dashboard.widgets.addButton.tooltip"}}>
-                    <i class="glyphicon glyphicon-plus"></i></button>
-                </div>
-              {{/if}}
-            </div>
-          {{/if}}
-          <table class="graphs">
-            {{#each graphs in view.serviceMetricGraphs}}
-              <tr>
-                {{#each graph in graphs}}
-                  <td>
-                    <div>
-                      {{view graph}}
-                    </div>
-                  </td>
-                {{/each}}
-              </tr>
-            {{/each}}
-          </table>
-        </div>
-      </div>
-    </div>
-  {{/if}}
 </div>
 
 {{#if view.collapsedSections}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/templates/main/service/item.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/item.hbs b/ambari-web/app/templates/main/service/item.hbs
index e942eb1..df26a9d 100644
--- a/ambari-web/app/templates/main/service/item.hbs
+++ b/ambari-web/app/templates/main/service/item.hbs
@@ -16,7 +16,10 @@
 * limitations under the License.
 }}
 
-{{view App.MainServiceInfoMenuView configTabBinding="view.hasConfigTab" heatmapTabBinding="view.hasHeatmapTab"}}
+{{view App.MainServiceInfoMenuView
+    configTabBinding="view.hasConfigTab"
+    heatmapTabBinding="view.hasHeatmapTab"
+    metricTabBinding="view.hasMetricTab"}}
 
 {{#isAuthorized "SERVICE.RUN_CUSTOM_COMMAND, SERVICE.RUN_SERVICE_CHECK, SERVICE.START_STOP, SERVICE.TOGGLE_MAINTENANCE, SERVICE.ENABLE_HA"}}
 <div class="service-button">

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 8031434..50729a7 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -348,6 +348,7 @@ require('views/main/charts/heatmap/heatmap_rack');
 require('views/main/charts/heatmap/heatmap_host');
 require('views/main/charts/heatmap/heatmap_host_detail');
 require('views/main/service/info/heatmap_view');
+require('views/main/service/info/metrics_view');
 
 require('views/main/service/widgets/create/wizard_view');
 require('views/main/service/widgets/create/step1_view');

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views/main/service/info/menu.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/info/menu.js b/ambari-web/app/views/main/service/info/menu.js
index 3533a72..89d5401 100644
--- a/ambari-web/app/views/main/service/info/menu.js
+++ b/ambari-web/app/views/main/service/info/menu.js
@@ -45,6 +45,13 @@ App.MainServiceInfoMenuView = Em.CollectionView.extend({
         routing: 'configs'
       });
     }
+    if (this.get('metricTab')) {
+      menuItems.push({
+        label: Em.I18n.t('services.service.info.menu.metrics'),
+        id: 'metrics-service-tab',
+        routing: 'metrics'
+      });
+    }
     return menuItems;
   }.property(),
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views/main/service/info/metrics_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/info/metrics_view.js b/ambari-web/app/views/main/service/info/metrics_view.js
new file mode 100644
index 0000000..161dce1
--- /dev/null
+++ b/ambari-web/app/views/main/service/info/metrics_view.js
@@ -0,0 +1,290 @@
+/**
+ * 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');
+var misc = require('utils/misc');
+require('views/main/service/service');
+require('data/service_graph_config');
+
+App.MainServiceInfoMetricsView = Em.View.extend(App.Persist, App.TimeRangeMixin, {
+  templateName: require('templates/main/service/info/metrics'),
+  /**
+   * @property {Number} chunkSize - number of columns in Metrics section
+   */
+  chunkSize: 5,
+
+  service: null,
+
+  svc: function () {
+    var svc = this.get('controller.content');
+    var svcName = svc.get('serviceName');
+    if (svcName) {
+      switch (svcName.toLowerCase()) {
+        case 'hdfs':
+          svc = App.HDFSService.find().objectAt(0);
+          break;
+        case 'yarn':
+          svc = App.YARNService.find().objectAt(0);
+          break;
+        case 'hbase':
+          svc = App.HBaseService.find().objectAt(0);
+          break;
+        case 'flume':
+          svc = App.FlumeService.find().objectAt(0);
+          break;
+        default:
+          break;
+      }
+    }
+    return svc;
+  }.property('controller.content.serviceName').volatile(),
+
+  getServiceModel: function (serviceName) {
+    var extended = App.Service.extendedModel[serviceName];
+    if (extended) {
+      return App[extended].find().objectAt(0);
+    }
+    return App.Service.find(serviceName);
+  },
+
+  serviceName: Em.computed.alias('service.serviceName'),
+
+  /**
+   * Contains graphs for this particular service
+   */
+  serviceMetricGraphs: [],
+
+  /**
+   * @type {boolean}
+   * @default false
+   */
+  serviceHasMetrics: false,
+
+  /**
+   * Key-name to store time range in Persist
+   * @type {string}
+   */
+  persistKey: Em.computed.format('time-range-service-{0}', 'service.serviceName'),
+
+  didInsertElement: function () {
+    var svcName = this.get('controller.content.serviceName');
+    this.set('service', this.getServiceModel(svcName));
+    var isMetricsSupported = svcName !== 'STORM' || App.get('isStormMetricsSupported');
+
+    this.get('controller').getActiveWidgetLayout();
+    if (App.get('supports.customizedWidgetLayout')) {
+      this.get('controller').loadWidgetLayouts();
+    }
+
+    if (svcName && isMetricsSupported) {
+      var allServices = require('data/service_graph_config');
+      this.constructGraphObjects(allServices[svcName.toLowerCase()]);
+    }
+    this.makeSortable();
+    this.addWidgetTooltip();
+  },
+
+  addWidgetTooltip: function() {
+    Em.run.later(this, function () {
+      App.tooltip($("[rel='add-widget-tooltip']"));
+      // enable description show up on hover
+      $('.img-thumbnail').hoverIntent(function() {
+        if ($(this).is('hover')) {
+          $(this).find('.hidden-description').delay(1000).fadeIn(200).end();
+        }
+      }, function() {
+        $(this).find('.hidden-description').stop().hide().end();
+      });
+    }, 1000);
+  },
+
+  willDestroyElement: function() {
+    $("[rel='add-widget-tooltip']").tooltip('destroy');
+    $('.img-thumbnail').off();
+    $('#widget_layout').sortable('destroy');
+    $('.widget.span2p4').detach().remove();
+    this.get('serviceMetricGraphs').clear();
+    this.set('service', null);
+  },
+
+   /*
+   * Find the graph class associated with the graph name, and split
+   * the array into sections of 5 for displaying on the page
+   * (will only display rows with 5 items)
+   */
+  constructGraphObjects: function (graphNames) {
+    var self = this,
+        stackService = App.StackService.find(this.get('controller.content.serviceName'));
+
+    if (!graphNames && !stackService.get('isServiceWithWidgets')) {
+      this.get('serviceMetricGraphs').clear();
+      this.set('serviceHasMetrics', false);
+      return;
+    }
+
+    // load time range(currentTimeRangeIndex) for current service from server
+    this.getUserPref(self.get('persistKey')).complete(function () {
+      var result = [], graphObjects = [], chunkSize = self.get('chunkSize');
+      if (graphNames) {
+        graphNames.forEach(function (graphName) {
+          graphObjects.push(App["ChartServiceMetrics" + graphName].extend());
+        });
+      }
+      while (graphObjects.length) {
+        result.push(graphObjects.splice(0, chunkSize));
+      }
+      self.set('serviceMetricGraphs', result);
+      self.set('serviceHasMetrics', true);
+    });
+  },
+
+  getUserPrefSuccessCallback: function (response, request) {
+    if (response) {
+      this.set('currentTimeRangeIndex', response);
+    }
+  },
+
+  getUserPrefErrorCallback: function (request) {
+    if (request.status === 404) {
+      this.postUserPref(this.get('persistKey'), 0);
+      this.set('currentTimeRangeIndex', 0);
+    }
+  },
+
+  /**
+   * list of static actions of widget
+   * @type {Array}
+   */
+  staticGeneralWidgetActions: [
+    Em.Object.create({
+      label: Em.I18n.t('dashboard.widgets.actions.browse'),
+      class: 'glyphicon glyphicon-th',
+      action: 'goToWidgetsBrowser',
+      isAction: true
+    })
+  ],
+
+  /**
+   *list of static actions of widget accessible to Admin/Operator privelege
+   * @type {Array}
+   */
+
+  staticAdminPrivelegeWidgetActions: [
+    Em.Object.create({
+      label: Em.I18n.t('dashboard.widgets.create'),
+      class: 'glyphicon glyphicon-plus',
+      action: 'createWidget',
+      isAction: true
+    })
+  ],
+
+  /**
+   * List of static actions related to widget layout
+   */
+  staticWidgetLayoutActions: [
+    Em.Object.create({
+      label: Em.I18n.t('dashboard.widgets.layout.save'),
+      class: 'glyphicon glyphicon-download-alt',
+      action: 'saveLayout',
+      isAction: true
+    }),
+    Em.Object.create({
+      label: Em.I18n.t('dashboard.widgets.layout.import'),
+      class: 'glyphicon glyphicon-file',
+      isAction: true,
+      layouts: App.WidgetLayout.find()
+    })
+  ],
+
+  /**
+   * @type {Array}
+   */
+  widgetActions: function() {
+    var options = [];
+    if (App.isAuthorized('SERVICE.MODIFY_CONFIGS')) {
+      if (App.supports.customizedWidgetLayout) {
+        options.pushObjects(this.get('staticWidgetLayoutActions'));
+      }
+      options.pushObjects(this.get('staticAdminPrivelegeWidgetActions'));
+    }
+    options.pushObjects(this.get('staticGeneralWidgetActions'));
+    return options;
+  }.property(''),
+
+  /**
+   * call action function defined in controller
+   * @param event
+   */
+  doWidgetAction: function(event) {
+    if($.isFunction(this.get('controller')[event.context])) {
+      this.get('controller')[event.context].apply(this.get('controller'));
+    }
+  },
+
+  /**
+   * onclick handler for a time range option
+   * @param {object} event
+   */
+  setTimeRange: function (event) {
+    var graphs = this.get('controller.widgets').filterProperty('widgetType', 'GRAPH'),
+      callback = function () {
+        graphs.forEach(function (widget) {
+          widget.set('properties.time_range', event.context.value);
+        });
+      };
+    this._super(event, callback);
+
+    // Preset time range is specified by user
+    if (event.context.value !== '0') {
+      callback();
+    }
+  },
+
+  /**
+   * Define if some widget is currently moving
+   * @type {boolean}
+   */
+  isMoving: false,
+
+  /**
+   * Make widgets' list sortable on New Dashboard style
+   */
+  makeSortable: function () {
+    var self = this;
+    $('html').on('DOMNodeInserted', '#widget_layout', function () {
+      $(this).sortable({
+        items: "> div",
+        cursor: "move",
+        tolerance: "pointer",
+        scroll: false,
+        update: function () {
+          var widgets = misc.sortByOrder($("#widget_layout .widget").map(function () {
+            return this.id;
+          }), self.get('controller.widgets'));
+          self.get('controller').saveWidgetLayout(widgets);
+        },
+        activate: function () {
+          self.set('isMoving', true);
+        },
+        deactivate: function () {
+          self.set('isMoving', false);
+        }
+      }).disableSelection();
+      $('html').off('DOMNodeInserted', '#widget_layout');
+    });
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/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 91b2ca3..a4769e6 100644
--- a/ambari-web/app/views/main/service/info/summary.js
+++ b/ambari-web/app/views/main/service/info/summary.js
@@ -21,13 +21,10 @@ var misc = require('utils/misc');
 require('views/main/service/service');
 require('data/service_graph_config');
 
-App.MainServiceInfoSummaryView = Em.View.extend(App.Persist, App.TimeRangeMixin, {
+App.MainServiceInfoSummaryView = Em.View.extend({
   templateName: require('templates/main/service/info/summary'),
-  /**
-   * @property {Number} chunkSize - number of columns in Metrics section
-   */
-  chunkSize: 5,
-  attributes:null,
+
+  attributes: null,
 
   /**
    * Contain array with list of master components from <code>App.Service.hostComponets</code> which are
@@ -165,6 +162,47 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.Persist, App.TimeRangeMixin,
     Em.run.once(self, 'setComponentsContent');
   }.observes('service.hostComponents.length', 'service.slaveComponents.@each.totalCount', 'service.clientComponents.@each.totalCount'),
 
+  loadServiceSummary: function () {
+    var serviceName = this.get('serviceName');
+    var serviceSummaryView = null;
+
+    if (!serviceName) {
+      return;
+    }
+
+    if (this.get('oldServiceName')) {
+      // do not delete it!
+      return;
+    }
+
+    var customServiceView = this.get('serviceCustomViewsMap')[serviceName];
+    if (customServiceView) {
+      serviceSummaryView = customServiceView.extend({
+        service: this.get('service')
+      });
+    } else {
+      serviceSummaryView = Em.View.extend(App.MainDashboardServiceViewWrapper, {
+        templateName: this.get('templatePathPrefix') + 'base'
+      });
+    }
+    this.set('serviceSummaryView', serviceSummaryView);
+    this.set('oldServiceName', serviceName);
+  }.observes('serviceName'),
+
+  didInsertElement: function () {
+    this._super();
+    var svcName = this.get('controller.content.serviceName');
+    this.set('service', this.getServiceModel(svcName));
+    App.loadTimer.finish('Service Summary Page');
+  },
+
+  willDestroyElement: function() {
+    this.set('service', null);
+    this.get('mastersObj').clear();
+    this.get('slavesObj').clear();
+    this.get('clientObj').clear();
+  },
+
   setComponentsContent: function() {
     Em.run.next(function() {
       if (Em.isNone(this.get('service'))) {
@@ -372,270 +410,5 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.Persist, App.TimeRangeMixin,
 
   rollingRestartStaleConfigSlaveComponents: function (componentName) {
     batchUtils.launchHostComponentRollingRestart(componentName.context, this.get('service.displayName'), this.get('service.passiveState') === "ON", true);
-  },
-
-   /*
-   * Find the graph class associated with the graph name, and split
-   * the array into sections of 5 for displaying on the page
-   * (will only display rows with 5 items)
-   */
-  constructGraphObjects: function (graphNames) {
-    var self = this,
-        stackService = App.StackService.find(this.get('controller.content.serviceName'));
-
-    if (!graphNames && !stackService.get('isServiceWithWidgets')) {
-      this.get('serviceMetricGraphs').clear();
-      this.set('isServiceMetricLoaded', false);
-      return;
-    }
-
-    // load time range(currentTimeRangeIndex) for current service from server
-    this.getUserPref(self.get('persistKey')).complete(function () {
-      var result = [], graphObjects = [], chunkSize = self.get('chunkSize');
-      if (graphNames) {
-        graphNames.forEach(function (graphName) {
-          graphObjects.push(App["ChartServiceMetrics" + graphName].extend());
-        });
-      }
-      while (graphObjects.length) {
-        result.push(graphObjects.splice(0, chunkSize));
-      }
-      self.set('serviceMetricGraphs', result);
-      self.set('isServiceMetricLoaded', true);
-    });
-  },
-
-  /**
-   * Contains graphs for this particular service
-   */
-  serviceMetricGraphs: [],
-
-  /**
-   * @type {boolean}
-   * @default false
-   */
-  isServiceMetricLoaded: false,
-
-  /**
-   * Key-name to store time range in Persist
-   * @type {string}
-   */
-  persistKey: Em.computed.format('time-range-service-{0}', 'service.serviceName'),
-
-  getUserPrefSuccessCallback: function (response, request) {
-    if (response) {
-      this.set('currentTimeRangeIndex', response);
-    }
-  },
-
-  getUserPrefErrorCallback: function (request) {
-    if (request.status === 404) {
-      this.postUserPref(this.get('persistKey'), 0);
-      this.set('currentTimeRangeIndex', 0);
-    }
-  },
-
-  /**
-   * list of static actions of widget
-   * @type {Array}
-   */
-  staticGeneralWidgetActions: [
-    Em.Object.create({
-      label: Em.I18n.t('dashboard.widgets.actions.browse'),
-      class: 'glyphicon glyphicon-th',
-      action: 'goToWidgetsBrowser',
-      isAction: true
-    })
-  ],
-
-  /**
-   *list of static actions of widget accessible to Admin/Operator privelege
-   * @type {Array}
-   */
-
-  staticAdminPrivelegeWidgetActions: [
-    Em.Object.create({
-      label: Em.I18n.t('dashboard.widgets.create'),
-      class: 'glyphicon glyphicon-plus',
-      action: 'createWidget',
-      isAction: true
-    })
-  ],
-
-  /**
-   * List of static actions related to widget layout
-   */
-  staticWidgetLayoutActions: [
-    Em.Object.create({
-      label: Em.I18n.t('dashboard.widgets.layout.save'),
-      class: 'glyphicon glyphicon-download-alt',
-      action: 'saveLayout',
-      isAction: true
-    }),
-    Em.Object.create({
-      label: Em.I18n.t('dashboard.widgets.layout.import'),
-      class: 'glyphicon glyphicon-file',
-      isAction: true,
-      layouts: App.WidgetLayout.find()
-    })
-  ],
-
-  /**
-   * @type {Array}
-   */
-  widgetActions: function() {
-    var options = [];
-    if (App.isAuthorized('SERVICE.MODIFY_CONFIGS')) {
-      if (App.supports.customizedWidgetLayout) {
-        options.pushObjects(this.get('staticWidgetLayoutActions'));
-      }
-      options.pushObjects(this.get('staticAdminPrivelegeWidgetActions'));
-    }
-    options.pushObjects(this.get('staticGeneralWidgetActions'));
-    return options;
-  }.property(''),
-
-  /**
-   * call action function defined in controller
-   * @param event
-   */
-  doWidgetAction: function(event) {
-    if($.isFunction(this.get('controller')[event.context])) {
-      this.get('controller')[event.context].apply(this.get('controller'));
-    }
-  },
-
-  /**
-   * onclick handler for a time range option
-   * @param {object} event
-   */
-  setTimeRange: function (event) {
-    var graphs = this.get('controller.widgets').filterProperty('widgetType', 'GRAPH'),
-      callback = function () {
-        graphs.forEach(function (widget) {
-          widget.set('properties.time_range', event.context.value);
-        });
-      };
-    this._super(event, callback);
-
-    // Preset time range is specified by user
-    if (event.context.value !== '0') {
-      callback();
-    }
-  },
-
-  loadServiceSummary: function () {
-    var serviceName = this.get('serviceName');
-    var serviceSummaryView = null;
-
-    if (!serviceName) {
-      return;
-    }
-
-    if (this.get('oldServiceName')) {
-      // do not delete it!
-      return;
-    }
-
-    var customServiceView = this.get('serviceCustomViewsMap')[serviceName];
-    if (customServiceView) {
-      serviceSummaryView = customServiceView.extend({
-        service: this.get('service')
-      });
-    } else {
-      serviceSummaryView = Em.View.extend(App.MainDashboardServiceViewWrapper, {
-        templateName: this.get('templatePathPrefix') + 'base'
-      });
-    }
-    this.set('serviceSummaryView', serviceSummaryView);
-    this.set('oldServiceName', serviceName);
-  }.observes('serviceName'),
-
-
-  /**
-   * Service metrics panel not displayed when metrics service (ex:Ganglia) is not in stack definition.
-   *
-   * @type {boolean}
-   */
-  isNoServiceMetricsService: Em.computed.equal('App.services.serviceMetrics.length', 0),
-
-  didInsertElement: function () {
-    this._super();
-    var svcName = this.get('controller.content.serviceName');
-    this.set('service', this.getServiceModel(svcName));
-    var isMetricsSupported = svcName !== 'STORM' || App.get('isStormMetricsSupported');
-
-    this.get('controller').getActiveWidgetLayout();
-    if (App.get('supports.customizedWidgetLayout')) {
-      this.get('controller').loadWidgetLayouts();
-    }
-
-    if (svcName && isMetricsSupported) {
-      var allServices = require('data/service_graph_config');
-      this.constructGraphObjects(allServices[svcName.toLowerCase()]);
-    }
-    this.makeSortable();
-    this.addWidgetTooltip();
-    App.loadTimer.finish('Service Summary Page');
-  },
-
-  addWidgetTooltip: function() {
-    Em.run.later(this, function () {
-      App.tooltip($("[rel='add-widget-tooltip']"));
-      // enable description show up on hover
-      $('.img-thumbnail').hoverIntent(function() {
-        if ($(this).is('hover')) {
-          $(this).find('.hidden-description').delay(1000).fadeIn(200).end();
-        }
-      }, function() {
-        $(this).find('.hidden-description').stop().hide().end();
-      });
-    }, 1000);
-  },
-
-  willDestroyElement: function() {
-    $("[rel='add-widget-tooltip']").tooltip('destroy');
-    $('.img-thumbnail').off();
-    $('#widget_layout').sortable('destroy');
-    $('.widget.span2p4').detach().remove();
-    this.get('serviceMetricGraphs').clear();
-    this.set('service', null);
-    this.get('mastersObj').clear();
-    this.get('slavesObj').clear();
-    this.get('clientObj').clear();
-  },
-
-  /**
-   * Define if some widget is currently moving
-   * @type {boolean}
-   */
-  isMoving: false,
-
-  /**
-   * Make widgets' list sortable on New Dashboard style
-   */
-  makeSortable: function () {
-    var self = this;
-    $('html').on('DOMNodeInserted', '#widget_layout', function () {
-      $(this).sortable({
-        items: "> div",
-        cursor: "move",
-        tolerance: "pointer",
-        scroll: false,
-        update: function () {
-          var widgets = misc.sortByOrder($("#widget_layout .widget").map(function () {
-            return this.id;
-          }), self.get('controller.widgets'));
-          self.get('controller').saveWidgetLayout(widgets);
-        },
-        activate: function () {
-          self.set('isMoving', true);
-        },
-        deactivate: function () {
-          self.set('isMoving', false);
-        }
-      }).disableSelection();
-      $('html').off('DOMNodeInserted', '#widget_layout');
-    });
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/app/views/main/service/item.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/item.js b/ambari-web/app/views/main/service/item.js
index 43d75e6..45c783b 100644
--- a/ambari-web/app/views/main/service/item.js
+++ b/ambari-web/app/views/main/service/item.js
@@ -326,6 +326,12 @@ App.MainServiceItemView = Em.View.extend({
     return App.get('services.servicesWithHeatmapTab').contains(this.get('controller.content.serviceName'));
   }.property('controller.content.serviceName', 'App.services.servicesWithHeatmapTab'),
 
+  hasMetricTab: function() {
+    let serviceName = this.get('controller.content.serviceName');
+    let graphs = require('data/service_graph_config')[serviceName.toLowerCase()];
+    return graphs || App.StackService.find(serviceName).get('isServiceWithWidgets');
+  }.property('controller.content.serviceName'),
+
   didInsertElement: function () {
     this.get('controller').setStartStopState();
   },

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/test/controllers/main/service/info/metric_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/info/metric_test.js b/ambari-web/test/controllers/main/service/info/metric_test.js
new file mode 100644
index 0000000..5ef6279
--- /dev/null
+++ b/ambari-web/test/controllers/main/service/info/metric_test.js
@@ -0,0 +1,110 @@
+/**
+ * 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/service/info/metric');
+var testHelpers = require('test/helpers');
+function getController() {
+  return App.MainServiceInfoMetricsController.create();
+}
+
+describe('App.MainServiceInfoMetricsController', function () {
+
+  var controller;
+
+  beforeEach(function () {
+    controller = App.MainServiceInfoMetricsController.create();
+  });
+
+  App.TestAliases.testAsComputedOr(getController(), 'showTimeRangeControl', ['!isServiceWithEnhancedWidgets', 'someWidgetGraphExists']);
+
+
+  describe("#getActiveWidgetLayout() for Enhanced Dashboard", function () {
+
+    it("make GET call", function () {
+      controller.reopen({
+        isServiceWithEnhancedWidgets: true,
+        content: Em.Object.create({serviceName: 'HDFS'})
+      });
+      controller.getActiveWidgetLayout();
+      expect(testHelpers.findAjaxRequest('name', 'widgets.layouts.active.get')).to.exists;
+    });
+  });
+
+  describe("#getActiveWidgetLayoutSuccessCallback()", function () {
+    beforeEach(function () {
+      sinon.stub( App.widgetLayoutMapper, 'map');
+      sinon.stub( App.widgetMapper, 'map');
+    });
+    afterEach(function () {
+      App.widgetLayoutMapper.map.restore();
+      App.widgetMapper.map.restore();
+    });
+    it("isWidgetLayoutsLoaded should be set to true", function () {
+      controller.reopen({
+        isServiceWithEnhancedWidgets: true,
+        content: Em.Object.create({serviceName: 'HDFS'})
+      });
+      controller.getActiveWidgetLayoutSuccessCallback({items:[{
+        WidgetLayoutInfo: {}
+      }]});
+      expect(controller.get('isWidgetsLoaded')).to.be.true;
+    });
+
+  });
+
+  describe("#hideWidgetSuccessCallback()", function () {
+    beforeEach(function () {
+      sinon.stub(App.widgetLayoutMapper, 'map');
+      sinon.stub(controller, 'propertyDidChange');
+      var params = {
+        data: {
+          WidgetLayoutInfo: {
+            widgets: [
+              {id: 1}
+            ]
+          }
+        }
+      };
+      controller.hideWidgetSuccessCallback({}, {}, params);
+    });
+    afterEach(function () {
+      App.widgetLayoutMapper.map.restore();
+      controller.propertyDidChange.restore();
+    });
+    it("mapper is called with valid data", function () {
+      expect(App.widgetLayoutMapper.map.calledWith({
+        items: [{
+          WidgetLayoutInfo: {
+            widgets: [
+              {
+                WidgetInfo: {
+                  id: 1
+                }
+              }
+            ]
+          }
+        }]
+      })).to.be.true;
+    });
+    it('`widgets` is forced to be recalculated', function () {
+      expect(controller.propertyDidChange.calledWith('widgets')).to.be.true;
+    });
+  });
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/test/controllers/main/service/info/summary_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/info/summary_test.js b/ambari-web/test/controllers/main/service/info/summary_test.js
index 51dd595..e5cc32a 100644
--- a/ambari-web/test/controllers/main/service/info/summary_test.js
+++ b/ambari-web/test/controllers/main/service/info/summary_test.js
@@ -18,7 +18,6 @@
 
 var App = require('app');
 require('controllers/main/service/info/summary');
-var testHelpers = require('test/helpers');
 function getController() {
   return App.MainServiceInfoSummaryController.create();
 }
@@ -31,8 +30,6 @@ describe('App.MainServiceInfoSummaryController', function () {
     controller = App.MainServiceInfoSummaryController.create();
   });
 
-App.TestAliases.testAsComputedOr(getController(), 'showTimeRangeControl', ['!isServiceWithEnhancedWidgets', 'someWidgetGraphExists']);
-
   describe('#setRangerPlugins', function () {
 
     var cases = [
@@ -184,77 +181,4 @@ App.TestAliases.testAsComputedOr(getController(), 'showTimeRangeControl', ['!isS
 
   });
 
-  describe("#getActiveWidgetLayout() for Enhanced Dashboard", function () {
-
-    it("make GET call", function () {
-      var _controller = App.MainServiceInfoSummaryController.create({
-        isServiceWithEnhancedWidgets: true,
-        content: Em.Object.create({serviceName: 'HDFS'})
-      });
-      _controller.getActiveWidgetLayout();
-      expect(testHelpers.findAjaxRequest('name', 'widgets.layouts.active.get')).to.exists;
-    });
-  });
-
-  describe("#getActiveWidgetLayoutSuccessCallback()", function () {
-    beforeEach(function () {
-      sinon.stub( App.widgetLayoutMapper, 'map');
-      sinon.stub( App.widgetMapper, 'map');
-    });
-    afterEach(function () {
-      App.widgetLayoutMapper.map.restore();
-      App.widgetMapper.map.restore();
-    });
-    it("isWidgetLayoutsLoaded should be set to true", function () {
-      var _controller = App.MainServiceInfoSummaryController.create({
-        isServiceWithEnhancedWidgets: true,
-        content: Em.Object.create({serviceName: 'HDFS'})
-      });
-      _controller.getActiveWidgetLayoutSuccessCallback({items:[{
-        WidgetLayoutInfo: {}
-      }]});
-      expect(_controller.get('isWidgetsLoaded')).to.be.true;
-    });
-
-  });
-
-  describe("#hideWidgetSuccessCallback()", function () {
-    beforeEach(function () {
-      sinon.stub(App.widgetLayoutMapper, 'map');
-      sinon.stub(controller, 'propertyDidChange');
-      var params = {
-        data: {
-          WidgetLayoutInfo: {
-            widgets: [
-              {id: 1}
-            ]
-          }
-        }
-      };
-      controller.hideWidgetSuccessCallback({}, {}, params);
-    });
-    afterEach(function () {
-      App.widgetLayoutMapper.map.restore();
-      controller.propertyDidChange.restore();
-    });
-    it("mapper is called with valid data", function () {
-      expect(App.widgetLayoutMapper.map.calledWith({
-        items: [{
-          WidgetLayoutInfo: {
-            widgets: [
-              {
-                WidgetInfo: {
-                  id: 1
-                }
-              }
-            ]
-          }
-        }]
-      })).to.be.true;
-    });
-    it('`widgets` is forced to be recalculated', function () {
-      expect(controller.propertyDidChange.calledWith('widgets')).to.be.true;
-    });
-  });
-
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/6eb273e1/ambari-web/test/views/main/service/info/metrics_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/service/info/metrics_view_test.js b/ambari-web/test/views/main/service/info/metrics_view_test.js
new file mode 100644
index 0000000..916d451
--- /dev/null
+++ b/ambari-web/test/views/main/service/info/metrics_view_test.js
@@ -0,0 +1,334 @@
+/**
+ * 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('views/main/service/info/metrics_view');
+
+describe('App.MainServiceInfoMetricsView', function() {
+
+  var view = App.MainServiceInfoMetricsView.create({
+    controller: Em.Object.create({
+      content: Em.Object.create({
+        id: 'HDFS',
+        serviceName: 'HDFS',
+        hostComponents: []
+      }),
+      getActiveWidgetLayout: Em.K,
+      loadWidgetLayouts: Em.K
+    }),
+    service: Em.Object.create()
+  });
+
+  describe("#getServiceModel()", function() {
+
+    beforeEach(function() {
+      sinon.stub(App.Service, 'find').returns({serviceName: 'S1'});
+      sinon.stub(App.HDFSService, 'find').returns([{serviceName: 'HDFS'}]);
+    });
+    afterEach(function() {
+      App.Service.find.restore();
+      App.HDFSService.find.restore();
+    });
+
+    it("HDFS service", function() {
+      expect(view.getServiceModel('HDFS')).to.eql({serviceName: 'HDFS'});
+    });
+
+    it("Simple model service", function() {
+      expect(view.getServiceModel('S1')).to.eql({serviceName: 'S1'});
+    });
+  });
+
+  describe("#constructGraphObjects()", function() {
+    var mock = Em.Object.create({
+      isServiceWithWidgets: false
+    });
+
+    beforeEach(function() {
+      sinon.stub(App.StackService, 'find').returns(mock);
+      sinon.stub(view, 'getUserPref').returns({
+        complete: function(callback){callback();}
+      })
+    });
+    afterEach(function() {
+      App.StackService.find.restore();
+      view.getUserPref.restore();
+    });
+
+    it("metrics not loaded", function() {
+      mock.set('isServiceWithWidgets', false);
+      view.constructGraphObjects(null);
+      expect(view.get('serviceHasMetrics')).to.be.false;
+      expect(view.getUserPref.called).to.be.false;
+    });
+
+    it("metrics loaded", function() {
+      App.ChartServiceMetricsG1 = Em.Object.extend();
+      mock.set('isServiceWithWidgets', true);
+      view.constructGraphObjects(['G1']);
+      expect(view.get('serviceHasMetrics')).to.be.true;
+      expect(view.getUserPref.calledOnce).to.be.true;
+      expect(view.get('serviceMetricGraphs')).to.not.be.empty;
+    });
+  });
+
+  describe("#getUserPrefSuccessCallback()", function() {
+
+    it("currentTimeRangeIndex should be set", function() {
+      view.getUserPrefSuccessCallback(1);
+      expect(view.get('currentTimeRangeIndex')).to.equal(1);
+    });
+  });
+
+  describe("#getUserPrefErrorCallback()", function() {
+
+    beforeEach(function() {
+      sinon.stub(view, 'postUserPref');
+    });
+    afterEach(function() {
+      view.postUserPref.restore();
+    });
+
+    it("request.status = 404", function() {
+      view.getUserPrefErrorCallback({status: 404});
+      expect(view.get('currentTimeRangeIndex')).to.equal(0);
+      expect(view.postUserPref.calledOnce).to.be.true;
+    });
+
+    it("request.status = 403", function() {
+      view.getUserPrefErrorCallback({status: 403});
+      expect(view.postUserPref.called).to.be.false;
+    });
+  });
+
+  describe("#widgetActions", function() {
+
+    beforeEach(function() {
+      this.mock = sinon.stub(App, 'isAuthorized');
+      view.setProperties({
+        staticWidgetLayoutActions: [{id: 1}],
+        staticAdminPrivelegeWidgetActions: [{id: 2}],
+        staticGeneralWidgetActions: [{id: 3}]
+      });
+    });
+    afterEach(function() {
+      this.mock.restore();
+    });
+
+    it("not authorized", function() {
+      this.mock.returns(false);
+      view.propertyDidChange('widgetActions');
+      expect(view.get('widgetActions').mapProperty('id')).to.eql([3]);
+    });
+
+    it("is authorized", function() {
+      this.mock.returns(true);
+      App.supports.customizedWidgetLayout = true;
+      view.propertyDidChange('widgetActions');
+      expect(view.get('widgetActions').mapProperty('id')).to.eql([1, 2, 3]);
+    });
+  });
+
+  describe("#doWidgetAction()", function() {
+
+    beforeEach(function() {
+      view.set('controller.action1', Em.K);
+      sinon.stub(view.get('controller'), 'action1');
+    });
+    afterEach(function() {
+      view.get('controller').action1.restore();
+    });
+
+    it("action exist", function() {
+      view.doWidgetAction({context: 'action1'});
+      expect(view.get('controller').action1.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#setTimeRange", function() {
+
+    it("range = 0", function() {
+      var widget = Em.Object.create({
+        widgetType: 'GRAPH',
+        properties: {
+          time_range: '0'
+        }
+      });
+      view.set('controller.widgets', [widget]);
+      view.setTimeRange({context: {value: '0'}});
+      expect(widget.get('properties').time_range).to.be.equal('0')
+    });
+
+    it("range = 1", function() {
+      var widget = Em.Object.create({
+        widgetType: 'GRAPH',
+        properties: {
+          time_range: 0
+        }
+      });
+      view.set('controller.widgets', [widget]);
+      view.setTimeRange({context: {value: '1'}});
+      expect(widget.get('properties').time_range).to.be.equal('1')
+    });
+  });
+
+  describe("#makeSortable()", function() {
+    var mock = {
+      on: function(arg1, arg2, callback) {
+        callback();
+      },
+      off: Em.K,
+      sortable: function() {
+        return {
+          disableSelection: Em.K
+        }
+      }
+    };
+
+    beforeEach(function() {
+      sinon.stub(window, '$').returns(mock);
+      sinon.spy(mock, 'on');
+      sinon.spy(mock, 'off');
+      sinon.spy(mock, 'sortable');
+      view.makeSortable();
+    });
+    afterEach(function() {
+      window.$.restore();
+      mock.on.restore();
+      mock.off.restore();
+      mock.sortable.restore();
+    });
+
+    it("on() should be called", function() {
+      expect(mock.on.calledWith('DOMNodeInserted', '#widget_layout')).to.be.true;
+    });
+
+    it("sortable() should be called", function() {
+      expect(mock.sortable.calledOnce).to.be.true;
+    });
+
+    it("off() should be called", function() {
+      expect(mock.off.calledWith('DOMNodeInserted', '#widget_layout')).to.be.true;
+    });
+  });
+
+  describe('#didInsertElement', function () {
+
+    beforeEach(function () {
+      sinon.stub(view, 'constructGraphObjects', Em.K);
+      this.mock = sinon.stub(App, 'get');
+      sinon.stub(view, 'getServiceModel');
+      sinon.stub(view.get('controller'), 'getActiveWidgetLayout');
+      sinon.stub(view.get('controller'), 'loadWidgetLayouts');
+      sinon.stub(view, 'makeSortable');
+      sinon.stub(view, 'addWidgetTooltip');
+
+    });
+
+    afterEach(function () {
+      view.constructGraphObjects.restore();
+      this.mock.restore();
+      view.getServiceModel.restore();
+      view.get('controller').getActiveWidgetLayout.restore();
+      view.get('controller').loadWidgetLayouts.restore();
+      view.makeSortable.restore();
+      view.addWidgetTooltip.restore();
+    });
+
+    it("getServiceModel should be called", function() {
+      view.didInsertElement();
+      expect(view.getServiceModel.calledOnce).to.be.true;
+    });
+    it("addWidgetTooltip should be called", function() {
+      view.didInsertElement();
+      expect(view.addWidgetTooltip.calledOnce).to.be.true;
+    });
+    it("makeSortable should be called", function() {
+      view.didInsertElement();
+      expect(view.makeSortable.calledOnce).to.be.true;
+    });
+    it("getActiveWidgetLayout should be called", function() {
+      view.didInsertElement();
+      expect(view.get('controller').getActiveWidgetLayout.calledOnce).to.be.true;
+    });
+
+    describe("serviceName is null, metrics not supported, widgets not supported", function() {
+      beforeEach(function () {
+        view.set('controller.content.serviceName', null);
+        this.mock.returns(false);
+        view.didInsertElement();
+      });
+
+      it("loadWidgetLayouts should not be called", function() {
+        expect(view.get('controller').loadWidgetLayouts.called).to.be.false;
+      });
+      it("constructGraphObjects should not be called", function() {
+        expect(view.constructGraphObjects.called).to.be.false;
+      });
+    });
+
+    describe("serviceName is set, metrics is supported, widgets is supported", function() {
+      beforeEach(function () {
+        view.set('controller.content.serviceName', 'S1');
+        this.mock.returns(true);
+        view.didInsertElement();
+      });
+
+      it("loadWidgetLayouts should be called", function() {
+        expect(view.get('controller').loadWidgetLayouts.calledOnce).to.be.true;
+      });
+      it("constructGraphObjects should be called", function() {
+        expect(view.constructGraphObjects.calledOnce).to.be.true;
+      });
+    });
+  });
+
+  describe("#addWidgetTooltip()", function() {
+    var mock = {
+      hoverIntent: Em.K
+    };
+
+    beforeEach(function() {
+      sinon.stub(Em.run, 'later', function(arg1, callback) {
+        callback();
+      });
+      sinon.stub(App, 'tooltip');
+      sinon.stub(window, '$').returns(mock);
+      sinon.spy(mock, 'hoverIntent');
+      view.addWidgetTooltip();
+    });
+    afterEach(function() {
+      Em.run.later.restore();
+      App.tooltip.restore();
+      window.$.restore();
+      mock.hoverIntent.restore();
+    });
+
+    it("Em.run.later should be called", function() {
+      expect(Em.run.later.calledOnce).to.be.true;
+    });
+    it("App.tooltip should be called", function() {
+      expect(App.tooltip.calledOnce).to.be.true;
+    });
+    it("hoverIntent should be called", function() {
+      expect(mock.hoverIntent.calledOnce).to.be.true;
+    });
+  });
+
+});
\ No newline at end of file