Преглед на файлове

AMBARI-884. Implement Dashboard/Service Summary. (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1399495 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako преди 12 години
родител
ревизия
ec3c47fdd4
променени са 35 файла, в които са добавени 1493 реда и са изтрити 190 реда
  1. 2 0
      AMBARI-666-CHANGES.txt
  2. 1 1
      ambari-web/app/assets/data/services/summary/hdfs.json
  3. 28 12
      ambari-web/app/controllers/installer/step7_controller.js
  4. 56 1
      ambari-web/app/messages.js
  5. 5 0
      ambari-web/app/models/alert.js
  6. 10 3
      ambari-web/app/models/service.js
  7. 14 17
      ambari-web/app/routes/main.js
  8. 146 25
      ambari-web/app/styles/application.less
  9. 0 1
      ambari-web/app/templates/installer/slave_group_hosts.hbs
  10. 23 5
      ambari-web/app/templates/installer/step7.hbs
  11. 7 40
      ambari-web/app/templates/main/dashboard.hbs
  12. 89 0
      ambari-web/app/templates/main/dashboard/service/hbase.hbs
  13. 100 0
      ambari-web/app/templates/main/dashboard/service/hdfs.hbs
  14. 36 0
      ambari-web/app/templates/main/dashboard/service/hive.hbs
  15. 100 0
      ambari-web/app/templates/main/dashboard/service/mapreduce.hbs
  16. 36 0
      ambari-web/app/templates/main/dashboard/service/oozie.hbs
  17. 36 0
      ambari-web/app/templates/main/dashboard/service/zookeeper.hbs
  18. 167 60
      ambari-web/app/templates/main/service/info/summary.hbs
  19. 39 1
      ambari-web/app/utils/helper.js
  20. 10 1
      ambari-web/app/views.js
  21. 1 1
      ambari-web/app/views/common/chart.js
  22. 70 0
      ambari-web/app/views/common/chart/linear.js
  23. 70 0
      ambari-web/app/views/common/chart/pie.js
  24. 7 7
      ambari-web/app/views/installer/step7_view.js
  25. 102 0
      ambari-web/app/views/main/dashboard/service.js
  26. 60 0
      ambari-web/app/views/main/dashboard/service/hbase.js
  27. 72 0
      ambari-web/app/views/main/dashboard/service/hdfs.js
  28. 24 0
      ambari-web/app/views/main/dashboard/service/hive.js
  29. 62 0
      ambari-web/app/views/main/dashboard/service/mapreduce.js
  30. 25 0
      ambari-web/app/views/main/dashboard/service/oozie.js
  31. 24 0
      ambari-web/app/views/main/dashboard/service/zookeeper.js
  32. 25 10
      ambari-web/app/views/main/menu.js
  33. 43 4
      ambari-web/app/views/main/service/info/summary.js
  34. 1 0
      ambari-web/config.coffee
  35. 2 1
      ambari-web/package.json

+ 2 - 0
AMBARI-666-CHANGES.txt

@@ -12,6 +12,8 @@ AMBARI-666 branch (unreleased changes)
 
   NEW FEATURES
 
+  AMBARI-884. Implement Dashboard/Service summary. (yusaku)
+
   AMBARI-882. Group-based DataNode/TaskTracker/RegionServer overrides.
   (yusaku)
 

+ 1 - 1
ambari-web/app/assets/data/services/summary/hdfs.json

@@ -21,7 +21,7 @@
     "safemode": false,
     "pending_upgrades": false,
     "dfs_configured_capacity": 885570207744,
-    "dfs_percent_used": 4.91,
+    "dfs_percent_used": 0.01,
     "dfs_percent_remaining": 95.09,
     "dfs_total_bytes": 885570207744,
     "dfs_used_bytes": 104898560,

+ 28 - 12
ambari-web/app/controllers/installer/step7_controller.js

@@ -232,7 +232,7 @@ App.SlaveComponentGroupsController = Ember.ArrayController.extend({
       component.newGroupIndex++;
       newGroupName = 'New Group ' + component.newGroupIndex;
     }
-    var newGroup = {groupName: newGroupName, label: 'new_group_' + component.newGroupIndex, type: 'new', active: true};
+    var newGroup = {name: newGroupName, index: component.newGroupIndex, type: 'new', active: true};
     component.groups.pushObject(newGroup);
   },
 
@@ -258,13 +258,13 @@ App.SlaveComponentGroupsController = Ember.ArrayController.extend({
     }
   }.property('@each.hosts', 'selectedComponentName'),
 
-  getGroups: function () {
+  componentGroups: function () {
     if (this.get('selectedComponentName') !== null) {
       var component = this.findProperty('componentName', this.get('selectedComponentName'));
       if (component !== undefined && component !== null) {
         if (component.groups === undefined){
           component.groups = [];
-          var defaultGroup = {groupName: 'Default', label: 'default', type: 'default', active: true};
+          var defaultGroup = {name: 'Default', index: 'default', type: 'default', active: true};
           component.groups.pushObject(defaultGroup);
         }
         return component.groups;
@@ -272,24 +272,40 @@ App.SlaveComponentGroupsController = Ember.ArrayController.extend({
     }
   }.property('selectedComponentName'),
 
+//  activeGroup: function(){
+//    return this.get('componentGroups').findProperty('active', true);
+//  }.property('selectedComponentName', 'componentGroups.@each'),
+
   showSlaveComponentGroup: function(event){
     var component = this.findProperty('componentName', this.get('selectedComponentName'));
     component.groups.setEach('active', false);
-    var group = component.groups.filterProperty('groupName', event.context.groupName);
+    var group = component.groups.filterProperty('name', event.context.name);
     group.setEach('active', true);
   },
 
   removeSlaveComponentGroup: function(event){
+    var group = event.context;
     var component = this.findProperty('componentName', this.get('selectedComponentName'));
-    component.groups.setEach('active', false);
-//    component.groups.forEach(function (group) {
-//      if(group.groupName == event.context.groupName)
-//        delete component.groups[i];
-//    }, this);
+    var assignedHosts = component.hosts.filterProperty('group', group.name);
+    if (assignedHosts.length !== 0){
+      $('.remove-group-error').show();
+    } else {
+      $('.remove-group-error').hide();
+      var key = component.groups.indexOf(group);
+      component.groups.removeObject(component.groups[key]);
+//      $('#slave-group'+ group.index).remove();
 
-    var group = component.groups.filterProperty('groupName', event.context.groupName);
-    component.groups.removeObjects(group);
-    group.setEach('active', true);
+      if(group.type === 'new' && component.newGroupIndex === group.index){
+        component.newGroupIndex--;
+      }
+      if (group.active){
+        var lastGroup;
+        if (key === component.groups.length)
+          lastGroup = component.groups.slice(key-1, key);
+        else lastGroup = component.groups.slice(key, key+1);
+        lastGroup.setEach('active', true);
+      }
+    }
   }
 
 });

+ 56 - 1
ambari-web/app/messages.js

@@ -202,6 +202,25 @@ Em.I18n.translations = {
   'services.service.summary.blocksTotal': 'Blocks (total)',
   'services.service.summary.blockErrors': 'Block Errors (corr./miss./underrep.)',
   'services.service.summary.totalFiles': 'Total Files + Directory Count',
+  'services.service.summary.jobTracker': 'Job Tracker',
+  'services.service.summary.jobTrackerUptime': 'Job Tracker Uptime',
+  'services.service.summary.trackersLiveTotal': 'Trackers (live/total)',
+  'services.service.summary.trackersBlacklistGraylist': 'Trackers (blacklist/graylist/excl.)',
+  'services.service.summary.jobTrackerHeap': 'Job Tracker Heap (used/max)',
+  'services.service.summary.totalSlotsCapacity': 'Total Slots Capacity (maps/reduces/avg per node)',
+  'services.service.summary.totalJobs': 'Total Jobs (submitted/completed)',
+  'services.service.summary.currentSlotUtiliMaps': 'Current Slot Utilization: Maps (occupied/reserved)',
+  'services.service.summary.currentSlotUtiliReduces': 'Current Slot Utilization: Reduces (occupied/reserved)',
+  'services.service.summary.tasksMaps': 'Tasks: Maps (running/waiting)',
+  'services.service.summary.tasksReduces': 'Tasks: Reduces (running/waiting)',
+  'services.service.summary.hbaseMaster': 'HBase Mastre',
+  'services.service.summary.regionServerCount': 'Region Server Count (live/dead)',
+  'services.service.summary.regionInTransition': 'Region In Transition',
+  'services.service.summary.masterStarted': 'Master Started',
+  'services.service.summary.masterActivated': 'Master Activated',
+  'services.service.summary.averageLoad': 'Average Load (regions per regionServer)',
+  'services.service.summary.masterHeap': 'Master Heap (used/max)',
+  'services.service.summary.moreStats': 'more stats here',
 
   'hosts.host.start.popup.header': 'Confirmation',
   'hosts.host.stop.popup.header': 'Confirmation',
@@ -226,6 +245,42 @@ Em.I18n.translations = {
   'metric.memory': 'disk used',
   'metric.network': 'network',
   'metric.io': 'io',
+
   'hosts.add.header' : 'Add Host Wizard',
-  'hosts.add.step2.warning' : 'Hosts are already part of the cluster and will be ignored'
+  'hosts.add.step2.warning' : 'Hosts are already part of the cluster and will be ignored',
+
+  'dashboard.services': 'Services',
+  'dashboard.services.hosts': 'Hosts',
+  'dashboard.services.uptime': '{0} days {1} hrs {2} mins',
+  'dashboard.services.hdfs.summary': '{0} of {1} nodes live, {2}% capacity free',
+  'dashboard.services.hdfs.capacity': 'HDFS Capacity',
+  'dashboard.services.hdfs.capacityUsed': '{0} of {1} ({2}% used)',
+  'dashboard.services.hdfs.totalFilesAndDirs': 'Total Files + Directories',
+  'dashboard.services.hdfs.nodes': 'Data Nodes',
+  'dashboard.services.hdfs.nodes.live':'live',
+  'dashboard.services.hdfs.nodes.dead':'dead',
+  'dashboard.services.hdfs.nodes.decom':'decom',
+  'dashboard.services.hdfs.nodes.uptime': 'NameNode Uptime',
+  'dashboard.services.hdfs.nodes.heap': 'NameNode Heap',
+  'dashboard.services.hdfs.nodes.heapUsed': '{0} of {1}, {2}% used',
+  'dashboard.services.hdfs.chart.label': 'Capacity (Free/Used)',
+
+  'dashboard.services.mapreduce.summary': '{0} of {1} trackers live, {2} jobs running',
+  'dashboard.services.mapreduce.trackers': 'Trackers',
+  'dashboard.services.mapreduce.trackersSummary': '{0} live / {1} total',
+  'dashboard.services.mapreduce.jobs': 'Jobs',
+  'dashboard.services.mapreduce.jobsSummary': '{0} running / {1} completed / {2} failed',
+  'dashboard.services.mapreduce.mapSlots': 'Map Slots',
+  'dashboard.services.mapreduce.reduceSlots': 'Reduce Slots',
+  'dashboard.services.mapreduce.jobTrackerHeap': 'JobTracker Heap',
+  'dashboard.services.mapreduce.jobTrackerUptime': 'Job Trackers Uptime',
+  'dashboard.services.mapreduce.chart.label': 'Jobs Running',
+
+  'dashboard.services.hbase.summary': '{0} of {1} region servers up, {2} average load',
+  'dashboard.services.hbase.masterServerHeap': 'Master Server Heap',
+  'dashboard.services.hbase.masterServerUptime': 'Master Server Uptime',
+  'dashboard.services.hbase.averageLoad': 'Average Load',
+  'dashboard.services.hbase.regionServers': 'Region Servers',
+  'dashboard.services.hbase.regionServersSummary': '{0} live / {1} total',
+  'dashboard.services.hbase.chart.label': 'Request Count'
 };

+ 5 - 0
ambari-web/app/models/alert.js

@@ -18,6 +18,11 @@
 
 var App = require('app');
 
+App.AlertStatus = {
+  negative: 'corrupt',
+  positive: 'ok'
+}
+
 App.Alert = DS.Model.extend({
   title: DS.attr('string'),
   service: DS.belongsTo('App.Service'),

+ 10 - 3
ambari-web/app/models/service.js

@@ -19,7 +19,7 @@
 
 var App = require('app');
 
-App.ServiceInfo = Ember.Object.extend({
+App.ServiceInfo = Em.Object.extend({
   elementId: 'service',
   serviceName: '',
   displayName: '',
@@ -142,6 +142,13 @@ App.Service = DS.Model.extend({
   quickLinks: DS.hasMany('App.QuickLinks')
 });
 
+App.Service.Health = {
+  live: "LIVE",
+  dead: "DEAD",
+  start: "STARTING",
+  stop: "STOPPING"
+}
+
 App.Service.FIXTURES = [
   {
     id:1,
@@ -160,7 +167,7 @@ App.Service.FIXTURES = [
     label:'MapReduce',
     components: [4, 5],
     service_audit: [4, 5, 6],
-    health_status: 'LIVE',
+    health_status: 'STARTING',
     work_status: true,
     alerts: [3, 4],
     quick_links: [5, 6, 7, 8, 9, 10]
@@ -179,7 +186,7 @@ App.Service.FIXTURES = [
     id:4,
     service_name:'zookeeper',
     label:'Zookeeper',
-    health_status: 'DEAD',
+    health_status: 'STOPPING',
     work_status: false,
     alerts: [7, 8]
   },

+ 14 - 17
ambari-web/app/routes/main.js

@@ -53,19 +53,19 @@ module.exports = Em.Route.extend({
         router.transitionTo('heatmap');
       });
     },
-    index: Ember.Route.extend({
-      route: '/',
-      redirectsTo: 'heatmap'
+    index:Ember.Route.extend({
+      route:'/',
+      redirectsTo:'heatmap'
     }),
-    heatmap: Em.Route.extend({
+    heatmap:Em.Route.extend({
       route:'/heatmap',
-      connectOutlets: function(router, context) {
+      connectOutlets:function (router, context) {
         router.get('mainChartsController').connectOutlet('mainChartsHeatmap');
       }
     }),
-    horizon_chart: Em.Route.extend({
+    horizon_chart:Em.Route.extend({
       route:'/horizon_chart',
-      connectOutlets: function(router, context) {
+      connectOutlets:function (router, context) {
         router.get('mainChartsController').connectOutlet('mainChartsHorizon');
       }
     }),
@@ -88,13 +88,13 @@ module.exports = Em.Route.extend({
       router.transitionTo('hostDetails.index', event.context)
     },
 
-    addHost: function (router) {
+    addHost:function (router) {
       router.transitionTo('hostAdd');
     }
 
   }),
 
-  hostAdd: require('routes/add_host_routes'),
+  hostAdd:require('routes/add_host_routes'),
 
   hostDetails:Em.Route.extend({
     route:'/hosts/:host_id',
@@ -143,8 +143,8 @@ module.exports = Em.Route.extend({
   admin:Em.Route.extend({
     route:'/admin',
 
-    enter: function(router) {
-      if(router.get('currentState.name') != 'main') { // is user comes from main -> navigate to users
+    enter:function (router) {
+      if (router.get('currentState.name') != 'main') { // is user comes from main -> navigate to users
         Em.run.next(function () {
           router.transitionTo('adminUser');
         });
@@ -167,7 +167,9 @@ module.exports = Em.Route.extend({
       // events
       gotoUsers:Em.Router.transitionTo("allUsers"),
       gotoCreateUser:Em.Router.transitionTo("createUser"),
-      gotoEditUser:function (router, event) { router.transitionTo("editUser", event.context) },
+      gotoEditUser:function (router, event) {
+        router.transitionTo("editUser", event.context)
+      },
 
       // states
       allUsers:Em.Route.extend({
@@ -313,10 +315,5 @@ module.exports = Em.Route.extend({
   filterHosts:function (router, component) {
     router.get('mainHostController').filterByComponent(component.context);
     router.transitionTo('hosts');
-  },
-  navigate:function (router, event) {
-    var parent = event.view._parentView;
-    parent.deactivateChildViews(event.context);
-    router.transitionTo(event.context.routing);
   }
 });

+ 146 - 25
ambari-web/app/styles/application.less

@@ -231,15 +231,31 @@ h1 {
     }
     .slave-component-group-menu {
       float: left;
+      .nav{
+        margin-bottom: 10px;
+      }
+      .nav li{
+        position: relative;
+        a{
+          padding-right: 24px;
+        }
+        i {
+          border: 1px solid white;
+          position: absolute;
+          right: 7px;
+          top: 10px;
+          z-index: 2;
+        }
+        i:hover {
+          border: 1px solid grey;
+        }
+      }
     }
-    .add-slave-component-group {
-      margin-bottom: 20px;
-    }
-    .slave-group {
+    .remove-group-error{
       display: none;
     }
-    .slave-group.active {
-      display: block;
+    .add-slave-component-group {
+      margin-bottom: 10px;
     }
     .master-host, .master-hosts, .slave-hosts {
       padding-top: 5px;
@@ -288,19 +304,19 @@ a:focus {
   outline: none;
 }
 
-@status-live-marker: url("../img/health-status-live.png");
-@status-dead-marker: url("../img/health-status-dead.png");
-@status-ok-marker: url("../img/status-ok.jpg");
-@status-corrupt-marker: url("../img/status-corrupt.jpg");
-@arrow-right: url("../img/arrow-right.png");
+@status-live-marker: url("/img/health-status-live.png");
+@status-dead-marker: url("/img/health-status-dead.png");
+@status-ok-marker: url("/img/status-ok.jpg");
+@status-corrupt-marker: url("/img/status-corrupt.jpg");
+@arrow-right: url("/img/arrow-right.png");
 
 /*Rack images*/
-@rack-status-live: url("../img/rack-status-live.png");
-@rack-status-critical: url("../img/rack-status-critical.png");
-@rack-status-dead: url("../img/rack-status-dead.png");
-@rack-state-toggler: url("../img/rack-state-toggler.png");
-@rack-state-plus: url("../img/rack-state-plus.png");
-@rack-state-minus: url("../img/rack-state-minus.png");
+@rack-status-live: url("/img/rack-status-live.png");
+@rack-status-critical: url("/img/rack-status-critical.png");
+@rack-status-dead: url("/img/rack-status-dead.png");
+@rack-state-toggler: url("/img/rack-state-toggler.png");
+@rack-state-plus: url("/img/rack-state-plus.png");
+@rack-state-minus: url("/img/rack-state-minus.png");
 
 /*****start styles for boxes*****/
 .box {
@@ -345,12 +361,17 @@ a:focus {
 
 /*start services summary*/
 .services {
-  margin-left: 5px;
+  margin-left: 0;
+  margin-top: 0;
+  position: relative;
+
   .tab-marker-position {
-    padding-left: 25px;
     background-position: 6px 5px;
     background-repeat: no-repeat;
     list-style: none;
+    min-height: 20px;
+    min-width: 20px;
+    margin-left: 0;
   }
   .health-status-LIVE {
     .tab-marker-position;
@@ -367,9 +388,53 @@ a:focus {
   dd {
     margin-left: 145px;
   }
+
+  .service {
+    position:relative;
+    margin-top: 10px;
+    border-bottom: 1px solid #b8b8b8;
+    padding-left: 10px;
+    width: 540px;
+
+    .name {
+      line-height: 21px;
+      margin-left: 0;
+      width: 130px;
+      a {
+        text-decoration: underline;
+        margin-left: 5px
+      }
+    }
+    .summary {
+      line-height: 21px;
+    }
+
+    table.table {
+      margin-top: 14px;
+      color: #7b7b7b;
+      font-size: 12px;
+      width: 410px;
+      th, td {
+        line-height: 8px !important;
+      }
+    }
+
+    .chart {
+      right: 0;
+      top: 5px;
+      position: absolute;
+      overflow: visible; // for quick links
+      text-align: center;
+
+      .chartLabel {
+        font-size: 9px;
+        color: #7b7b7b;
+      }
+    }
+  }
 }
 #summary-info {
-  margin-top: 20px;
+  margin: 10px 0;
 /*
   tr td:first-child {
     font-weight: bold;
@@ -383,6 +448,18 @@ a:focus {
     }
   }
 }
+.more-stats {
+  display: block;
+  width: 100%;
+  padding: 7px 0;
+  text-align: center;
+  color: #333333;
+  &:hover {
+    background-color: #eee;
+    color: #333;
+    text-decoration: none;
+  }
+}
 
 /*end services summary*/
 
@@ -448,6 +525,7 @@ a:focus {
     margin: 20px 20px 10px;
   }
 }
+
 .nav-pills.move {
   float: right;
   margin-top: -48px;
@@ -713,7 +791,7 @@ ul.filter {
   .indicatorG {
     background-color: #88BF67;
   }
-  .statusIndicator  {
+  .statusIndicator {
     display: inline-block;
     width: 20px;
     height: 19px;
@@ -726,15 +804,15 @@ ul.filter {
     margin-top: 4px;
     float: left;
   }
-  .statusIndicator.LIVE{
+  .statusIndicator.LIVE {
     background-image: @rack-status-live;
   }
 
-  .statusIndicator.CRITICAL{
+  .statusIndicator.CRITICAL {
     background-image: @rack-status-critical;
   }
 
-  .statusIndicator.DEAD{
+  .statusIndicator.DEAD {
     background-image: @rack-status-dead;
   }
 
@@ -776,6 +854,18 @@ ul.filter {
   }
 }
 
+.heatmap_host_details {
+  font-size: 10px;
+  line-height: 1.6em;
+  border: 1px solid #006F9F;
+  background: #fff;
+  width: 73px;
+  height: 79px;
+  padding: 10px 10px;
+  position: absolute;
+  z-index: 1000;
+}
+
 /*Start Heatmap*/
 .heatmap {
   .rack.rack-5-2 {
@@ -815,6 +905,7 @@ ul.filter {
     z-index: 1000;
   }
 }
+
 /*End Heatmap*/
 .noDisplay {
   display: none !important;
@@ -911,8 +1002,33 @@ ul.filter {
   }
 }
 
+.linear {
+  path {
+    stroke: steelblue;
+    stroke-width: 2;
+    fill: none;
+  }
+
+  line {
+    stroke: black;
+  }
+
+  text {
+    font-family: Arial;
+    font-size: 9pt;
+  }
+}
+
 /* CHARTS END */
 
+/* UNIVERSAL STYLES */
+.left {
+  float: left;
+}
+
+.no-borders {
+  border: none !important;
+}
 
 ul.noStyle {
   list-style: none;
@@ -920,4 +1036,9 @@ ul.noStyle {
 
 ul.inline li {
   display: inline;
-}
+}
+
+.table.no-borders th, .table.no-borders td {
+  border-top: none;
+}
+/* UNIVERSAL STYLES END */

+ 0 - 1
ambari-web/app/templates/installer/slave_group_hosts.hbs

@@ -1 +0,0 @@
-{{view.content.groupName}}

+ 23 - 5
ambari-web/app/templates/installer/step7.hbs

@@ -51,13 +51,31 @@
             class="btn add-slave-component-group btn-large" {{action addSlaveComponentGroup category.name target="App.router.slaveComponentGroupsController"}}><i
             class="icon-plus"></i></a>
           {{/view}}
-          {{#each group in App.router.slaveComponentGroupsController.getGroups}}
-            {{#view App.SlaveComponentGroupView contentBinding="group" }}
-              {{group.groupName}}
-            {{/view}}
-          {{/each}}
+          <div class="remove-group-error control-group warning">
+            <span class="help-inline">You cannot delete this group since there are hosts assigned to it. You must assign them to another group before you can delete this group.</span>
+          </div>
           {{/if}}
           <form class="form-horizontal">
+            {{#if category.isForSlaveComponent}}
+              {{#each group in App.router.slaveComponentGroupsController.componentGroups}}
+                {{#if group.active}}
+                  {{#view App.SlaveComponentGroupView contentBinding="group" }}
+                    <div class="control-group">
+                      <label class="control-label">Group name</label>
+                      <div class="controls">
+                        <input class="span6" type="text" value="{{unbound group.name}}">
+                        <span class="help-inline"></span>
+                      </div>
+                    </div>
+                    <!--<div class="control-group">-->
+                      <!--<label class="control-label">DataNode hosts</label>-->
+                      <!--<div class="controls">-->
+                      <!--</div>-->
+                    <!--</div>-->
+                  {{/view}}
+                {{/if}}
+              {{/each}}
+            {{/if}}
             {{#each view.categoryConfigs}}
             <div {{bindAttr class="errorMessage:error: :control-group"}}>
               <label class="control-label">{{displayName}}</label>

+ 7 - 40
ambari-web/app/templates/main/dashboard.hbs

@@ -22,48 +22,15 @@
       <div class="span6">
         <div class="box">
           <div class="box-header">
-            <h4>Summary</h4>
-          </div>
-          <div id="myCarousel" class="carousel">
-            <div class="carousel-inner">
-              <div class="active item">
-
-              </div>
-              <div class="item">
-
-              </div>
-              <div class="item">
-
-              </div>
-            </div>
-            <a class="carousel-control left" href="#myCarousel" data-slide="prev">&lsaquo;</a>
-            <a class="carousel-control right" href="#myCarousel" data-slide="next">&rsaquo;</a>
-          </div>
-          <div class="box-footer"></div>
-        </div>
-      </div>
-    </div>
-    <div class="row">
-      <div class="span6">
-        <div class="box">
-          <div class="box-header">
-            <h4>Services</h4>
+            <h4>{{t dashboard.services}}</h4>
           </div>
           <dl class="dl-horizontal services">
-            {{#each service in controller.services}}
-              <dt class="health-status-{{unbound service.healthStatus}}">
-                <a {{action selectService service href=true}}>{{service.label}}</a> -
-              </dt>
-              <dd>
-                {{#each component in service.components}}
-                  {{#if component.type}}
-                    <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
-                  {{else}}
-                    <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
-                  {{/if}}
-                {{/each}}
-              </dd>
-            {{/each}}
+            {{view App.MainDashboardServiceHdfsView servicesBinding="controller.services"}}
+            {{view App.MainDashboardServiceMapreduceView servicesBinding="controller.services"}}
+            {{view App.MainDashboardServiceHbaseView servicesBinding="controller.services"}}
+            {{view App.MainDashboardServiceHiveView servicesBinding="controller.services"}}
+            {{view App.MainDashboardServiceZookeperView servicesBinding="controller.services"}}
+            {{view App.MainDashboardServiceOozieView servicesBinding="controller.services"}}
           </dl>
         </div>
       </div>

+ 89 - 0
ambari-web/app/templates/main/dashboard/service/hbase.hbs

@@ -0,0 +1,89 @@
+<!--
+* 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="clearfix">
+  <div class="name span2">
+    {{view App.MainDashboardServiceHealthView parentBinding="view"}}
+    <a {{action selectService view.service href=true}}>{{view.service.label}}</a>
+    {{#if view.criticalAlertsCount}}
+    <span class="label label-important alerts-count">{{view.criticalAlertsCount}}</span>
+    {{/if}}
+  </div>
+  <div class="summary span">
+    {{view.summaryHeader}}
+  </div>
+</div>
+<table class="table no-borders">
+  <tbody>
+
+  <!-- Master Server Heap -->
+  <tr>
+    <td>{{t dashboard.services.hbase.masterServerHeap}}</td>
+    <td>?</td>
+  </tr>
+
+  <!-- Average load -->
+  <tr>
+    <td>{{t dashboard.services.hbase.averageLoad}}</td>
+    <td>?</td>
+  </tr>
+
+  <!-- Region Servers -->
+  <tr>
+    <td>{{t dashboard.services.hbase.regionServers}}</td>
+    <td>{{view.regionServers}}</td>
+  </tr>
+
+  <!-- Master Server Uptime -->
+  <tr>
+    <td>{{t dashboard.services.hbase.masterServerUptime}}</td>
+    <td>{{view.masterServerUptime}}</td>
+  </tr>
+
+  <!-- Hosts -->
+  <tr>
+    <td>{{t dashboard.services.hosts}}</td>
+    <td>
+      {{#each component in view.service.components}}
+      {{#if component.type}}
+      <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
+      {{else}}
+      <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
+      {{/if}}
+      {{/each}}
+    </td>
+  </tr>
+  </tbody>
+</table>
+<div class="chart">
+  {{view view.Chart}}
+  <div class="chartLabel">{{t dashboard.services.hbase.chart.label}}</div>
+  {{#if view.service.quickLinks.length}}
+  <div class="btn-group">
+    <a class="btn btn-mini dropdown-toggle" data-toggle="dropdown" href="#">
+      Quick Links
+      <span class="caret"></span>
+    </a>
+    <ul class="dropdown-menu">
+      {{#each view.service.quickLinks}}
+      <li><a href="javascript:void(null)">{{label}}</a></li>
+      {{/each}}
+    </ul>
+  </div>
+  {{/if}}
+</div>

+ 100 - 0
ambari-web/app/templates/main/dashboard/service/hdfs.hbs

@@ -0,0 +1,100 @@
+<!--
+* 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="clearfix">
+  <div class="name span2">
+    {{view App.MainDashboardServiceHealthView parentBinding="view"}}
+    <a {{action selectService view.service href=true}}>{{view.service.label}}</a>
+    {{#if view.criticalAlertsCount}}
+    <span class="label label-important alerts-count">{{view.criticalAlertsCount}}</span>
+    {{/if}}
+  </div>
+  <div class="summary span">
+    {{view.summaryHeader}}
+  </div>
+</div>
+<table class="table no-borders">
+  <tbody>
+
+  <!-- HDFS Capacity -->
+  <tr>
+    <td>{{t dashboard.services.hdfs.capacity}}</td>
+    <td>{{view.capacity}}</td>
+  </tr>
+
+  <!-- Data Nodes -->
+  <tr>
+    <td>{{t dashboard.services.hdfs.nodes}}</td>
+    <td>
+      {{view.data.live_nodes}} {{t dashboard.services.hdfs.nodes.live}} /
+      {{view.data.dead_nodes}} {{t dashboard.services.hdfs.nodes.dead}} /
+      {{view.data.decommissioning_nodes}} {{t dashboard.services.hdfs.nodes.decom}}
+    </td>
+  </tr>
+
+  <!-- Total Files And Directories -->
+  <tr>
+    <td>{{t dashboard.services.hdfs.totalFilesAndDirs}}</td>
+    <td>?</td>
+  </tr>
+
+  <!-- NameNode Uptime -->
+  <tr>
+    <td>{{t dashboard.services.hdfs.nodes.uptime}}</td>
+    <td>{{view.nodeUptime}}</td>
+  </tr>
+
+  <!-- NameNode Heap -->
+  <tr>
+    <td>{{t dashboard.services.hdfs.nodes.heap}}</td>
+    <td>{{view.nodeHeap}}</td>
+  </tr>
+
+
+  <!-- Hosts -->
+  <tr>
+    <td>{{t dashboard.services.hosts}}</td>
+    <td>
+      {{#each component in view.service.components}}
+      {{#if component.type}}
+      <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
+      {{else}}
+      <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
+      {{/if}}
+      {{/each}}
+    </td>
+  </tr>
+  </tbody>
+</table>
+<div class="chart">
+  {{view view.Chart}}
+  <div class="chartLabel">{{t dashboard.services.hdfs.chart.label}}</div>
+  {{#if view.service.quickLinks.length}}
+  <div class="btn-group">
+    <a class="btn btn-mini dropdown-toggle" data-toggle="dropdown" href="#">
+      Quick Links
+      <span class="caret"></span>
+    </a>
+    <ul class="dropdown-menu">
+      {{#each view.service.quickLinks}}
+        <li><a href="javascript:void(null)">{{label}}</a></li>
+      {{/each}}
+    </ul>
+  </div>
+  {{/if}}
+</div>

+ 36 - 0
ambari-web/app/templates/main/dashboard/service/hive.hbs

@@ -0,0 +1,36 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+
+<div class="clearfix">
+  <div class="name span2">
+    {{view App.MainDashboardServiceHealthView parentBinding="view"}}
+    <a {{action selectService view.service href=true}}>{{view.service.label}}</a>
+    {{#if view.criticalAlertsCount}}
+    <span class="label label-important alerts-count">{{view.criticalAlertsCount}}</span>
+    {{/if}}
+  </div>
+  <div class="summary span">
+    {{#each component in view.service.components}}
+    {{#if component.type}}
+    <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
+    {{else}}
+    <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
+    {{/if}}
+    {{/each}}
+  </div>
+</div>

+ 100 - 0
ambari-web/app/templates/main/dashboard/service/mapreduce.hbs

@@ -0,0 +1,100 @@
+<!--
+* 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="clearfix">
+  <div class="name span2">
+    {{view App.MainDashboardServiceHealthView parentBinding="view"}}
+    <a {{action selectService view.service href=true}}>{{view.service.label}}</a>
+    {{#if view.criticalAlertsCount}}
+      <span class="label label-important alerts-count">{{view.criticalAlertsCount}}</span>
+    {{/if}}
+  </div>
+  <div class="summary span">
+    {{view.summaryHeader}}
+  </div>
+</div>
+<table class="table no-borders">
+  <tbody>
+
+  <!-- Trackers -->
+  <tr>
+    <td>{{t dashboard.services.mapreduce.trackers}}</td>
+    <td>{{view.trackersSummary}}</td>
+  </tr>
+
+  <!-- Jobs -->
+  <tr>
+    <td>{{t dashboard.services.mapreduce.jobs}}</td>
+    <td>{{view.jobsSummary}}</td>
+  </tr>
+
+  <!-- Map Slots -->
+  <tr>
+    <td>{{t dashboard.services.mapreduce.mapSlots}}</td>
+    <td>?</td>
+  </tr>
+
+  <!-- Reduce Slots -->
+  <tr>
+    <td>{{t dashboard.services.mapreduce.reduceSlots}}</td>
+    <td>?</td>
+  </tr>
+
+  <!-- Job Tracker Uptime -->
+  <tr>
+    <td>{{t dashboard.services.mapreduce.jobTrackerUptime}}</td>
+    <td>{{view.jobTrackerUptime}}</td>
+  </tr>
+
+  <!-- JobTracker Heap -->
+  <tr>
+    <td>{{t dashboard.services.mapreduce.jobTrackerHeap}}</td>
+    <td>?</td>
+  </tr>
+
+  <!-- Hosts -->
+  <tr>
+    <td>{{t dashboard.services.hosts}}</td>
+    <td>
+      {{#each component in view.service.components}}
+      {{#if component.type}}
+      <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
+      {{else}}
+      <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
+      {{/if}}
+      {{/each}}
+    </td>
+  </tr>
+  </tbody>
+</table>
+<div class="chart">
+  {{view view.Chart}}
+  <div class="chartLabel">{{t dashboard.services.mapreduce.chart.label}}</div>
+  {{#if view.service.quickLinks.length}}
+  <div class="btn-group">
+    <a class="btn btn-mini dropdown-toggle" data-toggle="dropdown" href="#">
+      Quick Links
+      <span class="caret"></span>
+    </a>
+    <ul class="dropdown-menu">
+      {{#each view.service.quickLinks}}
+      <li><a href="javascript:void(null)">{{label}}</a></li>
+      {{/each}}
+    </ul>
+  </div>
+  {{/if}}
+</div>

+ 36 - 0
ambari-web/app/templates/main/dashboard/service/oozie.hbs

@@ -0,0 +1,36 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+
+<div class="clearfix">
+  <div class="name span2">
+    {{view App.MainDashboardServiceHealthView parentBinding="view"}}
+    <a {{action selectService view.service href=true}}>{{view.service.label}}</a>
+    {{#if view.criticalAlertsCount}}
+    <span class="label label-important alerts-count">{{view.criticalAlertsCount}}</span>
+    {{/if}}
+  </div>
+  <div class="summary span">
+    {{#each component in view.service.components}}
+    {{#if component.type}}
+    <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
+    {{else}}
+    <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
+    {{/if}}
+    {{/each}}
+  </div>
+</div>

+ 36 - 0
ambari-web/app/templates/main/dashboard/service/zookeeper.hbs

@@ -0,0 +1,36 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+
+<div class="clearfix">
+  <div class="name span2">
+    {{view App.MainDashboardServiceHealthView parentBinding="view"}}
+    <a {{action selectService view.service href=true}}>{{view.service.label}}</a>
+    {{#if view.criticalAlertsCount}}
+    <span class="label label-important alerts-count">{{view.criticalAlertsCount}}</span>
+    {{/if}}
+  </div>
+  <div class="summary span">
+    {{#each component in view.service.components}}
+    {{#if component.type}}
+    <a href="#" {{action selectHost component.host}}>{{component.componentName}}</a>,
+    {{else}}
+    <a href="#" {{action filterHosts component}}>{{component.componentName}}</a>
+    {{/if}}
+    {{/each}}
+  </div>
+</div>

+ 167 - 60
ambari-web/app/templates/main/service/info/summary.hbs

@@ -37,67 +37,174 @@
         {{/if}}
 			{{/each}}
       {{#if view.serviceStatus.hdfs}}
-      <table id="summary-info" class="table table-bordered table-striped table-condensed">
-        <tr>
-          <td>{{t services.service.summary.version}}</td>
-          <td>{{view.attributes.version}}</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.nameNode}}</td>
-          <td><a {{bindAttr href="view.attributes.namenode_addr"}}>{{view.attributes.namenode_addr}}</a></td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.nameNodeUptime}}</td>
-          <td>{{view.attributes.start_time}}</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.nameNodeHeap}}</td>
-          <td>{{view.attributes.memory_heap_used}} MB / {{view.attributes.memory_heap_max}} MB ({{view.attributes.memory_heap_percent_used}}% used)</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.dataNodes}}</td>
-          <td>
-            <a href="javascript:void(null)">{{view.attributes.live_nodes}}</a> / <a href="javascript:void(null)">{{view.attributes.dead_nodes}}</a> / <a href="javascript:void(null)">{{view.attributes.decommissioning_nodes}}</a>
-          </td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.diskCapacity}}</td>
-          <td>{{view.attributes.used_bytes}} GB / {{view.attributes.dfs_total_bytes}} GB ({{view.attributes.dfs_percent_used}}% used)</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.blocksTotal}}</td>
-          <td>{{view.attributes.dfs_blocks_total}}</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.blockErrors}}</td>
-          <td>{{view.attributes.dfs_blocks_corrupt}} / {{view.attributes.dfs_blocks_missing}} / ({{view.attributes.dfs_blocks_underreplicated}})</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.totalFiles}}</td>
-          <td>{{view.attributes.dfs_dirfiles_count}}</td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.pendingUpgradeStatus}}</td>
-          <td>
-            {{#if view.attributes.pending_upgrades}}
-            Pending upgrade
-            {{else}}
-            No pending upgrade
-            {{/if}}
-          </td>
-        </tr>
-        <tr>
-          <td>{{t services.service.summary.safeModeStatus}}</td>
-          <td>
-            {{#if view.attributes.safemode}}
-            In safe mode
-            {{else}}
-            Not in safe mode
-            {{/if}}
-          </td>
-        </tr>
-      </table>
+        <table id="summary-info" class="table table-bordered table-striped table-condensed">
+          <tbody>
+            <tr>
+              <td>{{t services.service.summary.version}}</td>
+              <td>{{view.attributes.version}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.nameNode}}</td>
+              <td><a {{bindAttr href="view.attributes.namenode_addr"}}>{{view.attributes.namenode_addr}}</a></td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.nameNodeUptime}}</td>
+              <td>{{view.attributes.start_time.d}}day {{view.attributes.start_time.h}}hr {{view.attributes.start_time.m}}min</td>
+            </tr>
+          </tbody>
+          <tbody {{bindAttr class="view.isHide:hide"}}>
+            <tr>
+              <td>{{t services.service.summary.nameNodeHeap}}</td>
+              <td>{{view.attributes.memory_heap_used}} MB / {{view.attributes.memory_heap_max}} MB ({{view.attributes.memory_heap_percent_used}}% used)</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.dataNodes}}</td>
+              <td>
+                <a href="javascript:void(null)">{{view.attributes.live_nodes}}</a> / <a href="javascript:void(null)">{{view.attributes.dead_nodes}}</a> / <a href="javascript:void(null)">{{view.attributes.decommissioning_nodes}}</a>
+              </td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.diskCapacity}}</td>
+              <td>{{view.attributes.used_bytes}} GB / {{view.attributes.dfs_total_bytes}} GB ({{view.attributes.dfs_percent_disk_used}}% used)</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.blocksTotal}}</td>
+              <td>{{view.attributes.dfs_blocks_total}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.blockErrors}}</td>
+              <td>{{view.attributes.dfs_blocks_corrupt}} / {{view.attributes.dfs_blocks_missing}} / ({{view.attributes.dfs_blocks_underreplicated}})</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.totalFiles}}</td>
+              <td>{{view.attributes.dfs_dirfiles_count}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.pendingUpgradeStatus}}</td>
+              <td>
+                {{#if view.attributes.pending_upgrades}}
+                Pending upgrade
+                {{else}}
+                No pending upgrade
+                {{/if}}
+              </td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.safeModeStatus}}</td>
+              <td>
+                {{#if view.attributes.safemode}}
+                In safe mode
+                {{else}}
+                Not in safe mode
+                {{/if}}
+              </td>
+            </tr>
+          </tbody>
+        </table>
       {{/if}}
+      {{#if view.serviceStatus.mapreduce}}
+        <table id="summary-info" class="table table-bordered table-striped table-condensed">
+          <tbody>
+            <tr>
+              <td>{{t services.service.summary.version}}</td>
+              <td>{{view.attributes.version}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.jobTracker}}</td>
+              <td><a {{bindAttr href="view.attributes.jobtracker_addr"}}>{{view.attributes.jobtracker_addr}}</a></td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.jobTrackerUptime}}</td>
+              <td>{{view.attributes.start_time.d}}day {{view.attributes.start_time.h}}hr {{view.attributes.start_time.m}}min</td>
+            </tr>
+          </tbody>
+          <tbody {{bindAttr class="view.isHide:hide"}}>
+            <tr>
+              <td>{{t services.service.summary.trackersLiveTotal}}</td>
+              <td>
+                <a href="javascript:void(null)">{{view.attributes.trackers_live}}</a> / {{view.attributes.trackers_total}}
+              </td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.trackersBlacklistGraylist}}</td>
+              <td>
+                <a href="javascript:void(null)">{{view.attributes.trackers_blacklisted}}</a> / <a href="javascript:void(null)">{{view.attributes.trackers_graylisted}}</a> / <a href="javascript:void(null)">{{view.attributes.trackers_excluded}}</a>
+              </td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.jobTrackerHeap}}</td>
+              <td>{{view.attributes.memory_heap_used}} MB / {{view.attributes.memory_heap_max}} MB ({{view.attributes.memory_heap_percent_used}}% used)</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.totalSlotsCapacity}}</td>
+              <td>{{view.attributes.map_task_capacity}} / {{view.attributes.reduce_task_capacity}} / {{view.attributes.average_node_capacity}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.totalJobs}}</td>
+              <td>{{view.attributes.job_total_submissions}} / {{view.attributes.job_total_completions}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.currentSlotUtiliMaps}}</td>
+              <td>{{view.attributes.occupied_map_slots}} / {{view.attributes.reserved_map_slots}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.currentSlotUtiliReduces}}</td>
+              <td>{{view.attributes.occupied_reduce_slots}} / {{view.attributes.reserved_reduce_slots}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.tasksMaps}}</td>
+              <td>{{view.attributes.running_map_tasks}} / {{view.attributes.waiting_maps}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.tasksReduces}}</td>
+              <td>{{view.attributes.running_reduce_tasks}} / {{view.attributes.waiting_reduces}}</td>
+            </tr>
+          </tbody>
+        </table>
+      {{/if}}
+      {{#if view.serviceStatus.hbase}}
+        <table id="summary-info" class="table table-bordered table-striped table-condensed">
+          <tbody>
+            <tr>
+              <td>{{t services.service.summary.version}}</td>
+              <td>{{view.attributes.version}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.hbaseMaster}}</td>
+              <td><a {{bindAttr href="view.attributes.hbasemaster_addr"}}>{{view.attributes.hbasemaster_addr}}</a></td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.regionServerCount}}</td>
+              <td>
+                <a href="javascript:void(null)">{{view.attributes.live_regionservers}}</a> / <a href="javascript:void(null)">{{view.attributes.dead_regionservers}}</a>
+              </td>
+            </tr>
+          </tbody>
+          <tbody {{bindAttr class="view.isHide:hide"}}>
+            <tr>
+              <td>{{t services.service.summary.regionInTransition}}</td>
+              <td>{{view.attributes.regions_in_transition_count}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.masterStarted}}</td>
+              <td>{{view.attributes.start_time.d}}day {{view.attributes.start_time.h}}hr {{view.attributes.start_time.m}}min</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.masterActivated}}</td>
+              <td>{{view.attributes.active_time.d}}day {{view.attributes.active_time.h}}hr {{view.attributes.active_time.m}}min</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.averageLoad}}</td>
+              <td>{{view.attributes.average_load}}</td>
+            </tr>
+            <tr>
+              <td>{{t services.service.summary.masterHeap}}</td>
+              <td>{{view.attributes.memory_heap_used}} MB / {{view.attributes.memory_heap_max}} MB ({{view.attributes.memory_heap_percent_used}}% used)</td>
+            </tr>
+          </tbody>
+        </table>
+      {{/if}}
+      {{view view.moreStatsView}}
 		</div>
 	</div>
 	<div class="span6">

+ 39 - 1
ambari-web/app/utils/helper.js

@@ -32,4 +32,42 @@ Handlebars.registerHelper('log', function(variable) {
 
 Handlebars.registerHelper('warn', function(variable) {
   console.warn(variable);
-});
+});
+
+String.prototype.format = function() {
+  var args = arguments;
+  return this.replace(/{(\d+)}/g, function(match, number) {
+    return typeof args[number] != 'undefined' ? args[number] : match;
+  });
+};
+
+
+Number.prototype.bytesToSize = function(precision) {
+  var value = this;
+  var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
+  var posttxt = 0;
+  if (this == 0) return 'n/a';
+  while( value >= 1024 ) {
+    posttxt++;
+    value = value / 1024;
+  }
+  return parseInt(value).toFixed(precision) + " " + sizes[posttxt];
+}
+
+Number.prototype.toDaysHoursMinutes = function() {
+  var formatted = {},
+    dateDiff = this,
+    minK = 60, // sec
+    hourK = 60 * minK, // sec
+    dayK = 24 * hourK;
+
+  dateDiff = parseInt(dateDiff/1000);
+  formatted.d = Math.floor(dateDiff/dayK);
+  dateDiff -= formatted.d * dayK;
+  formatted.h = Math.floor(dateDiff/hourK);
+  dateDiff -= formatted.h * hourK;
+  formatted.m = Math.floor(dateDiff/minK);
+  dateDiff -= formatted.m * minK;
+
+  return formatted;
+}

+ 10 - 1
ambari-web/app/views.js

@@ -22,6 +22,8 @@
 
 require('views/application');
 require('views/common/chart');
+require('views/common/chart/pie');
+require('views/common/chart/linear');
 require('views/common/modal_popup');
 require('views/common/metric');
 require('views/common/form/field');
@@ -47,6 +49,13 @@ require('views/main/admin/user');
 require('views/main/admin/user/edit');
 require('views/main/admin/user/row');
 require('views/main/dashboard');
+require('views/main/dashboard/service');
+require('views/main/dashboard/service/hdfs');
+require('views/main/dashboard/service/mapreduce');
+require('views/main/dashboard/service/hbase');
+require('views/main/dashboard/service/hive');
+require('views/main/dashboard/service/zookeeper');
+require('views/main/dashboard/service/oozie');
 require('views/main/service');
 require('views/main/service/menu');
 require('views/main/service/item');
@@ -76,4 +85,4 @@ require('views/installer/step10_view');
 require('views/wizard/step2_view');
 require('views/wizard/step3_view');
 require('views/wizard/step4_view');
-require('views/wizard/step5_view');
+require('views/wizard/step5_view');

+ 1 - 1
ambari-web/app/views/common/chart.js

@@ -147,7 +147,7 @@ App.ChartView = Em.View.extend({
       .append("svg:svg")
       .attr("width", renderer.get('w') + 5)
       .attr("height", renderer.get('h'))
-      .attr("rendererId", this.$().attr('id'))
+      .attr("rendererId", this.get('elementId'))
       .on("mousemove", function () {
 
         var area = d3.select(this).select("path.line");

+ 70 - 0
ambari-web/app/views/common/chart/linear.js

@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.ChartLinearView = Em.View.extend({
+  w:90,
+  h:90,
+  margin:2,
+
+  classNames: ['linear'],
+
+  init:function () {
+    this._super();
+
+    this.x = d3.scale.linear().domain([0, this.get('data').length]).range([0 + this.get('margin'), this.get('w') - this.get('margin')]);
+    this.y = d3.scale.linear().domain([0, d3.max(this.get('data'))]).range([0 + this.get('margin'), this.get('h') - this.get('margin')]);
+    this.line = d3.svg.line().x(function (d, i) { return this.x(i); }).y(function (d) {return -1 * this.y(d); })
+  },
+
+  didInsertElement:function () {
+    this._super();
+    this.appendSvg();
+  },
+
+  selector:function () {
+    return '#' + this.get('elementId');
+  }.property('elementId'),
+
+  appendSvg:function () {
+    var thisChart = this;
+
+    this.set('svg', d3.select(this.get('selector'))
+      .append("svg:svg")
+      .attr("width", thisChart.get('w'))
+      .attr("height", thisChart.get('h')));
+
+    this.set('g', thisChart.get('svg').append("svg:g").attr("transform", "translate(0, " + thisChart.get('h') + ")"));
+    this.get('g').append("svg:path").attr("d", thisChart.line(thisChart.get('data')));
+
+
+    // axis
+    this.get('g').append("svg:line")
+      .attr("x1", thisChart.x(0))
+      .attr("y1", -1 * thisChart.y(0))
+      .attr("x2", thisChart.x(this.get('data').length))
+      .attr("y2", -1 * thisChart.y(0))
+
+    this.get('g').append("svg:line")
+      .attr("x1", thisChart.x(0))
+      .attr("y1", -1 * thisChart.y(0))
+      .attr("x2", thisChart.x(0))
+      .attr("y2", -1 * thisChart.y(d3.max(thisChart.get('data'))))
+  }
+});

+ 70 - 0
ambari-web/app/views/common/chart/pie.js

@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.ChartPieView = Em.View.extend({
+  w:90,
+  h:90,
+  data:[300, 500],
+  color:d3.scale.category20(),
+  donut:d3.layout.pie().sort(null),
+
+  r:function () {
+    return Math.min(this.get('w'), this.get('h')) / 2;
+  }.property('w', 'h'),
+
+  outerR:function () {
+    return this.get('r'); // - 10;
+  }.property('r'),
+
+  innerR:function () {
+    return 0; // this.get('r') - 20;
+  }.property('r'),
+
+  arc:function () {
+    return d3.svg.arc().innerRadius(this.get('innerR')).outerRadius(this.get('outerR'));
+  }.property(),
+
+  didInsertElement:function () {
+    this._super();
+    this.appendSvg();
+  },
+
+  selector:function () {
+    return '#' + this.get('elementId');
+  }.property('elementId'),
+
+  appendSvg:function () {
+    var thisChart = this;
+
+    this.set('svg', d3.select(this.get('selector')).append("svg:svg")
+      .attr("width", thisChart.get('w'))
+      .attr("height", thisChart.get('h'))
+      .append("svg:g")
+      .attr("transform", "translate(" + thisChart.get('w') / 2 + "," + thisChart.get('h') / 2 + ")"));
+
+    this.set('arcs', this.get('svg').selectAll("path")
+      .data(thisChart.donut(thisChart.get('data')))
+      .enter().append("svg:path")
+      .attr("fill", function (d, i) {
+        return thisChart.color(i);
+      })
+      .attr("d", this.get('arc')));
+  }
+});

+ 7 - 7
ambari-web/app/views/installer/step7_view.js

@@ -237,7 +237,7 @@ App.AddSlaveComponentGroupButton = Ember.View.extend({
 
 App.SlaveComponentGroupsMenu = Em.CollectionView.extend({
 
-  contentBinding: 'App.router.slaveComponentGroupsController.getGroups',
+  contentBinding: 'App.router.slaveComponentGroupsController.componentGroups',
   tagName:'ul',
   classNames: ["nav", "nav-tabs"],
 
@@ -246,13 +246,13 @@ App.SlaveComponentGroupsMenu = Em.CollectionView.extend({
     active:function(){
       return this.get('content.active');
     }.property('content.active'),
-    template:Ember.Handlebars.compile('<a {{action showSlaveComponentGroup view.content target="App.router.slaveComponentGroupsController"}} href="#"> {{unbound view.content.groupName}}</a>')  })
+    template:Ember.Handlebars.compile('<a {{action showSlaveComponentGroup view.content target="App.router.slaveComponentGroupsController"}} href="#"> {{unbound view.content.name}}</a><i {{action removeSlaveComponentGroup view.content target="App.router.slaveComponentGroupsController"}} class="icon-remove"></i>')  })
 });
 
 App.SlaveComponentGroupView = Ember.View.extend({
-  classNames: ['slave-group'],
-  classNameBindings:["active"],
-  active:function(){
-    return this.get('content.active');
-  }.property('content.active')
+//  contentBinding: 'App.router.slaveComponentGroupsController.activeGroup',
+  classNames: ['slave-group']
+//  elementId: function(){
+//    return 'slave-group' + this.get('content.index');
+//  }.property('content.index')
 });

+ 102 - 0
ambari-web/app/views/main/dashboard/service.js

@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceHealthView = Em.View.extend({
+  classNameBindings:["healthStatus"],
+  template:Em.Handlebars.compile(""),
+  blink:false,
+
+  status:function () {
+    return this.get('parent.service.healthStatus');
+  }.property('parent.service.healthStatus'),
+
+  startBlink:function () {
+      this.set('blink', true);
+  },
+
+  doBlink:function () {
+    if (this.get('blink') && (this.get("state") == "inDOM")){
+      this.$().effect("pulsate", { times:1 }, "slow", function () {
+        var view = Em.View.views[$(this).attr('id')];
+        view.doBlink();
+      });
+    }
+  }.observes('blink'),
+
+  stopBlink:function () {
+    this.set('blink', false);
+  },
+
+  healthStatus:function () {
+    var status = this.get('status');
+    switch (status) {
+      case App.Service.Health.start:
+        status = App.Service.Health.live;
+        this.startBlink();
+        break;
+      case App.Service.Health.stop:
+        status = App.Service.Health.dead;
+        this.startBlink();
+        break;
+      default:
+        this.stopBlink();
+        break;
+    }
+
+    return 'health-status-' + status + " span";
+  }.property('status'),
+
+  didInsertElement:function () {
+    this._super();
+    this.doBlink(); // check for blink availability
+  }
+});
+
+App.MainDashboardServiceView = Em.View.extend({
+  classNames:['service', 'clearfix'],
+  service:function () {
+    var services = this.get('services');
+    thisView = this;
+    var serviceProperty = false;
+    services.forEach(function (service) {
+      if (service.get('serviceName') == thisView.get('serviceName')) {
+        return serviceProperty = service;
+      }
+    })
+
+    return serviceProperty;
+  }.property('services'),
+
+  init:function () {
+    this._super();
+    var thisView = this;
+  },
+
+  criticalAlertsCount: function(){
+    var alerts = this.get('service.alerts');
+    var count = 0;
+
+    alerts.forEach(function (alert) {
+      count += (alert.get('status') == App.AlertStatus.negative);
+    });
+
+    return count;
+  }.property('service.alerts')
+});

+ 60 - 0
ambari-web/app/views/main/dashboard/service/hbase.js

@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceHbaseView = App.MainDashboardServiceView.extend({
+  templateName: require('templates/main/dashboard/service/hbase'),
+  serviceName: 'hbase',
+  data: {
+    "hbasemaster_addr": "ec2-23-21-1-25.compute-1.amazonaws.com:60010",
+    "total_regionservers": "1",
+    "hbasemaster_starttime": 1348935496,
+    "live_regionservers": 1,
+    "dead_regionservers": 0,
+    "regions_in_transition_count": 0,
+    "chart": [3,7,7,5,5,3,5,3,7]
+  },
+
+  Chart: App.ChartLinearView.extend({
+    data: function(){
+      return this.get('_parentView.data.chart');
+    }.property('_parentView.data.chart')
+  }),
+
+  summaryHeader: function(){
+    return this.t("dashboard.services.hbase.summary").format(
+      this.get('data.live_regionservers'),
+      this.get('data.total_regionservers'),
+      "?"
+    );
+  }.property('data'),
+
+  regionServers: function(){
+    return this.t('dashboard.services.hbase.regionServersSummary').format(
+      this.get('data.live_regionservers'), this.get('data.total_regionservers')
+    );
+
+  }.property('data'),
+
+  masterServerUptime: function(){
+    var uptime = this.get('data.hbasemaster_starttime');
+    var formatted = uptime.toDaysHoursMinutes();
+    return this.t('dashboard.services.uptime').format(formatted.d, formatted.h, formatted.m);
+  }.property("data")
+});

+ 72 - 0
ambari-web/app/views/main/dashboard/service/hdfs.js

@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
+  templateName:require('templates/main/dashboard/service/hdfs'),
+  serviceName:'hdfs',
+  data:{
+    "namenode_addr":"ec2-23-21-1-25.compute-1.amazonaws.com:50070",
+    "secondary_namenode_addr":"ec2-23-21-1-25.compute-1.amazonaws.com:50090",
+    "namenode_starttime":1348935028,
+    "total_nodes":"1",
+    "live_nodes":1,
+    "dead_nodes":0,
+    "decommissioning_nodes":0,
+    "dfs_blocks_underreplicated":145,
+    "safemode":false,
+    "pending_upgrades":false,
+    "dfs_configured_capacity":885570207744,
+    "dfs_percent_used":0.01,
+    "dfs_percent_remaining":95.09,
+    "dfs_total_bytes":885570207744,
+    "dfs_used_bytes":104898560,
+    "nondfs_used_bytes":43365113856,
+    "dfs_free_bytes":842100195328
+  },
+
+  Chart:App.ChartPieView.extend({
+    data:function () {
+      return [ this.get('_parentView.data.dfs_used_bytes') + this.get('_parentView.data.nondfs_used_bytes'), this.get('_parentView.data.dfs_free_bytes') ];
+    }.property('_parentView.data')
+  }),
+
+  nodeUptime:function () {
+    var uptime = this.get('data.namenode_starttime');
+    var formatted = uptime.toDaysHoursMinutes();
+    return this.t('dashboard.services.uptime').format(formatted.d, formatted.h, formatted.m);
+  }.property("data"),
+
+  nodeHeap:function () {
+    return this.t('dashboard.services.hdfs.nodes.heapUsed').format("?", "?", "?");
+  }.property('data'),
+
+  summaryHeader:function () {
+    var text = this.t("dashboard.services.hdfs.summary");
+    return text.format(this.get('data.live_nodes'), this.get('data.total_nodes'), this.get('data.dfs_percent_remaining'));
+  }.property('data'),
+
+  capacity:function () {
+    var text = this.t("dashboard.services.hdfs.capacityUsed");
+    var total = this.get('data.dfs_total_bytes') + 0;
+    var used = this.get('data.dfs_used_bytes') + this.get('data.nondfs_used_bytes');
+
+    return text.format(used.bytesToSize(2), total.bytesToSize(2), this.get('data.dfs_percent_used'));
+  }.property('data')
+});

+ 24 - 0
ambari-web/app/views/main/dashboard/service/hive.js

@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceHiveView = App.MainDashboardServiceView.extend({
+  templateName: require('templates/main/dashboard/service/hive'),
+  serviceName: 'hive'
+});

+ 62 - 0
ambari-web/app/views/main/dashboard/service/mapreduce.js

@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceMapreduceView = App.MainDashboardServiceView.extend({
+  templateName: require('templates/main/dashboard/service/mapreduce'),
+  serviceName: 'mapreduce',
+  data: {
+    "jobtracker_addr": "ec2-23-21-1-25.compute-1.amazonaws.com:50030",
+    "jobtracker_starttime": 1348935243,
+    "running_jobs": 1,
+    "waiting_jobs": 0,
+    "trackers_total": "1",
+    "trackers_live": 1,
+    "trackers_graylisted": 0,
+    "trackers_blacklisted": 0,
+    "chart": [4,8,7,2,1,4,3,3,3]
+  },
+
+  Chart: App.ChartLinearView.extend({
+    data: function(){
+      return this.get('_parentView.data.chart');
+    }.property('_parentView.data.chart')
+  }),
+
+  jobTrackerUptime: function(){
+    var uptime = this.get('data.jobtracker_starttime') + 0;
+    var formatted = uptime.toDaysHoursMinutes();
+    return this.t('dashboard.services.uptime').format(formatted.d, formatted.h, formatted.m);
+  }.property("data"),
+
+  summaryHeader: function(){
+    var template = this.t('dashboard.services.mapreduce.summary');
+    return template.format(this.get('data.trackers_live'), this.get('data.trackers_total'), this.get('data.running_jobs'));
+  }.property('data'),
+
+  trackersSummary: function (){
+    var template = this.t('dashboard.services.mapreduce.trackersSummary');
+    return template.format(this.get('data.trackers_live'), this.get('data.trackers_total'));
+  }.property('data'),
+
+  jobsSummary: function (){
+    var template = this.t('dashboard.services.mapreduce.jobsSummary');
+    return template.format(this.get('data.running_jobs'), "?", "?");
+  }.property('data')
+});

+ 25 - 0
ambari-web/app/views/main/dashboard/service/oozie.js

@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceOozieView = App.MainDashboardServiceView.extend({
+  classNames: ['no-borders'],
+  serviceName: 'oozie',
+  templateName: require('templates/main/dashboard/service/oozie')
+});

+ 24 - 0
ambari-web/app/views/main/dashboard/service/zookeeper.js

@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.MainDashboardServiceZookeperView = App.MainDashboardServiceView.extend({
+  templateName: require('templates/main/dashboard/service/zookeeper'),
+  serviceName: 'zookeeper'
+});

+ 25 - 10
ambari-web/app/views/main/menu.js

@@ -23,8 +23,8 @@ var App = require('app');
  * @type {*}
  */
 App.MainMenuView = Em.CollectionView.extend({
-  tagName: 'ul',
-  classNames: ['nav'],
+  tagName:'ul',
+  classNames:['nav'],
   content:[
     { label:'Dashboard', routing:'dashboard', active:'active'},
     { label:'Charts', routing:'charts'},
@@ -33,23 +33,38 @@ App.MainMenuView = Em.CollectionView.extend({
     { label:'Admin', routing:'admin'}
   ],
 
-  deactivateChildViews:function (content) {
+  /**
+   *    Adds observer on lastSetURL and calls navigation sync procedure
+   */
+  didInsertElement:function () {
+    App.router.location.addObserver('lastSetURL', this, 'renderOnRoute');
+    this.renderOnRoute();
+  },
+
+  /**
+   *    Syncs navigation menu with requested URL
+   */
+  renderOnRoute:function () {
+    var last_url = App.router.location.lastSetURL || location.href.replace(/^[^#]*#/);
+    var reg = /^\/main\/([a-z]+)/g;
+    var sub_url = reg.exec(last_url);
+    var chunk = (null != sub_url) ? sub_url[1] : 'dashboard';
     $.each(this._childViews, function () {
-      this.set('active', this.get('content') == content ? "active" : "");
+      this.set('active', this.get('content.routing') == chunk ? "active" : "");
     });
   },
 
-  itemViewClass: Em.View.extend({
+  itemViewClass:Em.View.extend({
 
-    classNameBindings: ['active', ':span2'],
-    active: '',
+    classNameBindings:['active', ':span2'],
+    active:'',
 
-    alertsCount: function() {
-      if(this.get('content').routing == 'dashboard'){
+    alertsCount:function () {
+      if (this.get('content').routing == 'dashboard') {
         return App.router.get('mainDashboardController.alertsCount');
       }
     }.property(),
 
-    template: Ember.Handlebars.compile('<a {{action navigate view.content}} href="#">{{unbound view.content.label}}{{#if view.alertsCount}}<span class="label label-important alerts-count">{{view.alertsCount}}</span>{{/if}}</a>')
+    template:Ember.Handlebars.compile('<a href="#/main/{{unbound view.content.routing}}">{{unbound view.content.label}}{{#if view.alertsCount}}<span class="label label-important alerts-count">{{view.alertsCount}}</span>{{/if}}</a>')
   })
 });

+ 43 - 4
ambari-web/app/views/main/service/info/summary.js

@@ -22,8 +22,27 @@ App.MainServiceInfoSummaryView = Em.View.extend({
   templateName: require('templates/main/service/info/summary'),
   attributes: null,
   serviceStatus: {
-    hdfs: false
+    hdfs: false,
+    mapreduce: false,
+    hbase: false
   },
+  isHide: true,
+  showMoreStats: function() {
+    this.set('isHide', false);
+  },
+  moreStatsView: Em.View.extend({
+    tagName: "a",
+    template: Ember.Handlebars.compile('{{t services.service.summary.moreStats}}'),
+    attributeBindings: ['href'],
+    classNameBindings: ['hide'],
+    classNames: ['more-stats'],
+    hide: false,
+    click: function(event) {
+      this._parentView.set('isHide', false);
+      this.remove();
+    },
+    href: 'javascript:void(null)'
+  }),
   serviceName: function() {
     return App.router.mainServiceInfoSummaryController.get('content.serviceName');
   }.property('App.router.mainServiceInfoSummaryController.content'),
@@ -34,19 +53,39 @@ App.MainServiceInfoSummaryView = Em.View.extend({
   },
   loadServiceSummary: function(serviceName) {
     var summaryView = this;
+    var serviceStatus = summaryView.get('serviceStatus');
+    $.each(serviceStatus, function(key, value) {
+      if (key == serviceName) {
+        summaryView.set('serviceStatus.' + key, true);
+      } else {
+        summaryView.set('serviceStatus.' + key, false);
+      }
+    });
     jQuery.getJSON('data/services/summary/' + serviceName + '.json',
       function (data) {
         if (data[serviceName]) {
           var summary = data[serviceName];
           if(serviceName == 'hdfs') {
-            summaryView.set('serviceStatus.hdfs', true);
+            summary['start_time'] = summary['start_time'].toDaysHoursMinutes();
             summary['memory_heap_used'] = summaryView.convertByteToMbyte(summary['memory_heap_used']);
             summary['memory_heap_max'] = summaryView.convertByteToMbyte(summary['memory_heap_max']);
             summary['memory_heap_percent_used'] = summaryView.countPercentageRatio(summary['memory_heap_used'], summary['memory_heap_max']);
             summary['used_bytes'] = summaryView.convertByteToGbyte(summary['dfs_used_bytes'] + summary['nondfs_used_bytes']);
             summary['dfs_total_bytes'] = summaryView.convertByteToGbyte(summary['dfs_total_bytes']);
+            summary['dfs_percent_disk_used'] = parseFloat((100 - summary['dfs_percent_remaining']).toFixed(2));
+          } else if (serviceName == 'mapreduce') {
+            summary['start_time'] = summary['start_time'].toDaysHoursMinutes();
+            summary['memory_heap_used'] = summaryView.convertByteToMbyte(summary['memory_heap_used']);
+            summary['memory_heap_max'] = summaryView.convertByteToMbyte(summary['memory_heap_max']);
+            summary['memory_heap_percent_used'] = summaryView.countPercentageRatio(summary['memory_heap_used'], summary['memory_heap_max']);
+          } else if (serviceName == 'hbase') {
+            summary['memory_heap_used'] = summaryView.convertByteToMbyte(summary['memory_heap_used']);
+            summary['memory_heap_max'] = summaryView.convertByteToMbyte(summary['memory_heap_max']);
+            summary['memory_heap_percent_used'] = summaryView.countPercentageRatio(summary['memory_heap_used'], summary['memory_heap_max']);
+            summary['start_time'] = summary['start_time'].toDaysHoursMinutes();
+            summary['active_time'] = summary['active_time'].toDaysHoursMinutes();
           } else {
-            summaryView.set('serviceStatus.hdfs', false);
+
           }
           summaryView.set('attributes', summary);
         }
@@ -56,7 +95,7 @@ App.MainServiceInfoSummaryView = Em.View.extend({
   convertByteToMbyte: function(value) {
     var bytesInMbyte = 1048576;
     var newValue = value/bytesInMbyte;
-    return parseFloat(newValue.toFixed(1));
+    return parseFloat(newValue.toFixed(2));
   },
   convertByteToGbyte: function(value) {
     var bytesInGbyte = 1073741824;

+ 1 - 0
ambari-web/config.coffee

@@ -35,6 +35,7 @@ exports.config =
         before: [
           'vendor/scripts/console-helper.js',
           'vendor/scripts/jquery-1.7.2.min.js',
+          'vendor/scripts/jquery-ui-1.9.0.custom.js'
           'vendor/scripts/handlebars-1.0.0.beta.6.js',
           'vendor/scripts/ember-latest.js',
           'vendor/scripts/ember-data-latest.js',

+ 2 - 1
ambari-web/package.json

@@ -19,7 +19,8 @@
     "uglify-js-brunch": ">= 1.0 < 1.5",
     "clean-css-brunch": ">= 1.0 < 1.5",
     "ember-handlebars-brunch": "git://github.com/icholy/ember-handlebars-brunch.git",
-    "less-brunch": "git://github.com/brunch/less-brunch.git"
+    "less-brunch": "git://github.com/brunch/less-brunch.git",
+    "d3": ">=2.10.3"
   },
   "devDependencies": {
     "mocha": "0.14.0",