This is an automated email from the ASF dual-hosted git repository.

ababiichuk pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 1f8c873  AMBARI-23271 Make HDFS widgets namespace-scoped. (ababiichuk)
1f8c873 is described below

commit 1f8c8737ce2528a49632a9fa76ad82324d830a68
Author: ababiichuk <[email protected]>
AuthorDate: Fri Mar 16 21:34:10 2018 +0200

    AMBARI-23271 Make HDFS widgets namespace-scoped. (ababiichuk)
---
 ambari-web/app/app.js                              |   2 +-
 .../app/controllers/global/update_controller.js    |  76 +++----
 ambari-web/app/controllers/main/service/item.js    |   2 +-
 ambari-web/app/data/dashboard_widgets.js           |  18 +-
 ambari-web/app/mappers/service_metrics_mapper.js   |  93 +++++----
 ambari-web/app/messages.js                         |   1 +
 ambari-web/app/mixins.js                           |   1 +
 .../main/dashboard/widgets/namenode_widget.js}     |  13 +-
 ambari-web/app/models/host_component.js            |   4 +-
 ambari-web/app/models/service/hdfs.js              |  81 +++++--
 ambari-web/app/styles/dashboard.less               |   9 +
 .../app/templates/main/dashboard/widgets.hbs       |  43 +++-
 .../main/dashboard/widgets/hdfs_links.hbs          |   9 +-
 .../main/service/info/summary/hdfs/widgets.hbs     | 116 +++++++++++
 .../service/info/summary/master_components.hbs     |   3 +
 .../app/templates/main/service/services/hdfs.hbs   | 104 +--------
 ambari-web/app/views.js                            |   1 +
 ambari-web/app/views/common/chart/linear_time.js   |   7 -
 .../app/views/common/quick_view_link_view.js       |   4 +-
 ambari-web/app/views/main/dashboard/widgets.js     | 232 ++++++++++++++++++---
 .../main/dashboard/widgets/hbase_master_heap.js    |   9 +-
 .../main/dashboard/widgets/hbase_master_uptime.js  |   2 +-
 .../views/main/dashboard/widgets/hdfs_capacity.js  |  22 +-
 .../app/views/main/dashboard/widgets/hdfs_links.js |  21 +-
 .../views/main/dashboard/widgets/namenode_cpu.js   |  27 ++-
 .../views/main/dashboard/widgets/namenode_heap.js  |  12 +-
 .../views/main/dashboard/widgets/namenode_rpc.js   |  14 +-
 .../main/dashboard/widgets/namenode_uptime.js      |   4 +-
 .../main/dashboard/widgets/pie_chart_widget.js     |  14 +-
 .../dashboard/widgets/resource_manager_heap.js     |   8 +-
 .../dashboard/widgets/resource_manager_uptime.js   |   2 +-
 .../main/dashboard/widgets/uptime_text_widget.js   |   9 +-
 .../views/main/dashboard/widgets/yarn_memory.js    |   4 +-
 ambari-web/app/views/main/service/info/summary.js  |   5 +-
 .../main/service/info/summary/hdfs/widgets.js      | 164 +++++++++++++++
 ambari-web/app/views/main/service/services/hdfs.js | 119 -----------
 .../test/views/common/chart/linear_time_test.js    |   4 -
 .../test/views/main/dashboard/widget_test.js       |   3 +-
 .../main/dashboard/widgets/namenode_rpc_test.js    |  21 +-
 .../dashboard/widgets/pie_chart_widget_test.js     |   4 +-
 .../dashboard/widgets/uptime_text_widget_test.js   |   2 +-
 .../test/views/main/dashboard/widgets_test.js      |  17 +-
 .../test/views/main/service/services/hdfs_test.js  | 171 ---------------
 43 files changed, 852 insertions(+), 625 deletions(-)

diff --git a/ambari-web/app/app.js b/ambari-web/app/app.js
index 0acd1f9..2c6a67e 100644
--- a/ambari-web/app/app.js
+++ b/ambari-web/app/app.js
@@ -244,7 +244,7 @@ module.exports = Em.Application.create({
 
   hasNameNodeFederation: function () {
     return 
App.HDFSService.find().objectAt(0).get('masterComponentGroups.length') > 1;
-  }.property('router.clusterController.isHDFSNameSpacesLoaded'),
+  }.property('router.clusterController.isHostComponentMetricsLoaded', 
'router.clusterController.isHDFSNameSpacesLoaded'),
 
   /**
    * If ResourceManager High Availability is enabled
diff --git a/ambari-web/app/controllers/global/update_controller.js 
b/ambari-web/app/controllers/global/update_controller.js
index 4360a5e..40fadc1 100644
--- a/ambari-web/app/controllers/global/update_controller.js
+++ b/ambari-web/app/controllers/global/update_controller.js
@@ -685,43 +685,47 @@ App.UpdateController = Em.Controller.extend({
   },
 
   updateHDFSNameSpaces: function () {
-    const siteName = 'hdfs-site',
-      storedHdfsSiteconfigs = App.db.getConfigs().findProperty('type', 
siteName),
-      tagName = storedHdfsSiteconfigs && storedHdfsSiteconfigs.tag;
-    App.router.get('configurationController').getConfigsByTags([{
-      siteName,
-      tagName
-    }]).done(configs => {
-      const properties = configs && configs[0] && configs[0].properties;
-      if (properties) {
-        const nameSpaceProperty = properties['dfs.nameservices'];
-        if (nameSpaceProperty) {
-          const nameSpaces = nameSpaceProperty.split(',').map(nameSpace => {
-              const nameNodeIdsProperty = 
properties[`dfs.ha.namenodes.${nameSpace}`];
-              if (nameNodeIdsProperty) {
-                const nameNodeIds = nameNodeIdsProperty.split(','),
-                  hostNames = nameNodeIds.map(id => {
-                    const propertyValue = 
properties[`dfs.namenode.http-address.${nameSpace}.${id}`],
-                      matches = propertyValue && 
propertyValue.match(/([\D\d]+)\:\d+$/),
-                      hostName = matches && matches[1];
-                    return hostName;
-                  });
-                return {
-                  nameSpace,
-                  hostNames
-                };
+    if (App.Service.find().someProperty('serviceName', 'HDFS')) {
+      const siteName = 'hdfs-site',
+        storedHdfsSiteconfigs = App.db.getConfigs().findProperty('type', 
siteName),
+        tagName = storedHdfsSiteconfigs && storedHdfsSiteconfigs.tag;
+      App.router.get('configurationController').getConfigsByTags([{
+        siteName,
+        tagName
+      }]).done(configs => {
+        const properties = configs && configs[0] && configs[0].properties;
+        if (properties) {
+          const nameSpaceProperty = properties['dfs.nameservices'];
+          if (nameSpaceProperty) {
+            const nameSpaces = nameSpaceProperty.split(',').map(nameSpace => {
+                const nameNodeIdsProperty = 
properties[`dfs.ha.namenodes.${nameSpace}`];
+                if (nameNodeIdsProperty) {
+                  const nameNodeIds = nameNodeIdsProperty.split(','),
+                    hostNames = nameNodeIds.map(id => {
+                      const propertyValue = 
properties[`dfs.namenode.http-address.${nameSpace}.${id}`],
+                        matches = propertyValue && 
propertyValue.match(/([\D\d]+)\:\d+$/),
+                        hostName = matches && matches[1];
+                      return hostName;
+                    });
+                  return {
+                    nameSpace,
+                    hostNames
+                  };
+                }
+              }),
+              allNameNodes = 
App.HDFSService.find().objectAt(0).get('hostComponents').filterProperty('componentName',
 'NAMENODE');
+            allNameNodes.forEach(component => {
+              const nameSpaceObject = nameSpaces.find(ns => ns && ns.hostNames 
&& ns.hostNames.contains(component.get('hostName')));
+              if (nameSpaceObject) {
+                component.set('haNameSpace', nameSpaceObject.nameSpace);
               }
-            }),
-            allNameNodes = 
App.HDFSService.find().objectAt(0).get('hostComponents').filterProperty('componentName',
 'NAMENODE');
-          allNameNodes.forEach(component => {
-            const nameSpaceObject = nameSpaces.find(ns => ns && ns.hostNames 
&& ns.hostNames.contains(component.get('hostName')));
-            if (nameSpaceObject) {
-              component.set('haNameSpace', nameSpaceObject.nameSpace);
-            }
-          });
-          App.set('router.clusterController.isHDFSNameSpacesLoaded', true);
+            });
+            App.set('router.clusterController.isHDFSNameSpacesLoaded', true);
+          }
         }
-      }
-    })
+      })
+    } else {
+      App.set('router.clusterController.isHDFSNameSpacesLoaded', true);
+    }
   }
 });
diff --git a/ambari-web/app/controllers/main/service/item.js 
b/ambari-web/app/controllers/main/service/item.js
index da34e1a..38e8ee9 100644
--- a/ambari-web/app/controllers/main/service/item.js
+++ b/ambari-web/app/controllers/main/service/item.js
@@ -383,7 +383,7 @@ App.MainServiceItemController = 
Em.Controller.extend(App.SupportClientConfigsDow
         this.getHdfsUser().done(() => {
           const maxAge = App.nnCheckpointAgeAlertThreshold,
             hdfsUser = this.get('hdfsUser'),
-            confirmButton =Em.I18n.t('common.next');
+            confirmButton = Em.I18n.t('common.next');
           let msg;
           if (App.get('hasNameNodeFederation') && !groupName) {
             const oldCheckpointNameSpacesList = 
nameNodesWithOldCheckpoints.map(nn => `<li>${nn.haNameSpace}</li>`),
diff --git a/ambari-web/app/data/dashboard_widgets.js 
b/ambari-web/app/data/dashboard_widgets.js
index d58b0e2..32d1955 100644
--- a/ambari-web/app/data/dashboard_widgets.js
+++ b/ambari-web/app/data/dashboard_widgets.js
@@ -22,21 +22,24 @@ module.exports = [
     viewName: 'NameNodeHeapPieChartView',
     sourceName: 'HDFS',
     title: Em.I18n.t('dashboard.widgets.NameNodeHeap'),
-    threshold: [80, 90]
+    threshold: [80, 90],
+    groupName: 'nn'
   },
   {
     id: 2,
     viewName: 'NameNodeCapacityPieChartView',
     sourceName: 'HDFS',
     title: Em.I18n.t('dashboard.widgets.HDFSDiskUsage'),
-    threshold: [85, 95]
+    threshold: [85, 95],
+    groupName: 'nn'
   },
   {
     id: 3,
     viewName: 'NameNodeCpuPieChartView',
     sourceName: 'HDFS',
     title: Em.I18n.t('dashboard.widgets.NameNodeCpu'),
-    threshold: [90, 95]
+    threshold: [90, 95],
+    groupName: 'nn'
   },
   {
     id: 4,
@@ -50,7 +53,8 @@ module.exports = [
     viewName: 'NameNodeRpcView',
     sourceName: 'HDFS',
     title: Em.I18n.t('dashboard.widgets.NameNodeRpc'),
-    threshold: [1000, 3000]
+    threshold: [1000, 3000],
+    groupName: 'nn'
   },
   {
     id: 6,
@@ -85,14 +89,16 @@ module.exports = [
     viewName: 'NameNodeUptimeView',
     sourceName: 'HDFS',
     title: Em.I18n.t('dashboard.widgets.NameNodeUptime'),
-    threshold: []
+    threshold: [],
+    groupName: 'nn'
   },
   {
     id: 11,
     viewName: 'HDFSLinksView',
     sourceName: 'HDFS',
     title: Em.I18n.t('dashboard.widgets.HDFSLinks'),
-    threshold: []
+    threshold: [],
+    groupName: 'nn'
   },
   {
     id: 12,
diff --git a/ambari-web/app/mappers/service_metrics_mapper.js 
b/ambari-web/app/mappers/service_metrics_mapper.js
index 64d9de1..ddf03f2 100644
--- a/ambari-web/app/mappers/service_metrics_mapper.js
+++ b/ambari-web/app/mappers/service_metrics_mapper.js
@@ -41,34 +41,33 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
   },
   hdfsConfig: {
     version: 
'nameNodeComponent.host_components[0].metrics.dfs.namenode.Version',
+    // TODO renove active_name_node_id after activeNameNode property becomes 
unused
     active_name_node_id: 'active_name_node_id',
-    standby_name_node_id: 'standby_name_node_id',
-    standby_name_node2_id: 'standby_name_node2_id',
     active_name_nodes: 'active_name_nodes',
     standby_name_nodes: 'standby_name_nodes',
     journal_nodes: 'journal_nodes',
     name_node_id: 'name_node_id',
     sname_node_id: 'sname_node_id',
     metrics_not_available: 'metrics_not_available',
-    name_node_start_time: 
'nameNodeComponent.host_components[0].metrics.runtime.StartTime',
-    jvm_memory_heap_used: 
'nameNodeComponent.host_components[0].metrics.jvm.HeapMemoryUsed',
-    jvm_memory_heap_max: 
'nameNodeComponent.host_components[0].metrics.jvm.HeapMemoryMax',
+    name_node_start_time_values: 'name_node_start_time_values',
+    jvm_memory_heap_used_values: 'jvm_memory_heap_used_values',
+    jvm_memory_heap_max_values: 'jvm_memory_heap_max_values',
     live_data_nodes: 'live_data_nodes',
     dead_data_nodes: 'dead_data_nodes',
     decommission_data_nodes: 'decommission_data_nodes',
-    capacity_used: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.CapacityUsed',
-    capacity_total: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.CapacityTotal',
-    capacity_remaining: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.CapacityRemaining',
-    capacity_non_dfs_used: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.CapacityNonDFSUsed',
-    dfs_total_blocks: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.BlocksTotal',
-    dfs_corrupt_blocks: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.CorruptBlocks',
-    dfs_missing_blocks: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.MissingBlocks',
-    dfs_under_replicated_blocks: 
'nameNodeComponent.host_components[0].metrics.dfs.FSNamesystem.UnderReplicatedBlocks',
-    dfs_total_files: 
'nameNodeComponent.host_components[0].metrics.dfs.namenode.TotalFiles',
-    upgrade_status: 
'nameNodeComponent.host_components[0].metrics.dfs.namenode.UpgradeFinalized',
-    safe_mode_status: 
'nameNodeComponent.host_components[0].metrics.dfs.namenode.Safemode',
-    name_node_cpu: 'nameNodeComponent.host_components[0].metrics.cpu.cpu_wio',
-    name_node_rpc: 
'nameNodeComponent.host_components[0].metrics.rpc.client.RpcQueueTime_avg_time',
+    capacity_used_values: 'capacity_used_values',
+    capacity_total_values: 'capacity_total_values',
+    capacity_remaining_values: 'capacity_remaining_values',
+    capacity_non_dfs_used_values: 'capacity_non_dfs_used_values',
+    dfs_total_blocks_values: 'dfs_total_blocks_values',
+    dfs_corrupt_blocks_values: 'dfs_corrupt_blocks_values',
+    dfs_missing_blocks_values: 'dfs_missing_blocks_values',
+    dfs_under_replicated_blocks_values: 'dfs_under_replicated_blocks_values',
+    dfs_total_files_values: 'dfs_total_files_values',
+    work_status_values: 'work_status_values',
+    upgrade_status_values: 'upgrade_status_values',
+    safe_mode_status_values: 'safe_mode_status_values',
+    name_node_rpc_values: 'name_node_rpc_values',
     data_nodes_started: 'data_nodes_started',
     data_nodes_installed: 'data_nodes_installed',
     data_nodes_total: 'data_nodes_total',
@@ -76,6 +75,24 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
     nfs_gateways_installed: 'nfs_gateways_installed',
     nfs_gateways_total: 'nfs_gateways_total'
   },
+  activeNameNodeConfig: {
+    name_node_start_time_values: 'metrics.runtime.StartTime',
+    jvm_memory_heap_used_values: 'metrics.jvm.HeapMemoryUsed',
+    capacity_used_values: 'metrics.dfs.FSNamesystem.CapacityUsed',
+    capacity_total_values: 'metrics.dfs.FSNamesystem.CapacityTotal',
+    capacity_remaining_values: 'metrics.dfs.FSNamesystem.CapacityRemaining',
+    capacity_non_dfs_used_values: 
'metrics.dfs.FSNamesystem.CapacityNonDFSUsed',
+    jvm_memory_heap_max_values: 'metrics.jvm.HeapMemoryMax',
+    dfs_total_blocks_values: 'metrics.dfs.FSNamesystem.BlocksTotal',
+    dfs_corrupt_blocks_values: 'metrics.dfs.FSNamesystem.CorruptBlocks',
+    dfs_missing_blocks_values: 'metrics.dfs.FSNamesystem.MissingBlocks',
+    dfs_under_replicated_blocks_values: 
'metrics.dfs.FSNamesystem.UnderReplicatedBlocks',
+    dfs_total_files_values: 'metrics.dfs.namenode.TotalFiles',
+    work_status_values: 'HostRoles.state',
+    upgrade_status_values: 'metrics.dfs.namenode.UpgradeFinalized',
+    safe_mode_status_values: 'metrics.dfs.namenode.Safemode',
+    name_node_rpc_values: 'metrics.rpc.client.RpcQueueTime_avg_time',
+  },
   onefsConfig: {
     metrics_not_available: 'metrics_not_available',
     name_node_start_time: 'metrics.runtime.StartTime',
@@ -476,7 +493,13 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
   hdfsMapper: function (item) {
     let finalConfig = jQuery.extend({}, this.config);
     // Change the JSON so that it is easy to map
-    const hdfsConfig = this.hdfsConfig;
+    const hdfsConfig = this.hdfsConfig,
+      activeNameNodeConfig = this.activeNameNodeConfig,
+      activeNameNodeConfigKeys = Object.keys(activeNameNodeConfig),
+      activeNameNodeConfigInitial = activeNameNodeConfigKeys.reduce((obj, key) 
=> Object.assign({}, obj, {
+        [key]: {}
+      }), {});
+    Object.assign(item, activeNameNodeConfigInitial);
     item.components.forEach(component => {
       const hostComponents = component.host_components,
         firstHostComponent = hostComponents[0];
@@ -507,15 +530,24 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
                 });
                 break;
             }
+            activeNameNodeConfigKeys.forEach(key => {
+              if (clusterIdValue && (!item[key][clusterIdValue] || haState === 
'active')) {
+                item[key][clusterIdValue] = Em.get(hc, 
activeNameNodeConfig[key]);
+              }
+            });
           });
           unknownNameNodes.forEach(nameNode => {
             if 
(nameSpacesWithActiveNameNodes.contains(nameNode.clusterIdValue)) {
               item.standby_name_nodes.push(`NAMENODE_${nameNode.hostName}`);
             }
           });
+        } else {
+          activeNameNodeConfigKeys.forEach(key => {
+            item[key].default = Em.get(firstHostComponent, 
activeNameNodeConfigKeys[key]);
+          });
         }
 
-        // TODO remove after implementing widgets changes
+        // TODO remove after activeNameNode property becomes unused
         if (hostComponents.length === 2) {
           const haState1 = Em.get(firstHostComponent, 
'metrics.dfs.FSNamesystem.HAState'),
             haState2 = Em.get(hostComponents[1], 
'metrics.dfs.FSNamesystem.HAState'),
@@ -541,28 +573,11 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
               break;
           }
           item.active_name_node_id = null;
-          item.standby_name_node_id = null;
-          item.standby_name_node2_id = null;
           switch (active_name_node.length) {
             case 1:
               item.active_name_node_id = `NAMENODE_${active_name_node[0]}`;
               break;
           }
-          switch (standby_name_nodes.length) {
-            case 0:
-              if (active_name_node.length === 1) {
-                const standbyNameNode =  (active_name_node[0] === 
namenodeName1) ? namenodeName2 : namenodeName1;
-                item.standby_name_node_id = `NAMENODE_${standbyNameNode}`;
-              }
-              break;
-            case 1:
-              item.standby_name_node_id = `NAMENODE_${standby_name_nodes[0]}`;
-              break;
-            case 2:
-              item.standby_name_node_id = `NAMENODE_${standby_name_nodes[0]}`;
-              item.standby_name_node2_id = `NAMENODE_${standby_name_nodes[1]}`;
-              break;
-          }
           var activeHostComponentIndex = haState2 == "active" ? 1 : 0;
           this.setActiveAsFirstHostComponent(component, 
activeHostComponentIndex);
         }
@@ -592,10 +607,10 @@ App.serviceMetricsMapper = App.QuickDataMapper.create({
         }
         item.name_node_id = 
`NAMENODE_${firstHostComponent.HostRoles.host_name}`;
       }
-      if (this.isHostComponentPresent(component, "JOURNALNODE")) {
+      if (this.isHostComponentPresent(component, 'JOURNALNODE')) {
         item.journal_nodes = hostComponents.map(hc => 
`JOURNALNODE_${hc.HostRoles.host_name}`);
       }
-      if (this.isHostComponentPresent(component, "SECONDARY_NAMENODE")) {
+      if (this.isHostComponentPresent(component, 'SECONDARY_NAMENODE')) {
         item.sname_node_id = 
`SECONDARY_NAMENODE_${firstHostComponent.HostRoles.host_name}`;
       }
     });
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 188972b..17420e0 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -3005,6 +3005,7 @@ Em.I18n.translations = {
   'dashboard.widgets.HawqSegmentUp': 'HAWQ Segments Live',
   'dashboard.widgets.PxfUp': 'PXF Agents Live',
   'dashboard.widgets.PXFAgents': 'PXF Agents',
+  'dashboard.widgets.nameSpace': 'Namespace',
 
   'dashboard': {
     'widgets': {
diff --git a/ambari-web/app/mixins.js b/ambari-web/app/mixins.js
index 06c69f7..ae1cb4a 100644
--- a/ambari-web/app/mixins.js
+++ b/ambari-web/app/mixins.js
@@ -32,6 +32,7 @@ require('mixins/common/track_request_mixin');
 require('mixins/common/loading_overlay_support');
 require('mixins/main/dashboard/widgets/editable');
 require('mixins/main/dashboard/widgets/editable_with_limit');
+require('mixins/main/dashboard/widgets/namenode_widget');
 require('mixins/main/dashboard/widgets/single_numeric_threshold');
 require('mixins/main/host/details/host_components/decommissionable');
 require('mixins/main/host/details/host_components/install_component');
diff --git 
a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js 
b/ambari-web/app/mixins/main/dashboard/widgets/namenode_widget.js
similarity index 76%
copy from ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
copy to ambari-web/app/mixins/main/dashboard/widgets/namenode_widget.js
index a9da7a8..1e82d32 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
+++ b/ambari-web/app/mixins/main/dashboard/widgets/namenode_widget.js
@@ -16,11 +16,14 @@
  * limitations under the License.
  */
 
-var App = require('app');
+const App = require('app');
 
-App.ResourceManagerUptimeView = App.UptimeTextDashboardWidgetView.extend({
+App.NameNodeWidgetMixin = Em.Mixin.create({
 
-  component: 'ResourceManager',
-  modelField: 'resourceManagerStartTime'
+  subGroupId: 'default',
 
-});
+  componentGroup: Em.computed.findByKey('model.masterComponentGroups', 'name', 
'subGroupId'),
+
+  clusterId: Em.computed.alias('componentGroup.clusterId')
+
+});
\ No newline at end of file
diff --git a/ambari-web/app/models/host_component.js 
b/ambari-web/app/models/host_component.js
index 0ee1cde..9e01c4d 100644
--- a/ambari-web/app/models/host_component.js
+++ b/ambari-web/app/models/host_component.js
@@ -32,7 +32,9 @@ App.HostComponent = DS.Model.extend({
   publicHostName: DS.attr('string'),
   service: DS.belongsTo('App.Service'),
   adminState: DS.attr('string'),
-  haNameSpace: DS.attr('string'),
+  haNameSpace: DS.attr('string', {
+    defaultValue: 'default'
+  }),
   clusterIdValue: DS.attr('string'),
 
   serviceDisplayName: Em.computed.truncate('service.displayName', 14, 11),
diff --git a/ambari-web/app/models/service/hdfs.js 
b/ambari-web/app/models/service/hdfs.js
index 08e160b..ba5ee96 100644
--- a/ambari-web/app/models/service/hdfs.js
+++ b/ambari-web/app/models/service/hdfs.js
@@ -24,8 +24,6 @@ App.HDFSService = App.Service.extend({
 
   // TODO remove after implementing widgets changes
   activeNameNode: DS.belongsTo('App.HostComponent'),
-  standbyNameNode: DS.belongsTo('App.HostComponent'),
-  standbyNameNode2: DS.belongsTo('App.HostComponent'),
 
   activeNameNodes: DS.hasMany('App.HostComponent', {
     defaultValue: []
@@ -43,37 +41,78 @@ App.HDFSService = App.Service.extend({
   nfsGatewaysInstalled: DS.attr('number', {defaultValue: 0}),
   nfsGatewaysTotal: DS.attr('number', {defaultValue: 0}),
   journalNodes: DS.hasMany('App.HostComponent'),
-  nameNodeStartTime: DS.attr('number'),
-  jvmMemoryHeapUsed: DS.attr('number'),
-  jvmMemoryHeapMax: DS.attr('number'),
+  nameNodeStartTimeValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  jvmMemoryHeapUsedValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  jvmMemoryHeapMaxValues: DS.attr('object', {
+    defaultValue: {}
+  }),
   decommissionDataNodes: DS.hasMany('App.HostComponent'),
   liveDataNodes: DS.hasMany('App.HostComponent'),
   deadDataNodes: DS.hasMany('App.HostComponent'),
-  capacityUsed: DS.attr('number'),
-  capacityTotal: DS.attr('number'),
-  capacityRemaining: DS.attr('number'),
-  capacityNonDfsUsed: DS.attr('number'),
-  dfsTotalBlocks: DS.attr('number'),
-  dfsCorruptBlocks: DS.attr('number'),
-  dfsMissingBlocks: DS.attr('number'),
-  dfsUnderReplicatedBlocks: DS.attr('number'),
-  dfsTotalFiles: DS.attr('number'),
-  upgradeStatus: DS.attr('string'),
-  safeModeStatus: DS.attr('string'),
-  nameNodeRpc: DS.attr('number'),
+  capacityUsedValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  capacityTotalValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  capacityRemainingValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  capacityNonDfsUsedValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  dfsTotalBlocksValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  dfsCorruptBlocksValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  dfsMissingBlocksValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  dfsUnderReplicatedBlocksValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  dfsTotalFilesValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  workStatusValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  healthStatusValues: function () {
+    const workStatusValues = Object.keys(this.get('workStatusValues'));
+    return workStatusValues.reduce((acc, key) => Object.assign({}, acc, {
+      [key]: this.get('healthStatusMap')[workStatusValues[key]] || 'yellow'
+    }), {});
+  }.property('workStatusValues'),
+  upgradeStatusValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  safeModeStatusValues: DS.attr('object', {
+    defaultValue: {}
+  }),
+  nameNodeRpcValues: DS.attr('object', {
+    defaultValue: {}
+  }),
   metricsNotAvailable: DS.attr('boolean'),
   masterComponentGroups: function () {
     let result = [];
     this.get('hostComponents').forEach(component => {
-      const nameSpace = component.get('haNameSpace');
-      if (nameSpace) {
-        const hostName = component.get('hostName'),
+      if (component.get('componentName') === 'NAMENODE') {
+        const nameSpace = component.get('haNameSpace'),
+          hostName = component.get('hostName'),
+          clusterId = component.get('clusterIdValue'),
           existingNameSpace = result.findProperty('name', nameSpace),
           currentNameSpace = existingNameSpace || {
               name: nameSpace,
               title: nameSpace,
               hosts: [],
-              components: ['NAMENODE', 'ZKFC']
+              components: ['NAMENODE', 'ZKFC'],
+              clusterId
             };
         if (!existingNameSpace) {
           result.push(currentNameSpace);
diff --git a/ambari-web/app/styles/dashboard.less 
b/ambari-web/app/styles/dashboard.less
index c087ffa..dfede73 100644
--- a/ambari-web/app/styles/dashboard.less
+++ b/ambari-web/app/styles/dashboard.less
@@ -30,6 +30,10 @@
     display: inline-block;
     padding: 10px 1.1% 10px 1.1%;
     background-color: #ffffff;
+    .dashboard-widget-groups-container {
+      float: left;
+      width: 100%;
+    }
   }
   #widgets-options-menu {
     .add-widgets-text.pull-left .dropdown-menu {
@@ -351,6 +355,11 @@
       }
     }
   }
+
+  .widgets-group-title {
+    display: inline-block;
+    text-transform: uppercase;
+  }
 }
 
 @media (min-width: 1200px) {
diff --git a/ambari-web/app/templates/main/dashboard/widgets.hbs 
b/ambari-web/app/templates/main/dashboard/widgets.hbs
index a4625c7..89adaf9 100644
--- a/ambari-web/app/templates/main/dashboard/widgets.hbs
+++ b/ambari-web/app/templates/main/dashboard/widgets.hbs
@@ -45,7 +45,7 @@
   </div>
 
   <div class="dashboard-widgets-box">
-    <div id="dashboard-widgets"  class="widgets-container">
+    <div id="dashboard-widgets" class="widgets-container">
       <div class="thumbnails" id="sortable">
         {{#each widget in view.allWidgets}}
           {{#if widget.isVisible}}
@@ -53,6 +53,47 @@
           {{/if}}
         {{/each}}
       </div>
+      {{#if view.widgetGroups.length}}
+        {{#each group in view.widgetGroups}}
+          <div class="dashboard-widget-groups-container">
+            <div class="col-md-12">
+              <h5 class="widgets-group-title">{{group.title}}</h5>
+              {{#if group.subGroups.length}}
+                <div class="btn-group">
+                  <button class="btn btn-default dropdown-toggle" 
data-toggle="dropdown">
+                    {{#if group.activeSubGroup}}
+                      {{group.activeSubGroup.title}}
+                    {{else}}
+                      {{t common.all}}
+                    {{/if}}
+                    <span class="caret"></span>
+                  </button>
+                  <ul class="dropdown-menu">
+                    {{#each item in group.subGroups}}
+                      <li>
+                        <a href="#" {{action setActiveSubGroup group.subGroups 
item.name target="view"}}>
+                          {{item.title}}
+                        </a>
+                      </li>
+                    {{/each}}
+                  </ul>
+                </div>
+              {{/if}}
+            </div>
+            <div class="thumbnails">
+              {{#each groupLayout in group.allWidgets}}
+                {{#if groupLayout.isActive}}
+                  {{#each widget in groupLayout.widgets}}
+                    {{#if widget.isVisible}}
+                      {{view widget.viewClass widgetBinding="widget"}}
+                    {{/if}}
+                  {{/each}}
+                {{/if}}
+              {{/each}}
+            </div>
+          </div>
+        {{/each}}
+      {{/if}}
     </div>
   </div>
 {{else}}
diff --git a/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs 
b/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
index 54f92f7..4b0669b 100644
--- a/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
+++ b/ambari-web/app/templates/main/dashboard/widgets/hdfs_links.hbs
@@ -30,12 +30,12 @@
       </ul>
     </div>
     <div class="widget-content" >
-      {{#if view.isHAEnabled }}
+      {{#if view.isHAEnabled}}
         <table>
           <!--Active NameNode-->
           <tr class="active-namenode-link">
             {{#if view.isActiveNNValid}}
-              <td><a href="#" {{action showDetails 
view.model.activeNameNode.host}}>{{t 
dashboard.widgets.HDFSLinks.activeNameNode}}</a></td>
+              <td><a href="#" {{action showDetails 
view.activeNameNode.host}}>{{t 
dashboard.widgets.HDFSLinks.activeNameNode}}</a></td>
             {{else}}
               <td><a class="disabled-hdfs-link">{{t 
dashboard.widgets.HDFSLinks.activeNameNode}}</a></td>
             {{/if}}
@@ -48,7 +48,7 @@
                 <td><a href="#" {{action filterHosts 
view.twoStandbyComponent}}>{{t 
dashboard.widgets.HDFSLinks.standbyNameNodes}}</a></td>
               {{else}}
                 <!--One Standby NameNode-->
-                <td><a href="#" {{action showDetails 
view.model.standbyNameNode.host}}>{{t 
dashboard.widgets.HDFSLinks.standbyNameNode}}</a></td>
+                <td><a href="#" {{action showDetails 
view.standbyNameNodes.firstObject.host}}>{{t 
dashboard.widgets.HDFSLinks.standbyNameNode}}</a></td>
               {{/if}}
             {{else}}
               <td><a class="disabled-hdfs-link">{{t 
dashboard.widgets.HDFSLinks.standbyNameNode}}</a></td>
@@ -84,7 +84,8 @@
 
     <div class="link-button">
       {{#if view.model.quickLinks.length}}
-        {{#view App.QuickLinksView contentBinding="view.model"}}
+        {{#view App.QuickLinksView contentBinding="view.model" 
masterGroupsBinding="view.masterGroupsArray"
+          shouldSetGroupedLinks=true}}
           <div class="btn-group">
             <a class="dropdown-toggle" data-toggle="dropdown" href="#">
               {{t common.more}}
diff --git 
a/ambari-web/app/templates/main/service/info/summary/hdfs/widgets.hbs 
b/ambari-web/app/templates/main/service/info/summary/hdfs/widgets.hbs
new file mode 100644
index 0000000..cd17840
--- /dev/null
+++ b/ambari-web/app/templates/main/service/info/summary/hdfs/widgets.hbs
@@ -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.
+}}
+
+<div class="col-md-12">
+  <div class="col-md-10 col-md-offset-2">
+    {{! NameNode Uptime }}
+    <div class="row namenode-uptime col-md-3">
+      <div class="summary-value main-info">{{view.nodeUptime}}</div>
+      <div class="summary-label">{{t 
dashboard.services.hdfs.nodes.uptime}}</div>
+    </div>
+    {{! NameNode Heap }}
+    <div class="row namenode-heap col-md-3">
+      <div class="summary-value">
+        <div class="main-info"> {{view.nodeHeapPercent}} </div>
+        <div class="info-desc"> {{view.nodeHeap}} </div>
+      </div>
+      <div class="summary-label">{{t dashboard.services.hdfs.nodes.heap}}</div>
+    </div>
+  </div>
+</div>
+
+{{! Service Metrics Section }}
+<div class="col-md-12 metrics-summary">
+  <div class="col-md-2">{{t 
dashboard.services.hdfs.summary.service-metrics}}</div>
+  <div class="col-md-10">
+    {{! HDFS Capacity (Disk Usage)}}
+    <div class="row dfs-usage col-md-3">
+      <div class="summary-value">
+        <div class="main-info">{{view.dfsUsedDiskPercent}}</div>
+        <div class="info-desc">{{view.dfsUsedDisk}}</div>
+      </div>
+      <div class="summary-label">{{t 
dashboard.services.hdfs.capacity.dfsUsed}}</div>
+    </div>
+    <div class="row non-dfs-used col-md-3">
+      <div class="summary-value main-info">
+        <div class="main-info">{{view.nonDfsUsedDiskPercent}}</div>
+        <div class="info-desc">{{view.nonDfsUsedDisk}}</div>
+      </div>
+      <div class="summary-label">{{t 
dashboard.services.hdfs.capacity.nonDfsUsed}}</div>
+    </div>
+    <div class="row capacity-remaining col-md-3">
+      <div class="summary-value">
+        <div class="main-info">{{view.remainingDiskPercent}}</div>
+        <div class="info-desc">{{view.remainingDisk}}</div>
+      </div>
+      <div class="summary-label">{{t 
dashboard.services.hdfs.capacity.remaining}}</div>
+    </div>
+    {{! Blocks Total }}
+    <div class="row blocks-total col-md-3">
+      <div class="summary-value main-info">{{view.dfsTotalBlocks}}</div>
+      <div class="summary-label">{{t 
services.service.summary.blocksTotal}}</div>
+    </div>
+    {{! indent next row}}
+    <div class="row"></div>
+    {{! Block Errors corrupt }}
+    <div class="row block-errors-corrupt col-md-3">
+      <div class="summary-value main-info">
+        <div class="main-info">{{view.dfsCorruptBlocks}}</div>
+        <div class="info-desc">{{t 
dashboard.services.hdfs.blockErrors.corrupt}}</div>
+      </div>
+      <div class="summary-label">{{t 
services.service.summary.blockErrors}}</div>
+    </div>
+    {{! Block Errors missing }}
+    <div class="row block-errors-missing col-md-3">
+      <div class="summary-value main-info">
+        <div class="main-info">{{view.dfsMissingBlocks}}</div>
+        <div class="info-desc">{{t 
dashboard.services.hdfs.blockErrors.missing}}</div>
+      </div>
+      <div class="summary-label">{{t 
services.service.summary.blockErrors}}</div>
+    </div>
+    {{! Block Errors replicated }}
+    <div class="row block-errors-replicated col-md-3">
+      <div class="summary-value main-info">
+        <div class="main-info">{{view.dfsUnderReplicatedBlocks}}</div>
+        <div class="info-desc">{{t 
dashboard.services.hdfs.blockErrors.replicated}}</div>
+      </div>
+      <div class="summary-label">{{t 
services.service.summary.blockErrors}}</div>
+    </div>
+    {{! indent next row}}
+    <div class="row"></div>
+    {{! Total Files And Directories }}
+    <div class="row total-files-dirs col-md-3">
+      <div class="summary-value main-info">{{view.dfsTotalFiles}}</div>
+      <div class="summary-label">{{t 
dashboard.services.hdfs.totalFilesAndDirs}}</div>
+    </div>
+    {{! Upgrade Status }}
+    <div class="row upgrade-status col-md-3">
+      <div class="summary-value main-info">
+        <span {{bindAttr 
class="view.isUpgradeStatusWarning:upgrade-status-warning"}}>{{view.upgradeStatus}}</span>
+      </div>
+      <div class="summary-label">{{t 
services.service.summary.pendingUpgradeStatus}}</div>
+    </div>
+    {{! Safe Mode Status }}
+    <div class="row safe-mode-status col-md-3">
+      <div class="summary-value main-info">
+        {{view.safeModeStatus}}
+      </div>
+      <div class="summary-label">{{t 
services.service.summary.safeModeStatus}}</div>
+    </div>
+  </div>
+</div>
diff --git 
a/ambari-web/app/templates/main/service/info/summary/master_components.hbs 
b/ambari-web/app/templates/main/service/info/summary/master_components.hbs
index 6ca36c1..7549caf 100644
--- a/ambari-web/app/templates/main/service/info/summary/master_components.hbs
+++ b/ambari-web/app/templates/main/service/info/summary/master_components.hbs
@@ -56,5 +56,8 @@
         {{/each}}
       </div>
     </div>
+    {{#if group.componentWidgetsView}}
+      {{view group.componentWidgetsView}}
+    {{/if}}
   {{/if}}
 {{/each}}
diff --git a/ambari-web/app/templates/main/service/services/hdfs.hbs 
b/ambari-web/app/templates/main/service/services/hdfs.hbs
index a49f33d..e51cb06 100644
--- a/ambari-web/app/templates/main/service/services/hdfs.hbs
+++ b/ambari-web/app/templates/main/service/services/hdfs.hbs
@@ -21,24 +21,6 @@
   <div class="component-summary">
     {{view view.dashboardMasterComponentView}}
     <div class="col-md-12">
-      <div class="col-md-10 col-md-offset-2">
-        {{! TODO add NameNode uptime and heap for each namespace}}
-        {{! NameNode Uptime }}
-        <div class="row namenode-uptime col-md-3">
-          <div class="summary-value main-info">{{view.nodeUptime}}</div>
-          <div class="summary-label">{{t 
dashboard.services.hdfs.nodes.uptime}}</div>
-        </div>
-        {{! NameNode Heap }}
-        <div class="row namenode-heap col-md-3">
-          <div class="summary-value">
-            <div class="main-info"> {{view.nodeHeapPercent}} </div>
-            <div class="info-desc"> {{view.nodeHeap}} </div>
-          </div>
-          <div class="summary-label">{{t 
dashboard.services.hdfs.nodes.heap}}</div>
-        </div>
-      </div>
-    </div>
-    <div class="col-md-12">
       <div class="col-md-2">
         {{#if view.hasMultipleMasterGroups}}
           {{t dashboard.services.hdfs.summary.components}}
@@ -162,87 +144,9 @@
       </div>
     </div>
   </div>
-  {{! left column end }}
 
-  {{! Service Metrics Section }}
-  <div class="col-md-12 metrics-summary">
-    <div class="col-md-2">{{t 
dashboard.services.hdfs.summary.service-metrics}}</div>
-    <div class="col-md-10">
-      {{! HDFS Capacity (Disk Usage)}}
-      <div class="row dfs-usage col-md-3">
-        <div class="summary-value">
-          <div class="main-info">{{view.dfsUsedDiskPercent}}</div>
-          <div class="info-desc">{{view.dfsUsedDisk}}</div>
-        </div>
-        <div class="summary-label">{{t 
dashboard.services.hdfs.capacity.dfsUsed}}</div>
-      </div>
-      <div class="row non-dfs-used col-md-3">
-        <div class="summary-value main-info">
-          <div class="main-info">{{view.nonDfsUsedDiskPercent}}</div>
-          <div class="info-desc">{{view.nonDfsUsedDisk}}</div>
-        </div>
-        <div class="summary-label">{{t 
dashboard.services.hdfs.capacity.nonDfsUsed}}</div>
-      </div>
-      <div class="row capacity-remaining col-md-3">
-        <div class="summary-value">
-          <div class="main-info">{{view.remainingDiskPercent}}</div>
-          <div class="info-desc">{{view.remainingDisk}}</div>
-        </div>
-        <div class="summary-label">{{t 
dashboard.services.hdfs.capacity.remaining}}</div>
-      </div>
-      {{! Blocks Total }}
-      <div class="row blocks-total col-md-3">
-        <div class="summary-value main-info">{{view.dfsTotalBlocks}}</div>
-        <div class="summary-label">{{t 
services.service.summary.blocksTotal}}</div>
-      </div>
-      {{! indent next row}}
-      <div class="row"></div>
-      {{! Block Errors corrupt }}
-      <div class="row block-errors-corrupt col-md-3">
-        <div class="summary-value main-info">
-          <div class="main-info">{{view.dfsCorruptBlocks}}</div>
-          <div class="info-desc">{{t 
dashboard.services.hdfs.blockErrors.corrupt}}</div>
-        </div>
-        <div class="summary-label">{{t 
services.service.summary.blockErrors}}</div>
-      </div>
-      {{! Block Errors missing }}
-      <div class="row block-errors-missing col-md-3">
-        <div class="summary-value main-info">
-          <div class="main-info">{{view.dfsMissingBlocks}}</div>
-          <div class="info-desc">{{t 
dashboard.services.hdfs.blockErrors.missing}}</div>
-        </div>
-        <div class="summary-label">{{t 
services.service.summary.blockErrors}}</div>
-      </div>
-      {{! Block Errors replicated }}
-      <div class="row block-errors-replicated col-md-3">
-        <div class="summary-value main-info">
-          <div class="main-info">{{view.dfsUnderReplicatedBlocks}}</div>
-          <div class="info-desc">{{t 
dashboard.services.hdfs.blockErrors.replicated}}</div>
-        </div>
-        <div class="summary-label">{{t 
services.service.summary.blockErrors}}</div>
-      </div>
-      {{! indent next row}}
-      <div class="row"></div>
-      {{! Total Files And Directories }}
-      <div class="row total-files-dirs col-md-3">
-        <div class="summary-value main-info">{{view.dfsTotalFiles}}</div>
-        <div class="summary-label">{{t 
dashboard.services.hdfs.totalFilesAndDirs}}</div>
-      </div>
-      {{! Upgrade Status }}
-      <div class="row upgrade-status col-md-3">
-        <div class="summary-value main-info">
-          <span {{bindAttr 
class="view.isUpgradeStatusWarning:upgrade-status-warning"}}>{{view.upgradeStatus}}</span>
-        </div>
-        <div class="summary-label">{{t 
services.service.summary.pendingUpgradeStatus}}</div>
-      </div>
-      {{! Safe Mode Status }}
-      <div class="row safe-mode-status col-md-3">
-        <div class="summary-value main-info">
-          {{view.safeModeStatus}}
-        </div>
-        <div class="summary-label">{{t 
services.service.summary.safeModeStatus}}</div>
-      </div>
-    </div>
-  </div>
-  {{! right column end }}
+  {{#unless view.hasMultipleMasterGroups}}
+    {{! Service Metrics Section }}
+    {{view App.HDFSSummaryWidgetsView}}
+  {{/unless}}
 </div>
\ No newline at end of file
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 52d6a57..9eced52 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -311,6 +311,7 @@ require('views/main/service/reconfigure');
 require('views/main/service/info/components_list_view');
 require('views/main/service/info/menu');
 require('views/main/service/info/summary');
+require('views/main/service/info/summary/hdfs/widgets');
 require('views/main/service/info/configs');
 require('views/main/service/info/metric_graphs_view');
 require('views/main/service/info/metrics/ambari_metrics/master_average_load');
diff --git a/ambari-web/app/views/common/chart/linear_time.js 
b/ambari-web/app/views/common/chart/linear_time.js
index 691fda4..dfa1d90 100644
--- a/ambari-web/app/views/common/chart/linear_time.js
+++ b/ambari-web/app/views/common/chart/linear_time.js
@@ -362,14 +362,8 @@ App.ChartLinearTimeView = 
Ember.View.extend(App.ExportMetricsMixin, {
     var fromSeconds,
       toSeconds,
       hostName = (this.get('content')) ? this.get('content.hostName') : "",
-      HDFSService = App.HDFSService.find().objectAt(0),
-      nameNodeName = "",
       YARNService = App.YARNService.find().objectAt(0),
       resourceManager = YARNService ? 
YARNService.get('resourceManager.hostName') : "";
-    if (HDFSService) {
-      // TODO rewrite considering federation case and using activeNameNodes 
array
-      nameNodeName = (HDFSService.get('activeNameNode')) ? 
HDFSService.get('activeNameNode.hostName') : 
HDFSService.get('nameNode.hostName');
-    }
     if (this.get('currentTimeIndex') === 8 && 
!Em.isNone(this.get('customStartTime')) && 
!Em.isNone(this.get('customEndTime'))) {
       // Custom start and end time is specified by user
       toSeconds = this.get('customEndTime') / 1000;
@@ -385,7 +379,6 @@ App.ChartLinearTimeView = 
Ember.View.extend(App.ExportMetricsMixin, {
       fromSeconds: fromSeconds,
       stepSeconds: 15,
       hostName: hostName,
-      nameNodeName: nameNodeName,
       resourceManager: resourceManager
     };
   },
diff --git a/ambari-web/app/views/common/quick_view_link_view.js 
b/ambari-web/app/views/common/quick_view_link_view.js
index 2e4b61a..25e812b 100644
--- a/ambari-web/app/views/common/quick_view_link_view.js
+++ b/ambari-web/app/views/common/quick_view_link_view.js
@@ -83,6 +83,8 @@ App.QuickLinksView = Em.View.extend({
 
   tooltipAttribute: 'quick-links-title-tooltip',
 
+  shouldSetGroupedLinks: false,
+
   /**
    * @type {object}
    */
@@ -291,7 +293,7 @@ App.QuickLinksView = Em.View.extend({
       this.setEmptyLinks();
     } else if (hosts.length === 1 || isMultipleComponentsInLinks || 
this.hasOverriddenHost()) {
       this.setSingleHostLinks(hosts, response);
-    } else if (this.get('masterGroups.length') > 1) {
+    } else if (this.get('masterGroups.length') > 1 || 
this.get('shouldSetGroupedLinks')) {
       this.setMultipleGroupLinks(hosts);
     } else {
       this.setMultipleHostLinks(hosts);
diff --git a/ambari-web/app/views/main/dashboard/widgets.js 
b/ambari-web/app/views/main/dashboard/widgets.js
index ec10eae..1048c58 100644
--- a/ambari-web/app/views/main/dashboard/widgets.js
+++ b/ambari-web/app/views/main/dashboard/widgets.js
@@ -38,6 +38,45 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
     return this.get('widgetsDefinition').toMapByProperty('id');
   }.property('widgetsDefinition.[]'),
 
+  widgetGroups: [],
+
+  widgetGroupsDeferred: $.Deferred(),
+
+  setWidgetGroups: function () {
+    if (App.get('router.clusterController.isHDFSNameSpacesLoaded')) {
+      let groups = [];
+      const hdfsMasterGroups = 
App.HDFSService.find().objectAt(0).get('masterComponentGroups');
+      
this.removeObserver('App.router.clusterController.isHDFSNameSpacesLoaded', 
this, 'setWidgetGroups');
+      if (hdfsMasterGroups.length > 1) {
+        const nameSpacesListItems = hdfsMasterGroups.map(nameSpace => {
+          const {name, title} = nameSpace;
+          return {
+            name,
+            title,
+            isActive: false
+          };
+        });
+        groups.push(Em.Object.create({
+          name: 'nn',
+          title: Em.I18n.t('dashboard.widgets.nameSpace'),
+          serviceName: 'HDFS',
+          subGroups: [
+            {
+              name: '*',
+              title: Em.I18n.t('common.all'),
+              isActive: true
+            },
+            ...nameSpacesListItems
+          ],
+          activeSubGroup: Em.computed.findBy('subGroups', 'isActive', true),
+          allWidgets: this.get('allNameNodeWidgets')
+        }));
+      }
+      this.set('widgetGroups', groups);
+      this.get('widgetGroupsDeferred').resolve();
+    }
+  },
+
   /**
    * List of services
    * @type {Ember.Enumerable}
@@ -66,6 +105,8 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
    */
   allWidgets: [],
 
+  allNameNodeWidgets: [],
+
   /**
    * List of visible widgets
    *
@@ -99,12 +140,19 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
 
   didInsertElement: function () {
     this._super();
+    if (App.get('router.clusterController.isHDFSNameSpacesLoaded')) {
+      this.setWidgetGroups();
+    } else {
+      this.addObserver('App.router.clusterController.isHDFSNameSpacesLoaded', 
this, 'setWidgetGroups');
+    }
     this.loadWidgetsSettings().complete(() => {
-      this.checkServicesChange();
-      this.renderWidgets();
-      this.set('isDataLoaded', true);
-      App.loadTimer.finish('Dashboard Metrics Page');
-      Em.run.next(this, 'makeSortable');
+      this.get('widgetGroupsDeferred').done(() => {
+        this.checkServicesChange();
+        this.renderWidgets();
+        this.set('isDataLoaded', true);
+        App.loadTimer.finish('Dashboard Metrics Page');
+        Em.run.next(this, 'makeSortable');
+      });
     });
   },
 
@@ -126,7 +174,8 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
     let newSettings = {
       visible: [],
       hidden: [],
-      threshold: {}
+      threshold: {},
+      groups: {}
     };
     if (arguments.length === 1) {
       newSettings = settings;
@@ -137,6 +186,7 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
         let key = widget.get('isVisible') ? 'visible' : 'hidden';
         newSettings[key].push(widget.get('id'));
       });
+      //TODO handle grouped widgets
     }
     this.set('userPreferences', newSettings);
     this.setDBProperty(this.get('persistKey'), newSettings);
@@ -169,16 +219,43 @@ App.MainDashboardWidgetsView = 
Em.View.extend(App.Persist, App.LocalStorage, App
     var preferences = {
       visible: [],
       hidden: [],
-      threshold: {}
+      threshold: {},
+      groups: {}
     };
 
     this.resolveConfigDependencies(widgetsDefinition);
-    widgetsDefinition.forEach(function(widget) {
-      if (App.Service.find(widget.sourceName).get('isLoaded') || 
widget.sourceName === 'HOST_METRICS') {
-        let state = widget.isHiddenByDefault ? 'hidden' : 'visible';
-        preferences[state].push(widget.id);
+    widgetsDefinition.forEach(widget => {
+      const {sourceName, id} = widget,
+        widgetGroups = this.get('widgetGroups');
+      if (App.Service.find(sourceName).get('isLoaded') || sourceName === 
'HOST_METRICS') {
+        const state = widget.isHiddenByDefault ? 'hidden' : 'visible',
+          {threshold} = widget,
+          widgetGroup = widgetGroups.findProperty('serviceName', sourceName);
+        if (widgetGroup) {
+          const widgetGroupName = widgetGroup.get('name'),
+            subGroups = widgetGroup.get('subGroups'),
+            existingEntry = preferences.groups[widgetGroupName],
+            currentEntry = existingEntry || subGroups.reduce((current, 
subGroup) => {
+                return Object.assign({}, current, {
+                  [subGroup.name]: {
+                    visible: [],
+                    hidden: [],
+                    threshold: {}
+                  }
+                });
+              }, {});
+          subGroups.forEach(subGroup => {
+            const {name} = subGroup;
+            currentEntry[name][state].push(id);
+            currentEntry[name].threshold[id] = threshold;
+          });
+          if (!existingEntry) {
+            preferences.groups[widgetGroupName] = currentEntry;
+          }
+        }
+        preferences[state].push(id);
       }
-      preferences.threshold[widget.id] = widget.threshold;
+      preferences.threshold[id] = widget.threshold;
     });
 
     return preferences;
@@ -190,6 +267,7 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
    * @param {number} id
    */
   hideWidget(id) {
+    // TODO handle grouped widgets
     this.get('allWidgets').findProperty('id', id).set('isVisible', false);
     this.saveWidgetsSettings();
   },
@@ -214,12 +292,88 @@ App.MainDashboardWidgetsView = 
Em.View.extend(App.Persist, App.LocalStorage, App
   },
 
   /**
+   *
+   * @param {number} id
+   * @param {string} groupId
+   * @param {string} subGroupId
+   * @param {boolean} isVisible
+   * @returns {WidgetObject}
+   * @private
+   */
+  _createGroupWidgetObj(id, groupId, subGroupId, isVisible) {
+    var widget = this.get('widgetsDefinitionMap')[id];
+    return WidgetObject.create({
+      id: `${id}-${groupId}-${subGroupId}`,
+      threshold: 
this.get('userPreferences.groups')[groupId][subGroupId].threshold[id],
+      viewClass: App[widget.viewName].extend({
+        subGroupId
+      }),
+      sourceName: widget.sourceName,
+      title: `${widget.title} - ${subGroupId}`,
+      isVisible
+    });
+  },
+
+  /**
    * set widgets to view in order to render
    */
   renderWidgets: function () {
-    var userPreferences = this.get('userPreferences');
-    var newVisibleWidgets = userPreferences.visible.map(id => 
this._createWidgetObj(id, true));
-    var newHiddenWidgets = userPreferences.hidden.map(id => 
this._createWidgetObj(id, false));
+    const userPreferences = this.get('userPreferences'),
+      widgetsDefinition = this.get('widgetsDefinition'),
+      widgetGroups = this.get('widgetGroups');
+    let newVisibleWidgets = [],
+      newHiddenWidgets = [];
+    widgetGroups.forEach(group => group.get('allWidgets').clear());
+    widgetsDefinition.forEach(widget => {
+      const {id, groupName} = widget;
+      if (groupName && widgetGroups.someProperty('name', groupName)) {
+        const widgetGroup = widgetGroups.findProperty('name', groupName),
+          groupPreferences = userPreferences.groups[groupName];
+        if (groupPreferences) {
+          const allWidgets = widgetGroup.get('allWidgets'),
+            subGroupNames = widgetGroup.get('subGroups').mapProperty('name');
+          subGroupNames.forEach(subGroupName => {
+            const subGroupPreferences = groupPreferences[subGroupName],
+              existingSubGroup = allWidgets.findProperty('subGroupName', 
subGroupName),
+              currentSubGroup = existingSubGroup || Em.Object.create({
+                  subGroupName,
+                  parentGroup: widgetGroup,
+                  isActive: 
Em.computed.equal('parentGroup.activeSubGroup.name', subGroupName),
+                  widgets: []
+                });
+            if (!existingSubGroup) {
+              allWidgets.pushObject(currentSubGroup);
+            }
+            if (subGroupName === '*') {
+              subGroupNames.forEach(name => {
+                if (name !== '*') {
+                  if (subGroupPreferences.visible.contains(id)) {
+                    
currentSubGroup.get('widgets').push(this._createGroupWidgetObj(id, groupName, 
name, true));
+                  }
+                  if (subGroupPreferences.hidden.contains(id)) {
+                    
currentSubGroup.get('widgets').push(this._createGroupWidgetObj(id, groupName, 
name, false));
+                  }
+                }
+              });
+            } else {
+              if (subGroupPreferences.visible.contains(id)) {
+                
currentSubGroup.get('widgets').push(this._createGroupWidgetObj(id, groupName, 
subGroupName, true));
+              }
+              if (subGroupPreferences.hidden.contains(id)) {
+                
currentSubGroup.get('widgets').push(this._createGroupWidgetObj(id, groupName, 
subGroupName, false));
+              }
+            }
+          });
+        }
+      } else {
+        if (userPreferences.visible.contains(id)) {
+          newVisibleWidgets.push(this._createWidgetObj(id, true));
+        }
+        if (userPreferences.hidden.contains(id)) {
+          newHiddenWidgets.push(this._createWidgetObj(id, false));
+        }
+      }
+    });
     this.set('allWidgets', newVisibleWidgets.concat(newHiddenWidgets));
   },
 
@@ -228,14 +382,15 @@ App.MainDashboardWidgetsView = 
Em.View.extend(App.Persist, App.LocalStorage, App
    * Update the value on server if true.
    */
   checkServicesChange: function () {
-    var userPreferences = this.get('userPreferences');
-    var defaultPreferences = this.generateDefaultUserPreferences();
-    var newValue = {
-      visible: userPreferences.visible.slice(0),
-      hidden: userPreferences.hidden.slice(0),
-      threshold: userPreferences.threshold
-    };
-    var isChanged = false;
+    const userPreferences = this.get('userPreferences'),
+      defaultPreferences = this.generateDefaultUserPreferences();
+    let newValue = {
+        visible: userPreferences.visible.slice(0),
+        hidden: userPreferences.hidden.slice(0),
+        threshold: userPreferences.threshold,
+        groups: userPreferences.groups || {}
+      },
+      isChanged = false;
 
     ['visible', 'hidden'].forEach(state => {
       defaultPreferences[state].forEach(id => {
@@ -244,6 +399,30 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
           newValue[state].push(id);
         }
       });
+      Object.keys(defaultPreferences.groups).forEach(groupName => {
+        const groupPreferences = defaultPreferences.groups[groupName];
+        Object.keys(groupPreferences).forEach(subGroupName => {
+          groupPreferences[subGroupName][state].forEach(id => {
+            if (!newValue.groups[groupName] || 
!newValue.groups[groupName][subGroupName]) {
+              isChanged = true;
+              $.extend(true, newValue.groups, {
+                [groupName]: {
+                  [subGroupName]: {
+                    visible: [],
+                    hidden: [],
+                    threshold: {}
+                  }
+                }
+              });
+            }
+            const subGroupPreferences = 
newValue.groups[groupName][subGroupName];
+            if (!subGroupPreferences.visible.contains(id) && 
!subGroupPreferences.hidden.contains(id)) {
+              isChanged = true;
+              subGroupPreferences[state].push(id);
+            }
+          });
+        });
+      });
     });
     if (isChanged) {
       this.saveWidgetsSettings(newValue);
@@ -325,6 +504,11 @@ App.MainDashboardWidgetsView = Em.View.extend(App.Persist, 
App.LocalStorage, App
     }
   }),
 
-  showAlertsPopup: Em.K
+  showAlertsPopup: Em.K,
+
+  setActiveSubGroup: function (event) {
+    const subGroups = event.contexts[0] || [];
+    subGroups.forEach(subGroup => Em.set(subGroup, 'isActive', 
Em.get(subGroup, 'name') === event.contexts[1]));
+  }
 
 });
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js 
b/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
index 4ddba36..4a1f4e7 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
+++ b/ambari-web/app/views/main/dashboard/widgets/hbase_master_heap.js
@@ -17,12 +17,11 @@
  */
 
 var App = require('app');
-var numberUtils = require('utils/number_utils');
 
 App.HBaseMasterHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  modelFieldMax: 'heapMemoryMax',
-  modelFieldUsed: 'heapMemoryUsed',
+  modelValueMax: Em.computed.alias('model.heapMemoryMax'),
+  modelValueUsed: Em.computed.alias('model.heapMemoryUsed'),
   widgetHtmlId: 'widget-hbase-heap',
 
   didInsertElement: function() {
@@ -31,10 +30,10 @@ App.HBaseMasterHeapPieChartView = 
App.PieChartDashboardWidgetView.extend({
   },
 
   getUsed: function() {
-    return this.get('model').get(this.get('modelFieldUsed')) / (1024 * 1024) 
|| 0;
+    return this.get('modelValueUsed') / (1024 * 1024) || 0;
   },
 
   getMax: function() {
-    return this.get('model').get(this.get('modelFieldMax')) / (1024 * 1024) || 
0;
+    return this.get('modelValueMax') / (1024 * 1024) || 0;
   }
 });
\ No newline at end of file
diff --git a/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js 
b/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
index 8ca3671..6e9c7ce 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
+++ b/ambari-web/app/views/main/dashboard/widgets/hbase_master_uptime.js
@@ -21,6 +21,6 @@ var App = require('app');
 App.HBaseMasterUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
   component: 'Hbase Master',
-  modelField: 'masterStartTime'
+  modelValue: Em.computed.alias('model.masterStartTime')
 
 });
\ No newline at end of file
diff --git a/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js 
b/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
index 6bc19d9..903a1ed 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
+++ b/ambari-web/app/views/main/dashboard/widgets/hdfs_capacity.js
@@ -19,14 +19,16 @@
 var App = require('app');
 var numberUtils = require('utils/number_utils');
 
-App.NameNodeCapacityPieChartView = App.PieChartDashboardWidgetView.extend({
+App.NameNodeCapacityPieChartView = 
App.PieChartDashboardWidgetView.extend(App.NameNodeWidgetMixin, {
 
-  modelFieldMax: 'capacityTotal',
+  modelValueMax: Em.computed.getByKey('model.capacityTotalValues', 
'clusterId'),
   /**
    * HDFS model has 'remaining' value, but not 'used'
    */
-  modelFieldUsed: 'capacityRemaining',
-  widgetHtmlId: 'widget-nn-capacity',
+  modelValueUsed: Em.computed.getByKey('model.capacityRemainingValues', 
'clusterId'),
+  modelValueCapacityUsed: Em.computed.getByKey('model.capacityUsedValues', 
'clusterId'),
+  modelValueNonDfsUsed: Em.computed.getByKey('model.capacityNonDfsUsedValues', 
'clusterId'),
+  widgetHtmlId: Em.computed.format('widget-nn-capacity-{0}', 'subGroupId'),
   hiddenInfoClass: "hidden-info-six-line",
 
   didInsertElement: function() {
@@ -35,10 +37,10 @@ App.NameNodeCapacityPieChartView = 
App.PieChartDashboardWidgetView.extend({
   },
 
   calcHiddenInfo: function () {
-    var total = this.get('model').get(this.get('modelFieldMax')) + 0;
-    var remaining = this.get('model.capacityRemaining');
-    var dfsUsed = this.get('model.capacityUsed');
-    var nonDfsUsed = this.get('model.capacityNonDfsUsed');
+    var total = this.get('modelValueMax') + 0;
+    var remaining = this.get('modelValueUsed');
+    var dfsUsed = this.get('modelValueCapacityUsed');
+    var nonDfsUsed = this.get('modelValueNonDfsUsed');
     var dfsPercent = total > 0 ? (dfsUsed * 100 / total).toFixed(2) : 0;
     var nonDfsPercent = total > 0 ? (nonDfsUsed * 100 / total).toFixed(2) : 0;
     var remainingPercent = total > 0 ? (remaining * 100 / total).toFixed(2) : 
0;
@@ -62,8 +64,8 @@ App.NameNodeCapacityPieChartView = 
App.PieChartDashboardWidgetView.extend({
   },
 
   calcDataForPieChart: function() {
-    var total = this.get('model').get(this.get('modelFieldMax')) * 1024 * 1024;
-    var used = total - this.get('model').get(this.get('modelFieldUsed')) * 
1024 * 1024;
+    var total = this.get('modelValueMax') * 1024 * 1024;
+    var used = total - this.get('modelValueUsed') * 1024 * 1024;
     var percent = total > 0 ? (used * 100 / total).toFixed() : 0;
     var percentPrecise = total > 0 ? (used * 100 / total).toFixed(1) : 0;
     return [percent, percentPrecise];
diff --git a/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js 
b/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
index e4af3cd..f5c0f62 100644
--- a/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
+++ b/ambari-web/app/views/main/dashboard/widgets/hdfs_links.js
@@ -18,10 +18,14 @@
 
 var App = require('app');
 
-App.HDFSLinksView = App.LinkDashboardWidgetView.extend({
+App.HDFSLinksView = 
App.LinkDashboardWidgetView.extend(App.NameNodeWidgetMixin, {
 
   templateName: require('templates/main/dashboard/widgets/hdfs_links'),
 
+  activeNameNode: Em.computed.findByKey('model.activeNameNodes', 
'haNameSpace', 'clusterId'),
+
+  standbyNameNodes: Em.computed.filterByKey('model.standbyNameNodes', 
'haNameSpace', 'clusterId'),
+
   port: '50070',
 
   componentName: 'DATANODE',
@@ -35,15 +39,18 @@ App.HDFSLinksView = App.LinkDashboardWidgetView.extend({
 
   isHAEnabled: Em.computed.not('model.snameNode'),
 
-  // TODO rewrite considering federation case and using activeNameNodes and 
standbyNameNodes arrays
+  isActiveNNValid: Em.computed.bool('activeNameNode'),
 
-  isActiveNNValid: Em.computed.bool('model.activeNameNode'),
+  isStandbyNNValid: Em.computed.bool('standbyNameNodes.length'),
 
-  isStandbyNNValid: Em.computed.bool('model.standbyNameNode'),
-
-  isTwoStandbyNN: Em.computed.and('model.standbyNameNode', 
'model.standbyNameNode2'),
+  isTwoStandbyNN: Em.computed.equal('standbyNameNodes.length', 2),
 
   twoStandbyComponent: function () {
     return App.HostComponent.find().findProperty('componentName', 'NAMENODE');
-  }.property()
+  }.property(),
+
+  masterGroupsArray: function () {
+    const activeMasterGroup = 
this.get('model.masterComponentGroups').find(group => group.name === 
this.get('subGroupId'));
+    return [activeMasterGroup];
+  }.property('model.masterComponentGroups', 'subGroupId')
 });
\ No newline at end of file
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js 
b/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
index 2916cd3..394559d 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_cpu.js
@@ -18,9 +18,9 @@
 
 var App = require('app');
 
-App.NameNodeCpuPieChartView = App.PieChartDashboardWidgetView.extend({
+App.NameNodeCpuPieChartView = 
App.PieChartDashboardWidgetView.extend(App.NameNodeWidgetMixin, {
 
-  widgetHtmlId: 'widget-nn-cpu',
+  widgetHtmlId: Em.computed.format('widget-nn-cpu-{0}', 'subGroupId'),
   cpuWio: null,
   nnHostName: "",
   intervalId: null,
@@ -31,21 +31,20 @@ App.NameNodeCpuPieChartView = 
App.PieChartDashboardWidgetView.extend({
 
   didInsertElement: function () {
     this._super();
-    var self = this,
-      intervalId;
-    
App.router.get('mainController').isLoading.call(App.router.get('clusterController'),
 'isServiceContentFullyLoaded').done(function () {
+    let intervalId;
+    
App.router.get('mainController').isLoading.call(App.router.get('clusterController'),
 'isServiceContentFullyLoaded').done(() => {
       if (App.get('isHaEnabled')) {
-        // TODO rewrite considering federation case and using activeNameNodes 
array
-        self.set('nnHostName', self.get('model.activeNameNode.hostName'));
+        const nn = 
this.get('model.activeNameNodes').findProperty('haNameSpace', 
this.get('subGroupId'));
+        if (nn) {
+          this.set('nnHostName', nn.get('hostName'));
+        }
       } else {
-        self.set('nnHostName', self.get('model.nameNode.hostName'));
+        this.set('nnHostName', self.get('model.nameNode.hostName'));
       }
-      if (self.get('nnHostName')) {
-        self.getValue();
-        intervalId = setInterval(function () {
-          self.getValue()
-        }, App.componentsUpdateInterval);
-        self.set('intervalId', intervalId);
+      if (this.get('nnHostName')) {
+        this.getValue();
+        intervalId = setInterval(() => this.getValue(), 
App.componentsUpdateInterval);
+        this.set('intervalId', intervalId);
       }
     });
   },
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js 
b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
index 2ef6470..9f4eaa2 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_heap.js
@@ -18,18 +18,18 @@
 
 var App = require('app');
 
-App.NameNodeHeapPieChartView = App.PieChartDashboardWidgetView.extend({
+App.NameNodeHeapPieChartView = 
App.PieChartDashboardWidgetView.extend(App.NameNodeWidgetMixin, {
 
-  modelFieldMax: 'jvmMemoryHeapMax',
-  modelFieldUsed: 'jvmMemoryHeapUsed',
-  widgetHtmlId: 'widget-nn-heap',
+  modelValueMax: Em.computed.getByKey('model.jvmMemoryHeapMaxValues', 
'clusterId'),
+  modelValueUsed: Em.computed.getByKey('model.jvmMemoryHeapUsedValues', 
'clusterId'),
+  widgetHtmlId: Em.computed.format('widget-nn-heap-{0}', 'subGroupId'),
 
   getUsed: function() {
-    return this.get('model').get(this.get('modelFieldUsed')) / (1024 * 1024) 
|| 0;
+    return this.get('modelValueUsed') / (1024 * 1024) || 0;
   },
 
   getMax: function() {
-    return this.get('model').get(this.get('modelFieldMax')) / (1024 * 1024) || 
0;
+    return this.get('modelValueMax') / (1024 * 1024) || 0;
   },
 
   didInsertElement: function() {
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js 
b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
index 769fd22..ca62839 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_rpc.js
@@ -18,7 +18,7 @@
 
 var App = require('app');
 
-App.NameNodeRpcView = 
App.TextDashboardWidgetView.extend(App.EditableWidgetMixin, {
+App.NameNodeRpcView = 
App.TextDashboardWidgetView.extend(App.EditableWidgetMixin, 
App.NameNodeWidgetMixin, {
 
   hiddenInfo: function () {
     return [
@@ -34,21 +34,23 @@ App.NameNodeRpcView = 
App.TextDashboardWidgetView.extend(App.EditableWidgetMixin
   isRed: Em.computed.gtProperties('data', 'thresholdMax'),
 
   data: function () {
-    if (this.get('model.nameNodeRpc')) {
-      return (this.get('model.nameNodeRpc')).toFixed(2);
+    const clusterId = this.get('clusterId'),
+      rpc = this.get(`model.nameNodeRpcValues.${clusterId}`);
+    if (rpc) {
+      return rpc.toFixed(2);
     }
-    if (this.get('model.nameNodeRpc') == 0) {
+    if (rpc == 0) {
       return 0;
     }
     return null;
-  }.property('model.nameNodeRpc'),
+  }.property('model.nameNodeRpcValues', 'clusterId'),
 
   content: function () {
     if (this.get('data') || this.get('data') == 0) {
       return this.get('data') + " ms";
     }
     return Em.I18n.t('services.service.summary.notAvailable');
-  }.property('model.nameNodeRpc'),
+  }.property('model.nameNodeRpcValues', 'clusterId'),
 
   hintInfo: Em.I18n.t('dashboard.widgets.hintInfo.hint3')
 
diff --git a/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js 
b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
index 3f4876f..71f285c 100644
--- a/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
+++ b/ambari-web/app/views/main/dashboard/widgets/namenode_uptime.js
@@ -18,9 +18,9 @@
 
 var App = require('app');
 
-App.NameNodeUptimeView = App.UptimeTextDashboardWidgetView.extend({
+App.NameNodeUptimeView = 
App.UptimeTextDashboardWidgetView.extend(App.NameNodeWidgetMixin, {
 
   component: 'NameNode',
-  modelField: 'nameNodeStartTime'
+  modelValue: Em.computed.getByKey('model.nameNodeStartTimeValues', 
'clusterId')
 
 });
\ No newline at end of file
diff --git a/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js 
b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
index 34809b8..efb8069 100644
--- a/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
+++ b/ambari-web/app/views/main/dashboard/widgets/pie_chart_widget.js
@@ -25,8 +25,8 @@ App.PieChartDashboardWidgetView = 
App.DashboardWidgetView.extend({
 
   maxValue: 100,
 
-  modelFieldMax: null,
-  modelFieldUsed: null,
+  modelValueMax: null,
+  modelValueUsed: null,
 
   hiddenInfo: null,
 
@@ -37,11 +37,11 @@ App.PieChartDashboardWidgetView = 
App.DashboardWidgetView.extend({
   widgetHtmlId: null,
 
   getUsed: function() {
-    return this.get('model').get(this.get('modelFieldUsed')) || 0;
+    return this.get('modelValueUsed') || 0;
   },
 
   getMax: function() {
-    return this.get('model').get(this.get('modelFieldMax')) || 0;
+    return this.get('modelValueMax') || 0;
   },
 
   calcHiddenInfo: function() {
@@ -55,7 +55,7 @@ App.PieChartDashboardWidgetView = 
App.DashboardWidgetView.extend({
   },
 
   calcIsPieExists: function() {
-    return this.get('model').get(this.get('modelFieldMax')) > 0;
+    return this.get('modelValueMax') > 0;
   },
 
   calcDataForPieChart: function() {
@@ -77,8 +77,8 @@ App.PieChartDashboardWidgetView = 
App.DashboardWidgetView.extend({
 
   didInsertElement: function() {
     this._super();
-    this.addObserver('model.' + this.get('modelFieldMax'), this, this.calc);
-    this.addObserver('model.' + this.get('modelFieldUsed'), this, this.calc);
+    this.addObserver('modelValueMax', this, this.calc);
+    this.addObserver('modelValueUsed', this, this.calc);
   },
 
   content: App.ChartPieView.extend({
diff --git 
a/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js 
b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
index e0e6514..a294029 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
+++ b/ambari-web/app/views/main/dashboard/widgets/resource_manager_heap.js
@@ -20,16 +20,16 @@ var App = require('app');
 
 App.ResourceManagerHeapPieChartView = App.PieChartDashboardWidgetView.extend({
 
-  modelFieldMax: 'jvmMemoryHeapMax',
-  modelFieldUsed: 'jvmMemoryHeapUsed',
+  modelValueMax: Em.computed.alias('model.jvmMemoryHeapMax'),
+  modelValueUsed: Em.computed.alias('model.jvmMemoryHeapUsed'),
   widgetHtmlId: 'widget-rm-heap',
 
   getUsed: function() {
-    return this.get('model').get(this.get('modelFieldUsed')) / (1024 * 1024) 
|| 0;
+    return this.get('modelValueUsed') / (1024 * 1024) || 0;
   },
 
   getMax: function() {
-    return this.get('model').get(this.get('modelFieldMax')) / (1024 * 1024) || 
0;
+    return this.get('modelValueMax') / (1024 * 1024) || 0;
   },
 
   didInsertElement: function() {
diff --git 
a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js 
b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
index a9da7a8..395b5c9 100644
--- a/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
+++ b/ambari-web/app/views/main/dashboard/widgets/resource_manager_uptime.js
@@ -21,6 +21,6 @@ var App = require('app');
 App.ResourceManagerUptimeView = App.UptimeTextDashboardWidgetView.extend({
 
   component: 'ResourceManager',
-  modelField: 'resourceManagerStartTime'
+  modelValue: Em.computed.alias('model.resourceManagerStartTime')
 
 });
diff --git a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js 
b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
index 76982c1..c647281 100644
--- a/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
+++ b/ambari-web/app/views/main/dashboard/widgets/uptime_text_widget.js
@@ -30,11 +30,12 @@ App.UptimeTextDashboardWidgetView = 
App.TextDashboardWidgetView.extend({
   maxValue: 'infinity',
 
   component: null,
+
   /**
-   * Model's field that used to calculate data and content
+   * Value received from model that's used to calculate data and content
    * Should be defined in every child
    */
-  modelField: null,
+  modelValue: null,
 
   data: null,
 
@@ -65,11 +66,11 @@ App.UptimeTextDashboardWidgetView = 
App.TextDashboardWidgetView.extend({
   didInsertElement: function () {
     this._super();
     this.calculate();
-    this.addObserver('model.' + this.get('modelField'), this, this.calculate);
+    this.addObserver('modelValue', this, this.calculate);
   },
 
   calculate: function () {
-    const uptime = this.get('model').get(this.get('modelField'));
+    const uptime = this.get('modelValue');
     if (uptime) {
       let diff = App.dateTimeWithTimeZone() - App.dateTimeWithTimeZone(uptime);
       diff = diff < 0 ? 0 : diff;
diff --git a/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js 
b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
index be3beb2..61e30e8 100644
--- a/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
+++ b/ambari-web/app/views/main/dashboard/widgets/yarn_memory.js
@@ -21,8 +21,8 @@ var App = require('app');
 App.YARNMemoryPieChartView = App.PieChartDashboardWidgetView.extend({
 
   widgetHtmlId: 'widget-yarn-memory',
-  modelFieldUsed: 'allocatedMemory',
-  modelFieldMax: 'maxMemory',
+  modelValueUsed: Em.computed.alias('model.allocatedMemory'),
+  modelValueMax: Em.computed.alias('model.maxMemory'),
 
   didInsertElement: function() {
     this._super();
diff --git a/ambari-web/app/views/main/service/info/summary.js 
b/ambari-web/app/views/main/service/info/summary.js
index 40f4721..ed51947 100644
--- a/ambari-web/app/views/main/service/info/summary.js
+++ b/ambari-web/app/views/main/service/info/summary.js
@@ -443,7 +443,10 @@ App.MainServiceInfoSummaryView = Em.View.extend({
               if (!existingGroup) {
                 groups.push(currentGroup);
                 Em.setProperties(currentGroup, {
-                  components: []
+                  components: [],
+                  componentWidgetsView: App.HDFSSummaryWidgetsView.extend({
+                    nameSpace: name
+                  })
                 });
               }
               currentGroup.components.push(component);
diff --git a/ambari-web/app/views/main/service/info/summary/hdfs/widgets.js 
b/ambari-web/app/views/main/service/info/summary/hdfs/widgets.js
new file mode 100644
index 0000000..f937ef8
--- /dev/null
+++ b/ambari-web/app/views/main/service/info/summary/hdfs/widgets.js
@@ -0,0 +1,164 @@
+/**
+ * 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.
+ */
+
+const date = require('utils/date/date');
+const numberUtils = require('utils/number_utils');
+
+function diskPart(i18nKey, totalKey, usedKey) {
+  return Em.computed(totalKey, usedKey, function () {
+    const text = Em.I18n.t(i18nKey),
+      total = this.get(totalKey),
+      used = this.get(usedKey);
+    return text.format(numberUtils.bytesToSize(used, 1, 'parseFloat'), 
numberUtils.bytesToSize(total, 1, 'parseFloat'));
+  });
+}
+
+function diskPartPercent(i18nKey, totalKey, usedKey) {
+  return Em.computed(totalKey, usedKey, function () {
+    const text = Em.I18n.t(i18nKey),
+      total = this.get(totalKey),
+      used = this.get(usedKey);
+    let percent = total > 0 ? ((used * 100) / total).toFixed(2) : 0;
+    if (percent == 'NaN' || percent < 0) {
+      percent = Em.I18n.t('services.service.summary.notAvailable') + ' ';
+    }
+    return text.format(percent);
+  });
+}
+
+App.HDFSSummaryWidgetsView = Em.View.extend({
+
+  templateName: require('templates/main/service/info/summary/hdfs/widgets'),
+
+  nameSpace: 'default',
+
+  service: function () {
+    return App.HDFSService.find().objectAt(0);
+  }.property('controller.content.serviceName'),
+
+  componentGroup: Em.computed.findByKey('service.masterComponentGroups', 
'name', 'nameSpace'),
+
+  clusterId: Em.computed.alias('componentGroup.clusterId'),
+
+  nodeUptime: function () {
+    const uptime = 
this.get(`service.nameNodeStartTimeValues.${this.get('clusterId')}`);
+    if (uptime && uptime > 0) {
+      let diff = App.dateTime() - uptime;
+      if (diff < 0) {
+        diff = 0;
+      }
+      const formatted = date.timingFormat(diff);
+      return this.t('dashboard.services.uptime').format(formatted);
+    }
+    return this.t('services.service.summary.notRunning');
+  }.property('service.nameNodeStartTimeValues'),
+
+  jvmMemoryHeapUsed: Em.computed.getByKey('service.jvmMemoryHeapUsedValues', 
'clusterId'),
+
+  jvmMemoryHeapMax: Em.computed.getByKey('service.jvmMemoryHeapMaxValues', 
'clusterId'),
+
+  nodeHeapPercent: App.MainDashboardServiceView.formattedHeapPercent(
+    'dashboard.services.hdfs.nodes.heapUsedPercent', 'jvmMemoryHeapUsed', 
'jvmMemoryHeapMax'
+  ),
+
+  nodeHeap: App.MainDashboardServiceView.formattedHeap(
+    'dashboard.services.hdfs.nodes.heapUsed', 'jvmMemoryHeapUsed', 
'jvmMemoryHeapMax'
+  ),
+
+  capacityTotal: Em.computed.getByKey('service.capacityTotalValues', 
'clusterId'),
+
+  capacityUsed: Em.computed.getByKey('service.capacityUsedValues', 
'clusterId'),
+
+  capacityRemaining: Em.computed.getByKey('service.capacityRemainingValues', 
'clusterId'),
+
+  dfsUsedDiskPercent: 
diskPartPercent('dashboard.services.hdfs.capacityUsedPercent', 'capacityTotal', 
'capacityUsed'),
+
+  dfsUsedDisk: diskPart('dashboard.services.hdfs.capacityUsed', 
'capacityTotal', 'capacityUsed'),
+
+  nonDfsUsed: function () {
+    const total = this.get('capacityTotal'),
+      remaining = this.get('service.capacityRemaining'),
+      dfsUsed = this.get('service.capacityUsed');
+    return (Em.isNone(total) || Em.isNone(remaining) || Em.isNone(dfsUsed)) ? 
null : total - remaining - dfsUsed;
+  }.property('capacityTotal', 'capacityRemaining', 'capacityUsed'),
+
+  nonDfsUsedDiskPercent: 
diskPartPercent('dashboard.services.hdfs.capacityUsedPercent', 'capacityTotal', 
'nonDfsUsed'),
+
+  nonDfsUsedDisk: diskPart('dashboard.services.hdfs.capacityUsed', 
'capacityTotal', 'nonDfsUsed'),
+
+  remainingDiskPercent: diskPartPercent(
+    'dashboard.services.hdfs.capacityUsedPercent', 'capacityTotal', 
'capacityRemaining'
+  ),
+
+  remainingDisk: diskPart('dashboard.services.hdfs.capacityUsed', 
'capacityTotal', 'capacityRemaining'),
+
+  dfsTotalBlocksValue: Em.computed.getByKey('service.dfsTotalBlocksValues', 
'clusterId'),
+
+  dfsTotalBlocks: Em.computed.formatUnavailable('dfsTotalBlocksValue'),
+
+  dfsCorruptBlocksValue: Em.computed.getByKey('service.dfsTotalBlocksValues', 
'clusterId'),
+
+  dfsCorruptBlocks: Em.computed.formatUnavailable('dfsCorruptBlocksValue'),
+
+  dfsMissingBlocksValue: 
Em.computed.getByKey('service.dfsMissingBlocksValues', 'clusterId'),
+
+  dfsMissingBlocks: Em.computed.formatUnavailable('dfsMissingBlocksValue'),
+
+  dfsUnderReplicatedBlocksValue: 
Em.computed.getByKey('service.dfsUnderReplicatedBlocksValues', 'clusterId'),
+
+  dfsUnderReplicatedBlocks: 
Em.computed.formatUnavailable('dfsUnderReplicatedBlocksValue'),
+
+  dfsTotalFilesValue: Em.computed.getByKey('service.dfsTotalFilesValues', 
'clusterId'),
+
+  dfsTotalFiles: Em.computed.formatUnavailable('service.dfsTotalFilesValue'),
+
+  healthStatus: Em.computed.getByKey('service.healthStatusValues', 
'clusterId'),
+
+  upgradeStatusValue: Em.computed.getByKey('service.upgradeStatusValues', 
'clusterId'),
+
+  upgradeStatus: function () {
+    const upgradeStatus = this.get('upgradeStatusValue'),
+      healthStatus = this.get('healthStatus');
+    if (upgradeStatus == 'true') {
+      return 
Em.I18n.t('services.service.summary.pendingUpgradeStatus.notPending');
+    } else if (upgradeStatus == 'false' && healthStatus == 'green') {
+      return 
Em.I18n.t('services.service.summary.pendingUpgradeStatus.notFinalized');
+    } else {
+      // upgrade status == null
+      return Em.I18n.t('services.service.summary.notAvailable');
+    }
+  }.property('upgradeStatusValue', 'healthStatus'),
+
+  isUpgradeStatusWarning: function () {
+    return this.get('upgradeStatusValue') == 'false' && 
this.get('healthStatus') == 'green';
+  }.property('upgradeStatusValue', 'healthStatus'),
+
+  safeModeStatusValue: Em.computed.getByKey('service.safeModeStatusValues', 
'clusterId'),
+
+  safeModeStatus: function () {
+    const safeMode = this.get('safeModeStatusValue');
+    if (Em.isNone(safeMode)) {
+      return Em.I18n.t('services.service.summary.notAvailable');
+    } else if (safeMode.length === 0) {
+      return 
Em.I18n.t('services.service.summary.safeModeStatus.notInSafeMode');
+    } else {
+      return Em.I18n.t('services.service.summary.safeModeStatus.inSafeMode');
+    }
+  }.property('safeModeStatusValue')
+
+});
diff --git a/ambari-web/app/views/main/service/services/hdfs.js 
b/ambari-web/app/views/main/service/services/hdfs.js
index 862f59b..bce9690 100644
--- a/ambari-web/app/views/main/service/services/hdfs.js
+++ b/ambari-web/app/views/main/service/services/hdfs.js
@@ -17,50 +17,10 @@
 
 var App = require('app');
 var date = require('utils/date/date');
-var numberUtils = require('utils/number_utils');
-
-function diskPart(i18nKey, totalKey, usedKey) {
-  return Em.computed(totalKey, usedKey, function () {
-    var text = Em.I18n.t(i18nKey);
-    var total = this.get(totalKey);
-    var used = this.get(usedKey);
-    var percent = total > 0 ? ((used * 100) / total).toFixed(2) : 0;
-    if (percent == "NaN" || percent < 0) {
-      percent = Em.I18n.t('services.service.summary.notAvailable') + " ";
-    }
-    return text.format(numberUtils.bytesToSize(used, 1, 'parseFloat'), 
numberUtils.bytesToSize(total, 1, 'parseFloat'));
-  });
-}
-
-function diskPartPercent(i18nKey, totalKey, usedKey) {
-  return Em.computed(totalKey, usedKey, function () {
-    var text = Em.I18n.t(i18nKey);
-    var total = this.get(totalKey);
-    var used = this.get(usedKey);
-    var percent = total > 0 ? ((used * 100) / total).toFixed(2) : 0;
-    if (percent == "NaN" || percent < 0) {
-      percent = Em.I18n.t('services.service.summary.notAvailable') + " ";
-    }
-    return text.format(percent);
-  });
-}
 
 App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
   templateName: require('templates/main/service/services/hdfs'),
   serviceName: 'HDFS',
-  Chart: App.ChartPieView.extend({
-    service: null,
-    color: '#0066B3',
-    stroke: '#0066B3',
-    palette: new Rickshaw.Color.Palette({
-      scheme: ['rgba(0,102,179,1)', 'rgba(0,102,179,0)']
-    }),
-    data: function () {
-      var remaining = Number(this.get('service.capacityRemaining'));
-      var used = Number(this.get('service.capacityTotal')) - remaining;
-      return [ used, remaining ];
-    }.property('service.capacityUsed', 'service.capacityTotal')
-  }),
 
   metricsNotAvailableObserver: function () {
     if(!this.get("service.metricsNotAvailable")) {
@@ -80,48 +40,6 @@ App.MainDashboardServiceHdfsView = 
App.MainDashboardServiceView.extend({
 
   journalNodesTotal: Em.computed.alias('service.journalNodes.length'),
 
-  dfsTotalBlocks: Em.computed.formatUnavailable('service.dfsTotalBlocks'),
-
-  dfsTotalFiles: Em.computed.formatUnavailable('service.dfsTotalFiles'),
-
-  dfsCorruptBlocks: Em.computed.formatUnavailable('service.dfsCorruptBlocks'),
-
-  dfsMissingBlocks: Em.computed.formatUnavailable('service.dfsMissingBlocks'),
-
-  dfsUnderReplicatedBlocks: 
Em.computed.formatUnavailable('service.dfsUnderReplicatedBlocks'),
-
-  nodeUptime: function () {
-    var uptime = this.get('service.nameNodeStartTime');
-    if (uptime && uptime > 0){
-      var diff = App.dateTime() - uptime;
-      if (diff < 0) {
-        diff = 0;
-      }
-      var formatted = date.timingFormat(diff);
-      return this.t('dashboard.services.uptime').format(formatted);
-    }
-    return this.t('services.service.summary.notRunning');
-  }.property("service.nameNodeStartTime"),
-
-  nodeHeapPercent: 
App.MainDashboardServiceView.formattedHeapPercent('dashboard.services.hdfs.nodes.heapUsedPercent',
 'service.jvmMemoryHeapUsed', 'service.jvmMemoryHeapMax'),
-  nodeHeap: 
App.MainDashboardServiceView.formattedHeap('dashboard.services.hdfs.nodes.heapUsed',
 'service.jvmMemoryHeapUsed', 'service.jvmMemoryHeapMax'),
-
-  dfsUsedDisk: diskPart('dashboard.services.hdfs.capacityUsed', 
'service.capacityTotal', 'service.capacityUsed'),
-  dfsUsedDiskPercent: 
diskPartPercent('dashboard.services.hdfs.capacityUsedPercent', 
'service.capacityTotal', 'service.capacityUsed'),
-
-  nonDfsUsed: function () {
-    var total = this.get('service.capacityTotal');
-    var remaining = this.get('service.capacityRemaining');
-    var dfsUsed = this.get('service.capacityUsed');
-    return (Em.isNone(total) || Em.isNone(remaining) || Em.isNone(dfsUsed)) ? 
null : total - remaining - dfsUsed;
-  }.property('service.capacityTotal', 'service.capacityRemaining', 
'service.capacityUsed'),
-
-  nonDfsUsedDisk: diskPart('dashboard.services.hdfs.capacityUsed', 
'service.capacityTotal', 'nonDfsUsed'),
-  nonDfsUsedDiskPercent: 
diskPartPercent('dashboard.services.hdfs.capacityUsedPercent', 
'service.capacityTotal', 'nonDfsUsed'),
-
-  remainingDisk: diskPart('dashboard.services.hdfs.capacityUsed', 
'service.capacityTotal', 'service.capacityRemaining'),
-  remainingDiskPercent: 
diskPartPercent('dashboard.services.hdfs.capacityUsedPercent', 
'service.capacityTotal', 'service.capacityRemaining'),
-
   dataNodeComponent: Em.Object.create({
     componentName: 'DATANODE'
   }),
@@ -142,43 +60,6 @@ App.MainDashboardServiceHdfsView = 
App.MainDashboardServiceView.extend({
     componentName: 'JOURNALNODE'
   }),
 
-  /**
-   * @type {string}
-   */
-  safeModeStatus: function () {
-    var safeMode = this.get('service.safeModeStatus');
-    if (Em.isNone(safeMode)) {
-      return Em.I18n.t("services.service.summary.notAvailable");
-    } else if (safeMode.length === 0) {
-      return 
Em.I18n.t("services.service.summary.safeModeStatus.notInSafeMode");
-    } else {
-      return Em.I18n.t("services.service.summary.safeModeStatus.inSafeMode");
-    }
-  }.property('service.safeModeStatus'),
-
-  /**
-   * @type {string}
-   */
-  upgradeStatus: function () {
-    var upgradeStatus = this.get('service.upgradeStatus');
-    var healthStatus = this.get('service.healthStatus');
-    if (upgradeStatus == 'true') {
-      return 
Em.I18n.t('services.service.summary.pendingUpgradeStatus.notPending');
-    } else if (upgradeStatus == 'false' && healthStatus == 'green') {
-      return 
Em.I18n.t('services.service.summary.pendingUpgradeStatus.notFinalized');
-    } else {
-      // upgrade status == null
-      return Em.I18n.t("services.service.summary.notAvailable");
-    }
-  }.property('service.upgradeStatus', 'service.healthStatus'),
-
-  /**
-   * @type {boolean}
-   */
-  isUpgradeStatusWarning: function () {
-    return this.get('service.upgradeStatus') == 'false' && 
this.get('service.healthStatus') == 'green';
-  }.property('service.upgradeStatus', 'service.healthStatus'),
-
   isDataNodeCreated: function () {
     return this.isServiceComponentCreated('DATANODE');
   }.property('App.router.clusterController.isComponentsStateLoaded'),
diff --git a/ambari-web/test/views/common/chart/linear_time_test.js 
b/ambari-web/test/views/common/chart/linear_time_test.js
index 7173a2a..e975677 100644
--- a/ambari-web/test/views/common/chart/linear_time_test.js
+++ b/ambari-web/test/views/common/chart/linear_time_test.js
@@ -257,7 +257,6 @@ describe('App.ChartLinearTimeView', function () {
         fromSeconds: -3599,
         stepSeconds: 15,
         hostName: 'host1',
-        nameNodeName: '',
         resourceManager: ''
       });
     });
@@ -272,7 +271,6 @@ describe('App.ChartLinearTimeView', function () {
         fromSeconds: -3599,
         stepSeconds: 15,
         hostName: '',
-        nameNodeName: 'host1',
         resourceManager: ''
       });
       services.hdfsService = [];
@@ -288,7 +286,6 @@ describe('App.ChartLinearTimeView', function () {
         fromSeconds: -3599,
         stepSeconds: 15,
         hostName: '',
-        nameNodeName: 'host1',
         resourceManager: ''
       });
       services.hdfsService = [];
@@ -304,7 +301,6 @@ describe('App.ChartLinearTimeView', function () {
         fromSeconds: -3599,
         stepSeconds: 15,
         hostName: '',
-        nameNodeName: '',
         resourceManager: 'host1'
       });
       services.yarnService = [];
diff --git a/ambari-web/test/views/main/dashboard/widget_test.js 
b/ambari-web/test/views/main/dashboard/widget_test.js
index 8e10cef..b45f88d 100644
--- a/ambari-web/test/views/main/dashboard/widget_test.js
+++ b/ambari-web/test/views/main/dashboard/widget_test.js
@@ -150,7 +150,8 @@ describe('App.DashboardWidgetView', function () {
       expect(view.get('parentView.userPreferences')).to.be.eql({
         visible: [],
         hidden: [1],
-        threshold: []
+        threshold: [],
+        groups: {}
       });
     });
 
diff --git a/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js 
b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
index 15b7503..73fcd53 100644
--- a/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
+++ b/ambari-web/test/views/main/dashboard/widgets/namenode_rpc_test.js
@@ -32,7 +32,9 @@ describe('App.NameNodeRpcView', function() {
   var tests = [
     {
       model: {
-        nameNodeRpc: 1
+        nameNodeRpcValues: {
+          c: 1
+        }
       },
       e: {
         isNA: false,
@@ -42,7 +44,9 @@ describe('App.NameNodeRpcView', function() {
     },
     {
       model: {
-        nameNodeRpc: 10
+        nameNodeRpcValues: {
+          c: 10
+        }
       },
       e: {
         isNA: false,
@@ -52,7 +56,9 @@ describe('App.NameNodeRpcView', function() {
     },
     {
       model: {
-        nameNodeRpc: 0
+        nameNodeRpcValues: {
+          c: 0
+        }
       },
       e: {
         isNA: false,
@@ -62,7 +68,9 @@ describe('App.NameNodeRpcView', function() {
     },
     {
       model: {
-        nameNodeRpc: null
+        nameNodeRpcValues: {
+          c: null
+        }
       },
       e: {
         isNA: true,
@@ -74,7 +82,10 @@ describe('App.NameNodeRpcView', function() {
 
   tests.forEach(function(test) {
     describe('nameNodeRpc - ' + test.model.nameNodeRpc, function() {
-      var jobTrackerRpcView = App.NameNodeRpcView.create({model: test.model});
+      var jobTrackerRpcView = App.NameNodeRpcView.create({
+        clusterId: 'c',
+        model: test.model
+      });
       it('content', function() {
         expect(jobTrackerRpcView.get('content')).to.equal(test.e.content);
       });
diff --git 
a/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js 
b/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js
index d77e6ab..42f86ea 100644
--- a/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js
+++ b/ambari-web/test/views/main/dashboard/widgets/pie_chart_widget_test.js
@@ -32,8 +32,8 @@ describe('App.PieChartDashboardWidgetView', function() {
   var pieChartDashboardWidgetView = App.PieChartDashboardWidgetView.create({
     model_type: null,
     model: model,
-    modelFieldUsed: 'used',
-    modelFieldMax: 'max',
+    modelValueUsed: Em.computed.alias('model.used'),
+    modelValueMax: Em.computed.alias('model.max'),
     widgetHtmlId: 'fake'
   });
 
diff --git 
a/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js 
b/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js
index 2c7f540..b4249cf 100644
--- a/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js
+++ b/ambari-web/test/views/main/dashboard/widgets/uptime_text_widget_test.js
@@ -29,7 +29,7 @@ describe('App.UptimeTextDashboardWidgetView', function() {
     uptimeTextDashboardWidgetView = App.UptimeTextDashboardWidgetView.create({
       thresholdMin:40,
       thresholdMax:70,
-      modelField: 'field',
+      modelValue: Em.computed.alias('model.field'),
       model: Em.Object.create({})
     });
   });
diff --git a/ambari-web/test/views/main/dashboard/widgets_test.js 
b/ambari-web/test/views/main/dashboard/widgets_test.js
index 08269b5..54931a1 100644
--- a/ambari-web/test/views/main/dashboard/widgets_test.js
+++ b/ambari-web/test/views/main/dashboard/widgets_test.js
@@ -32,7 +32,10 @@ describe('App.MainDashboardWidgetsView', function () {
       getUserPref: Em.K,
       postUserPref: Em.K,
       setDBProperty: Em.K,
-      persistKey: 'key'
+      persistKey: 'key',
+      widgetGroupsDeferred: {
+        done: Em.clb
+      }
     });
   });
 
@@ -135,7 +138,8 @@ describe('App.MainDashboardWidgetsView', function () {
       var expectedUserSettings = {
         visible: [1, 2],
         hidden: [3, 4],
-        threshold: {}
+        threshold: {},
+        groups: {}
       };
 
       beforeEach(function () {
@@ -248,7 +252,8 @@ describe('App.MainDashboardWidgetsView', function () {
         "threshold": {
           "2": [],
           "3": [1,2]
-        }
+        },
+        "groups": {}
       }));
       expect(view.resolveConfigDependencies).to.be.calledOnce;
     });
@@ -335,7 +340,8 @@ describe('App.MainDashboardWidgetsView', function () {
     beforeEach(function() {
       sinon.stub(view, 'generateDefaultUserPreferences').returns({
         visible: [1, 2],
-        hidden: [3, 4]
+        hidden: [3, 4],
+        groups: {}
       });
       sinon.stub(view, 'saveWidgetsSettings');
     });
@@ -355,7 +361,8 @@ describe('App.MainDashboardWidgetsView', function () {
       expect(view.saveWidgetsSettings.getCall(0).args[0]).to.be.eql({
         visible: [3, 2],
         hidden: [1, 4],
-        threshold: {}
+        threshold: {},
+        groups: {}
       });
     });
   });
diff --git a/ambari-web/test/views/main/service/services/hdfs_test.js 
b/ambari-web/test/views/main/service/services/hdfs_test.js
index c67d30a..21fefb9 100644
--- a/ambari-web/test/views/main/service/services/hdfs_test.js
+++ b/ambari-web/test/views/main/service/services/hdfs_test.js
@@ -35,26 +35,6 @@ describe('App.MainDashboardServiceHdfsView', function () {
 
   App.TestAliases.testAsComputedAlias(getView(), 'journalNodesTotal', 
'service.journalNodes.length', 'number');
 
-  describe("#Chart", function() {
-    var chartView;
-
-    beforeEach(function() {
-      chartView = view.get('Chart').create();
-    });
-
-    describe("#data", function () {
-
-      it("should return data", function () {
-        chartView.set('service', Em.Object.create({
-          capacityTotal: 100,
-          capacityRemaining: 1
-        }));
-        chartView.propertyDidChange('data');
-        expect(chartView.get('data')).to.be.eql([99, 1]);
-      });
-    });
-  });
-
   describe("#metricsNotAvailableObserver()", function() {
 
     beforeEach(function() {
@@ -106,89 +86,6 @@ describe('App.MainDashboardServiceHdfsView', function () {
     });
   });
 
-  describe("#nodeUptime", function() {
-
-    beforeEach(function() {
-      sinon.stub(App, 'dateTime').returns(10);
-      sinon.stub(date, 'timingFormat').returns('11');
-    });
-    afterEach(function() {
-      App.dateTime.restore();
-      date.timingFormat.restore();
-    });
-
-    it("nameNodeStartTime is 0", function() {
-      view.set('service.nameNodeStartTime', 0);
-      view.propertyDidChange('nodeUptime');
-      
expect(view.get('nodeUptime')).to.be.equal(view.t('services.service.summary.notRunning'));
-    });
-
-    it("nameNodeStartTime is -1", function() {
-      view.set('service.nameNodeStartTime', -1);
-      view.propertyDidChange('nodeUptime');
-      
expect(view.get('nodeUptime')).to.be.equal(view.t('services.service.summary.notRunning'));
-    });
-
-    it("nameNodeStartTime is 1", function() {
-      view.set('service.nameNodeStartTime', 1);
-      view.propertyDidChange('nodeUptime');
-      
expect(view.get('nodeUptime')).to.be.equal(view.t('dashboard.services.uptime').format('11'));
-      expect(date.timingFormat.calledWith(9)).to.be.true;
-    });
-
-    it("nameNodeStartTime is 11", function() {
-      view.set('service.nameNodeStartTime', 11);
-      view.propertyDidChange('nodeUptime');
-      
expect(view.get('nodeUptime')).to.be.equal(view.t('dashboard.services.uptime').format('11'));
-      expect(date.timingFormat.calledWith(0)).to.be.true;
-    });
-  });
-
-  describe("#nonDfsUsed", function() {
-    var testCases = [
-      {
-        input: {
-          capacityTotal: null,
-          capacityRemaining: 1,
-          capacityUsed: 90
-        },
-        expected: null
-      },
-      {
-        input: {
-          capacityTotal: 100,
-          capacityRemaining: null,
-          capacityUsed: 90
-        },
-        expected: null
-      },
-      {
-        input: {
-          capacityTotal: 100,
-          capacityRemaining: 1,
-          capacityUsed: null
-        },
-        expected: null
-      },
-      {
-        input: {
-          capacityTotal: 100,
-          capacityRemaining: 1,
-          capacityUsed: 90
-        },
-        expected: 9
-      }
-    ];
-
-    testCases.forEach(function(test) {
-      it("total=" + test.input.capacityTotal + " remaining" + 
test.input.capacityRemaining + " used" + test.input.capacityUsed, function() {
-        view.get('service').setProperties(test.input);
-        view.propertyDidChange('nonDfsUsed');
-        expect(view.get('nonDfsUsed')).to.be.equal(test.expected);
-      });
-    });
-  });
-
   describe("#isNfsInStack", function() {
 
     beforeEach(function() {
@@ -211,72 +108,4 @@ describe('App.MainDashboardServiceHdfsView', function () {
     });
   });
 
-  describe("#safeModeStatus", function() {
-
-    it("safeModeStatus is null", function() {
-      view.set('service.safeModeStatus', null);
-      view.propertyDidChange('safeModeStatus');
-      
expect(view.get('safeModeStatus')).to.be.equal(Em.I18n.t("services.service.summary.notAvailable"));
-    });
-
-    it("safeModeStatus is empty", function() {
-      view.set('service.safeModeStatus', "");
-      view.propertyDidChange('safeModeStatus');
-      
expect(view.get('safeModeStatus')).to.be.equal(Em.I18n.t("services.service.summary.safeModeStatus.notInSafeMode"));
-    });
-
-    it("safeModeStatus is on", function() {
-      view.set('service.safeModeStatus', 'on');
-      view.propertyDidChange('safeModeStatus');
-      
expect(view.get('safeModeStatus')).to.be.equal(Em.I18n.t("services.service.summary.safeModeStatus.inSafeMode"));
-    });
-  });
-
-  describe("#upgradeStatus", function() {
-
-    it("upgradeStatus is 'true'", function() {
-      view.set('service.upgradeStatus', 'true');
-      view.propertyDidChange('upgradeStatus');
-      
expect(view.get('upgradeStatus')).to.be.equal(Em.I18n.t('services.service.summary.pendingUpgradeStatus.notPending'));
-    });
-
-    it("upgradeStatus is 'false', healthStatus is 'green'", function() {
-      view.set('service.upgradeStatus', 'false');
-      view.set('service.healthStatus', 'green');
-      view.propertyDidChange('upgradeStatus');
-      
expect(view.get('upgradeStatus')).to.be.equal(Em.I18n.t('services.service.summary.pendingUpgradeStatus.notFinalized'));
-    });
-
-    it("upgradeStatus is null", function() {
-      view.set('service.upgradeStatus', null);
-      view.propertyDidChange('upgradeStatus');
-      
expect(view.get('upgradeStatus')).to.be.equal(Em.I18n.t('services.service.summary.notAvailable'));
-    });
-  });
-
-  describe("#isUpgradeStatusWarning", function() {
-
-    it("upgradeStatus is 'false', healthStatus is 'green'", function() {
-      view.set('service.upgradeStatus', 'false');
-      view.set('service.healthStatus', 'green');
-      view.propertyDidChange('isUpgradeStatusWarning');
-      expect(view.get('isUpgradeStatusWarning')).to.be.true;
-    });
-
-    it("upgradeStatus is 'true', healthStatus is 'green'", function() {
-      view.set('service.upgradeStatus', 'true');
-      view.set('service.healthStatus', 'green');
-      view.propertyDidChange('isUpgradeStatusWarning');
-      expect(view.get('isUpgradeStatusWarning')).to.be.false;
-    });
-
-    it("upgradeStatus is 'false', healthStatus is 'red'", function() {
-      view.set('service.upgradeStatus', 'false');
-      view.set('service.healthStatus', 'red');
-      view.propertyDidChange('isUpgradeStatusWarning');
-      expect(view.get('isUpgradeStatusWarning')).to.be.false;
-    });
-  });
-
-
 });

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to