Browse Source

AMBARI-1059. Refactor cluster management. (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1418962 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 years ago
parent
commit
68ebef6cf0
31 changed files with 661 additions and 300 deletions
  1. 12 12
      ambari-web/app/assets/data/dashboard/services.json
  2. 19 1
      ambari-web/app/controllers/global/cluster_controller.js
  3. 0 1
      ambari-web/app/controllers/main/host.js
  4. 15 3
      ambari-web/app/controllers/main/service/add_controller.js
  5. 1 1
      ambari-web/app/controllers/wizard/step7_controller.js
  6. 1 0
      ambari-web/app/initialize.js
  7. 1 1
      ambari-web/app/mappers/services_mapper.js
  8. 87 0
      ambari-web/app/mappers/status_mapper.js
  9. 3 1
      ambari-web/app/messages.js
  10. 3 21
      ambari-web/app/models/host.js
  11. 28 14
      ambari-web/app/routes/add_service_routes.js
  12. 1 1
      ambari-web/app/routes/main.js
  13. 16 2
      ambari-web/app/styles/application.less
  14. 1 1
      ambari-web/app/templates/main/apps.hbs
  15. 23 0
      ambari-web/app/templates/main/apps/custom_rundate_popup.hbs
  16. 1 0
      ambari-web/app/templates/main/charts/linear_time.hbs
  17. 3 1
      ambari-web/app/templates/main/dashboard/service/hbase.hbs
  18. 3 1
      ambari-web/app/templates/main/dashboard/service/hdfs.hbs
  19. 3 1
      ambari-web/app/templates/main/dashboard/service/mapreduce.hbs
  20. 3 53
      ambari-web/app/templates/main/host.hbs
  21. 1 1
      ambari-web/app/templates/main/service/info/summary.hbs
  22. 76 54
      ambari-web/app/utils/data_table.js
  23. 1 0
      ambari-web/app/views.js
  24. 10 8
      ambari-web/app/views/common/chart/linear_time.js
  25. 24 0
      ambari-web/app/views/loading.js
  26. 6 40
      ambari-web/app/views/main.js
  27. 142 6
      ambari-web/app/views/main/apps_view.js
  28. 5 1
      ambari-web/app/views/main/dashboard/service/hbase.js
  29. 5 1
      ambari-web/app/views/main/dashboard/service/hdfs.js
  30. 5 1
      ambari-web/app/views/main/dashboard/service/mapreduce.js
  31. 162 73
      ambari-web/app/views/main/host.js

+ 12 - 12
ambari-web/app/assets/data/dashboard/services.json

@@ -25,7 +25,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "NAGIOS_SERVER",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "component" : [
                 {
@@ -66,7 +66,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "GANGLIA_MONITOR",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "component" : [
                 {
@@ -98,7 +98,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "GANGLIA_SERVER",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "component" : [
                 {
@@ -139,7 +139,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "NAMENODE",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "metrics" : {
                 "boottime" : 1.352763164E9,
@@ -341,7 +341,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "SECONDARY_NAMENODE",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "component" : [
                 {
@@ -373,7 +373,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "DATANODE",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "metrics" : {
                 "boottime" : 1.352763164E9,
@@ -516,7 +516,7 @@
                 "desired_state" : "INSTALLED",
                 "state" : "INSTALLED",
                 "component_name" : "HDFS_CLIENT",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "component" : [
                 {
@@ -557,7 +557,7 @@
                 "desired_state" : "INSTALLED",
                 "state" : "INSTALLED",
                 "component_name" : "MAPREDUCE_CLIENT",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "component" : [
                 {
@@ -589,7 +589,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "JOBTRACKER",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "metrics" : {
                 "boottime" : 1.352763164E9,
@@ -786,7 +786,7 @@
                 "desired_state" : "STARTED",
                 "state" : "STARTED",
                 "component_name" : "TASKTRACKER",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "metrics" : {
                 "boottime" : 1.352763164E9,
@@ -1031,7 +1031,7 @@
                 "role_id" : "7",
                 "state" : "STARTED",
                 "component_name" : "HBASE_MASTER",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "metrics" : {
                 "boottime" : 1.35085737E9,
@@ -1692,7 +1692,7 @@
                 "role_id" : "35",
                 "state" : "STARTED",
                 "component_name" : "HBASE_REGIONSERVER",
-                "host_name" : "dev.hortonworks.com"
+                "host_name" : "dev1.hortonworks.com"
               },
               "metrics" : {
                 "boottime" : 1.35085737E9,

+ 19 - 1
ambari-web/app/controllers/global/cluster_controller.js

@@ -189,6 +189,20 @@ App.ClusterController = Em.Controller.extend({
     }
   }.observes('nagiosUrl'),
 
+  updateStatus: function(){
+    var servicesUrl1 = this.getUrl('/data/dashboard/services.json', '/services?ServiceInfo/service_name!=MISCELLANEOUS&ServiceInfo/service_name!=DASHBOARD&fields=*,components/host_components/*');
+
+    var self = this;
+    App.HttpClient.get(servicesUrl1, App.statusMapper, {
+      complete:function (jqXHR, textStatus) {
+        console.log('update finished')
+        setTimeout(function(){
+          self.updateStatus();
+        }, 1000);
+      }
+    }, null);
+  },
+
   /**
    *
    *  load all data and update load status
@@ -201,7 +215,7 @@ App.ClusterController = Em.Controller.extend({
 
     var clusterUrl = this.getUrl('/data/clusters/cluster.json', '?fields=Clusters');
     var hostsUrl = this.getUrl('/data/hosts/hosts.json', '/hosts?fields=*');
-    var servicesUrl1 = this.getUrl('/data/dashboard/services.json', '/services?ServiceInfo/service_name!=MISCELLANEOUS&ServiceInfo/service_name!=DASHBOARD&fields=components/host_components/*');
+    var servicesUrl1 = this.getUrl('/data/dashboard/services.json', '/services?ServiceInfo/service_name!=MISCELLANEOUS&ServiceInfo/service_name!=DASHBOARD&fields=*,components/host_components/*');
     var servicesUrl2 = this.getUrl('/data/dashboard/serviceComponents.json', '/services?ServiceInfo/service_name!=MISCELLANEOUS&ServiceInfo/service_name!=DASHBOARD&fields=components/ServiceComponentInfo');
     var usersUrl = App.testMode ? '/data/users/users.json' : App.apiPrefix + '/users/?fields=*';
     var racksUrl = "/data/racks/racks.json";
@@ -354,6 +368,10 @@ App.ClusterController = Em.Controller.extend({
     // Hack for services END   //
     /////////////////////////////
 
+    setTimeout(function(){
+      self.updateStatus();
+    }, 1000);
+
   },
 
   clusterName:function () {

+ 0 - 1
ambari-web/app/controllers/main/host.js

@@ -42,7 +42,6 @@ App.MainHostController = Em.ArrayController.extend(App.Pagination, {
         componentName: item.get('componentName'),
         checkedForHostFilter: item.get('checkedForHostFilter')
       });
-      console.log(o);
       ret.push(o);
     });
     return ret;

+ 15 - 3
ambari-web/app/controllers/main/service/add_controller.js

@@ -273,7 +273,7 @@ App.AddServiceController = Em.Controller.extend({
    */
   loadServices: function () {
     var servicesInfo = App.db.getService();
-    if(!servicesInfo){
+    if(!servicesInfo || !servicesInfo.length){
       servicesInfo = require('data/mock/services').slice(0);
       servicesInfo.forEach(function(item, index){
         servicesInfo[index].isSelected = App.Service.find().someProperty('id', item.serviceName);
@@ -553,7 +553,17 @@ App.AddServiceController = Em.Controller.extend({
     this.set('content.clients', clients);
     console.log("AddServiceController.loadClients: loaded list ", clients);
   },
-
+  dataLoading: function(){
+    var dfd = $.Deferred();
+    this.connectOutlet('loading');
+    var interval = setInterval(function(){
+      if (App.router.get('clusterController.isLoaded')){
+        dfd.resolve();
+        clearInterval(interval);
+      }
+    },50);
+    return dfd.promise();
+  },
   /**
    * Generate clients list for selected services and save it to model
    * @param stepController step4WizardController
@@ -585,14 +595,16 @@ App.AddServiceController = Em.Controller.extend({
   loadAllPriorSteps: function () {
     var step = this.get('currentStep');
     switch (step) {
+      case '7':
       case '6':
       case '5':
         this.loadClusterInfo();
       case '4':
         this.loadServiceConfigProperties();
       case '3':
+        this.loadServices();
         this.loadClients();
-        this.loadSlaveComponentHosts();
+        this.loadSlaveComponentHosts();//depends on loadServices
       case '2':
         this.loadMasterComponentHosts();
         this.loadConfirmedHosts();

+ 1 - 1
ambari-web/app/controllers/wizard/step7_controller.js

@@ -70,7 +70,7 @@ App.WizardStep7Controller = Em.Controller.extend({
     console.log("TRACE: Loading step7: Configure Services");
     this.clearStep();
     var serviceConfigs = this.get('serviceConfigs');
-    var advancedConfig = this.get('content.advancedServiceConfig');
+    var advancedConfig = this.get('content.advancedServiceConfig') || [];
     advancedConfig.forEach(function (_config) {
       if (_config) {
         var service = serviceConfigs.findProperty('serviceName', _config.serviceName);

+ 1 - 0
ambari-web/app/initialize.js

@@ -37,6 +37,7 @@ require('router');
 
 require('mappers/server_data_mapper');
 require('mappers/services_mapper');
+require('mappers/status_mapper');
 require('mappers/hosts_mapper');
 require('mappers/cluster_mapper');
 require('mappers/jobs_mapper');

+ 1 - 1
ambari-web/app/mappers/services_mapper.js

@@ -42,7 +42,7 @@ App.servicesMapper = App.QuickDataMapper.create({
   config:{
     id:'ServiceInfo.service_name',
     service_name:'ServiceInfo.service_name',
-    $work_status:'DEAD',
+    work_status:'ServiceInfo.state',
     $service_audit:[ 1, 2, 3 ],
     $alerts:[ 1, 2, 3 ],
     components_key:'components',

+ 87 - 0
ambari-web/app/mappers/status_mapper.js

@@ -0,0 +1,87 @@
+/**
+ * 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.
+ */
+
+App.statusMapper = App.QuickDataMapper.create({
+
+  config:{
+    id:'ServiceInfo.service_name',
+    work_status:'ServiceInfo.state'
+  },
+
+  config2:{
+    id:'ServiceComponentInfo.component_name',
+    work_status:'host_components[0].HostRoles.state'
+  },
+
+  config3:{
+    id:'id',
+    work_status:'HostRoles.state'
+  },
+
+  map:function (json) {
+
+    if (json.items) {
+      var result = [];
+      json.items.forEach(function (item) {
+
+        item.host_components = [];
+        item.components.forEach(function (component) {
+          component.host_components.forEach(function (host_component) {
+            host_component.id = host_component.HostRoles.component_name + "_" + host_component.HostRoles.host_name;
+            item.host_components.push(host_component.id);
+          }, this)
+        }, this);
+
+        result.push(this.parseIt(item, this.config));
+      }, this);
+
+      //console.log(result)
+      var services = App.Service.find();
+      result.forEach(function(item){
+        services.findProperty('id', item.id).set('workStatus', item.work_status);
+      })
+
+      result = [];
+      json.items.forEach(function (item) {
+        item.components.forEach(function (component) {
+          result.push(this.parseIt(component, this.config2));
+        }, this)
+      }, this);
+
+      //console.log(result)
+      var components = App.Component.find();
+      result.forEach(function(item){
+        components.findProperty('id', item.id).set('workStatus', item.work_status);
+      })
+
+      result = [];
+      json.items.forEach(function (item) {
+        item.components.forEach(function (component) {
+          component.host_components.forEach(function (host_component) {
+            result.push(this.parseIt(host_component, this.config3));
+          }, this)
+        }, this)
+      }, this);
+
+      //console.log(result)
+      var hostComponents = App.HostComponent.find();
+      result.forEach(function(item){
+        hostComponents.findProperty('id', item.id).set('workStatus', item.work_status);
+      })
+    }
+  }
+});

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

@@ -349,5 +349,7 @@ Em.I18n.translations = {
   'timeRange.presets.1day':'1d',
   'timeRange.presets.1week':'1wk',
   'timeRange.presets.1month':'1mo',
-  'timeRange.presets.1year':'1yr'
+  'timeRange.presets.1year':'1yr',
+
+  'apps.filters.customRunDate':'Run Date custom filter'
 };

+ 3 - 21
ambari-web/app/models/host.js

@@ -60,7 +60,7 @@ App.Host = DS.Model.extend({
     /**
      * Do nothing until load
      */
-    if(!this.get('isLoaded') || !this.get('components').everyProperty('isLoaded', true)){
+    if(!this.get('isLoaded')){
       return;
     }
 
@@ -70,7 +70,7 @@ App.Host = DS.Model.extend({
     var masterComponents = components.filterProperty('isMaster', true);
     if(components.everyProperty('workStatus', App.Component.Status.started)){
       status = 'LIVE';
-    } else if(false && this.get('isNotHeartBeating')){ //todo uncomment on real data
+    } else if(this.get('isNotHeartBeating')){
       status = 'DEAD-YELLOW';
     } else if(masterComponents.length > 0 && !masterComponents.everyProperty('workStatus', App.Component.Status.started)){
       status = 'DEAD';
@@ -89,22 +89,4 @@ App.Host = DS.Model.extend({
   }.property('healthStatus')
 });
 
-App.Host.FIXTURES = [/*
-  {
-    id: 1,
-    host_name: 'dev.hortonworks.com',
-    cluster_id: 1,
-    components:[1, 2, 3, 4, 5],
-    cpu: '2x2.5GHz',
-    memory: '8GB',
-    disk_usage: '40',
-    load_avg: '0.2, 1.2, 2.4',
-    ip: '123.123.123.123',
-    health_status: 'LIVE',
-    cpu_usage: 33,
-    memory_usage: 26,
-    network_usage: 36,
-    io_usage: 39,
-    last_heart_beat_time : 1351536732366
-  }*/
-];
+App.Host.FIXTURES = [];

+ 28 - 14
ambari-web/app/routes/add_service_routes.js

@@ -41,8 +41,10 @@ module.exports = Em.Route.extend({
       var controller = router.get('addServiceController');
       controller.setCurrentStep('1', false);
       controller.set('hideBackButton', true);
-      controller.loadAllPriorSteps();
-      controller.connectOutlet('wizardStep4', controller.get('content.services'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep4', controller.get('content.services'));
+      })
     },
     next: function (router) {
       var addServiceController = router.get('addServiceController');
@@ -60,9 +62,11 @@ module.exports = Em.Route.extend({
       console.log('in addService.step2:connectOutlets');
       var controller = router.get('addServiceController');
       controller.setCurrentStep('2', false);
-      controller.loadAllPriorSteps();
       controller.set('hideBackButton', false);
-      controller.connectOutlet('wizardStep5', controller.get('content'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep5', controller.get('content'));
+      })
 
     },
     back: Em.Router.transitionTo('step1'),
@@ -81,8 +85,10 @@ module.exports = Em.Route.extend({
       console.log('in addService.step3:connectOutlets');
       var controller = router.get('addServiceController');
       controller.setCurrentStep('3', false);
-      controller.loadAllPriorSteps();
-      controller.connectOutlet('wizardStep6', controller.get('content'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep6', controller.get('content'));
+      })
     },
     back: function(router){
       var controller = router.get('addServiceController');
@@ -112,8 +118,10 @@ module.exports = Em.Route.extend({
       console.log('in addService.step4:connectOutlets');
       var controller = router.get('addServiceController');
       controller.setCurrentStep('4', false);
-      controller.loadAllPriorSteps();
-      controller.connectOutlet('wizardStep7', controller.get('content'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep7', controller.get('content'));
+      })
     },
     back: Em.Router.transitionTo('step3'),
     next: function (router) {
@@ -130,8 +138,10 @@ module.exports = Em.Route.extend({
       console.log('in addService.step5:connectOutlets');
       var controller = router.get('addServiceController');
       controller.setCurrentStep('5', false);
-      controller.loadAllPriorSteps();
-      controller.connectOutlet('wizardStep8', controller.get('content'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep8', controller.get('content'));
+      })
     },
     back: Em.Router.transitionTo('step4'),
     next: function (router) {
@@ -149,8 +159,10 @@ module.exports = Em.Route.extend({
       console.log('in addService.step6:connectOutlets');
       var controller = router.get('addServiceController');
       controller.setCurrentStep('6', false);
-      controller.loadAllPriorSteps();
-      controller.connectOutlet('wizardStep9', controller.get('content'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep9', controller.get('content'));
+      })
     },
     back: Em.Router.transitionTo('step5'),
     retry: function(router,context) {
@@ -176,8 +188,10 @@ module.exports = Em.Route.extend({
       console.log('in addService.step7:connectOutlets');
       var controller = router.get('addServiceController');
       controller.setCurrentStep('7', false);
-      controller.loadAllPriorSteps();
-      controller.connectOutlet('wizardStep10', controller.get('content'));
+      controller.dataLoading().done(function () {
+        controller.loadAllPriorSteps();
+        controller.connectOutlet('wizardStep10', controller.get('content'));
+      })
     },
     back: Em.Router.transitionTo('step6'),
     complete: function (router, context) {

+ 1 - 1
ambari-web/app/routes/main.js

@@ -97,7 +97,7 @@ module.exports = Em.Route.extend({
 
     showDetails:function (router, event) {
       router.get('mainHostDetailsController').setBack(true);
-      router.transitionTo('hostDetails.index', event.context)
+      router.transitionTo('hostDetails.summary', event.context)
     },
 
     addHost:function (router) {

+ 16 - 2
ambari-web/app/styles/application.less

@@ -498,7 +498,13 @@ a:focus {
     .summary {
       line-height: 21px;
     }
-
+    .clearfix {
+      padding-bottom: 8px;
+    }
+    #hdfs-info, #mapreduce-info, #hbase-info {
+      display: none;
+      position: relative;
+    }
     table.table {
       margin-top: 14px;
       color: #7b7b7b;
@@ -515,7 +521,7 @@ a:focus {
 
     .dashboard-mini-chart {
       right: 0;
-      top: 27px;
+      top: 7px;
       position: absolute;
       overflow: visible; // for quick links
       text-align: center;
@@ -539,6 +545,8 @@ a:focus {
 #summary-info {
   border-top: none;
   border-collapse: collapse;
+  color: #7B7B7B;
+  font-size: 13px;
 
   td.summary-label {
     width: 180px;
@@ -663,6 +671,12 @@ a:focus {
     width: 30%;
     z-index: 2;
   }
+  .chart-legend {
+    position: absolute;
+    top: 180px;
+    left: 35%;
+    z-index: 2;
+  }
   .rickshaw_legend {
     background-color: #999999 !important;
   }

+ 1 - 1
ambari-web/app/templates/main/apps.hbs

@@ -87,7 +87,7 @@
       <th class="notActive"><div class="view-wrapper">{{view view.inputFilterView viewName="inputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_inputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-input"></a></th>
       <th class="notActive"><div class="view-wrapper">{{view view.outputFilterView viewName="outputFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_outputFilterViewInstance" class="ui-icon ui-icon-circle-close ui-output"></a></th>
       <th class="notActive"><div class="view-wrapper">{{view view.durationFilterView viewName="durationFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_durationFilterViewInstance" class="ui-icon ui-icon-circle-close ui-duration"></a></th>
-      <th class="notActive"><div class="view-wrapper">{{view view.rundateSelectView viewName="rundateSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_rundateSelectViewInstance" class="ui-icon ui-icon-circle-close ui-rundate"></a></th>
+      <th class="notActive"><div class="view-wrapper">{{view view.rundateSelectView viewName="rundateSelectViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_rundateSelectViewInstance" class="ui-icon ui-icon-circle-close ui-rundate"></a><input id="custom_rundate_filter" type="hidden"></th>
     </tr>
     </thead>
     <tbody>

+ 23 - 0
ambari-web/app/templates/main/apps/custom_rundate_popup.hbs

@@ -0,0 +1,23 @@
+{{!
+* 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.
+}}
+
+<p>Select date and time range:</p>
+From {{view view.lowerBoundView}}
+To {{view view.upperBoundView}}
+<p>either specify exact date and time:</p>
+Equal {{view view.equalView}}

+ 1 - 0
ambari-web/app/templates/main/charts/linear_time.hbs

@@ -18,6 +18,7 @@
 <div id="{{unbound view.id}}-container" class="chart-container">
   <div id="{{unbound view.id}}-yaxis" class="chart-y-axis"></div>
   <div id="{{unbound view.id}}-xaxis" class="chart-x-axis"></div>
+  <div id="{{unbound view.id}}-legend" class="chart-legend"></div>
   <div id="{{unbound view.id}}-overlay" class="chart-overlay"></div>
   <div id="{{unbound view.id}}-chart" class="chart"></div>
   <div id="{{unbound view.id}}-title" class="chart-title">{{view.title}}</div>

+ 3 - 1
ambari-web/app/templates/main/dashboard/service/hbase.hbs

@@ -17,7 +17,7 @@
 }}
 
 {{#unless view.showOnlyRows}}
-<div class="clearfix">
+<div class="clearfix" {{action toggleInfoView target="view"}}>
   <div class="name span2">
     {{view App.MainDashboardServiceHealthView serviceBinding="view.service"}}
     <a {{action selectService view.service href=true}}>{{view.service.displayName}}</a>
@@ -29,6 +29,7 @@
     {{view.summaryHeader}}
   </div>
 </div>
+<div id="hbase-info">
 <table class="table no-borders">
   <tbody>
 {{/unless}}
@@ -101,4 +102,5 @@
   {{/view}}
   {{/if}}
 </div>
+</div>
 {{/unless}}

+ 3 - 1
ambari-web/app/templates/main/dashboard/service/hdfs.hbs

@@ -17,7 +17,7 @@
 }}
 
 {{#unless view.showOnlyRows}}
-<div class="clearfix">
+<div class="clearfix" {{action toggleInfoView target="view"}}>
   <div class="name span2">
     {{view App.MainDashboardServiceHealthView serviceBinding="view.service"}}
     <a {{action selectService view.service href=true}}>{{view.service.displayName}}</a>
@@ -29,6 +29,7 @@
     {{view.summaryHeader}}
   </div>
 </div>
+<div id="hdfs-info">
 <table class="table no-borders">
   <tbody>
 {{/unless}}
@@ -143,4 +144,5 @@
     {{/view}}
   {{/if}}
 </div>
+</div>
 {{/unless}}

+ 3 - 1
ambari-web/app/templates/main/dashboard/service/mapreduce.hbs

@@ -17,7 +17,7 @@
 }}
 
 {{#unless view.showOnlyRows}}
-<div class="clearfix">
+<div class="clearfix" {{action toggleInfoView target="view"}}>
   <div class="name span2">
     {{view App.MainDashboardServiceHealthView serviceBinding="view.service"}}
     <a {{action selectService view.service href=true}}>{{view.service.displayName}}</a>
@@ -29,6 +29,7 @@
     {{view.summaryHeader}}
   </div>
 </div>
+<div id="mapreduce-info">
 <table class="table no-borders">
   <tbody>
 {{/unless}}
@@ -130,4 +131,5 @@
   {{/view}}
   {{/if}}
 </div>
+</div>
 {{/unless}}

+ 3 - 53
ambari-web/app/templates/main/host.hbs

@@ -42,58 +42,8 @@
       <th class="notActive"><div class="view-wrapper">{{view view.ramFilterView viewName="ramFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_ramFilterViewInstance" class="ui-icon ui-icon-circle-close ui-ram"></a></th>
       <th></th>
       <th></th>
-      <th><input id="components_filter" type="hidden" />
-        <div {{bindAttr class="view.btnGroupClass"}} >
-          <button class="btn btn-info single-btn-group" {{action "clickFilterButton" target="view"}}>
-            Components
-            <span class="caret"></span>
-          </button>
-          <ul class="dropdown-menu filter-components" id="filter-dropdown">
-            <li>
-              <label class="checkbox">
-                {{view Ember.Checkbox checkedBinding="view.allComponentsChecked"}} All
-              </label>
-            </li>
-            <li>
-              <label class="checkbox">
-                {{view Ember.Checkbox checkedBinding="view.masterComponentsChecked"}} Master Components:
-              </label>
-              <ul>
-                {{#each component in masterComponents}}
-                <li>
-                  <label class="checkbox">
-                    {{view Ember.Checkbox checkedBinding="component.checkedForHostFilter" }} {{unbound component.displayName}}
-                  </label>
-                </li>
-                {{/each}}
-              </ul>
-            </li>
-
-            <li>
-              <label class="checkbox">
-                {{view Ember.Checkbox checkedBinding="view.slaveComponentsChecked"}} Slave Components:
-              </label>
-              <ul>
-                {{#each component in slaveComponents}}
-                <li>
-                  <label class="checkbox">
-                    {{view Ember.Checkbox checkedBinding="component.checkedForHostFilter" }} {{unbound component.displayName}}
-                  </label>
-                </li>
-                {{/each}}
-              </ul>
-            </li>
-
-            <li class="align-right">
-              <button class="btn" {{action "closeFilters" target="view"}}>
-                Cancel
-              </button>
-              <button class="btn btn-primary" {{action "applyFilters" target="view"}}>
-                Apply
-              </button>
-            </li>
-          </ul>
-        </div>
+      <th class="notActive"><input id="components_filter" type="hidden" />
+          <div class="view-wrapper">{{view view.componentsFilterView viewName="componentsFilterViewInstance"}}</div> <a href="#" {{action "clearFilterButtonClick" target="view"}} id="view_componentsFilterViewInstance" class="ui-icon ui-icon-circle-close ui-components"></a>
       </th>
     </tr>
     </thead>
@@ -101,7 +51,7 @@
     {{#each host in controller}}
     {{#view view.HostView contentBinding="host"}}
     <tr>
-      <td>
+      <td class="name">
         <span {{bindAttr class="host.healthClass"}}></span>
         <a href="#" {{action "showDetails" host}}>{{unbound host.hostName}}</a>
       </td>

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

@@ -38,7 +38,7 @@
     <h4>{{controller.content.label}} Summary</h4>
   </div>
   <div class="service-content">
-    <table id="summary-info" class="table table-bordered table-striped table-condensed">
+    <table id="summary-info" class="table no-borders table-condensed">
       <tbody>
       {{#unless view.serviceStatus.oozie}}
       {{#unless view.serviceStatus.hive}}

+ 76 - 54
ambari-web/app/utils/data_table.js

@@ -32,8 +32,8 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
   },
 
   // @see utils/date.js
-  "ambari-date-pre": function (date_string) {
-    date_string = $(date_string).text(); // strip Ember script tags
+  "ambari-datetime-pre": function (date_string) {
+    date_string = $.trim(date_string.replace(/<script[^>]*?>.*<\/script>/g, ''));
     var date = date_string.substring(4);
     var month = date.substring(1, 4);
     var day = date.substring(5, 7);
@@ -47,11 +47,11 @@ jQuery.extend(jQuery.fn.dataTableExt.oSort, {
     return year + month + day + hours + minutes;
   },
 
-  "ambari-date-asc": function (a, b) {
+  "ambari-datetime-asc": function (a, b) {
     return a - b;
   },
 
-  "ambari-date-desc": function (a, b) {
+  "ambari-datetime-desc": function (a, b) {
     return b - a;
   },
   /**
@@ -157,44 +157,49 @@ jQuery.extend($.fn.dataTableExt.afnFiltering.push(
         {iColumn: '0', elementId: 'star_filter', type: 'star'},
         {iColumn: '2', elementId: 'cpu_filter', type: 'number'},
         {iColumn: '4', elementId: 'user_filter', type: 'multiple'},
+        {iColumn: '6', elementId: 'components_filter', type: 'multiple'},
         {iColumn: '5', elementId: 'jobs_filter', type: 'number' },
         {iColumn: '3', elementId: 'ram_filter', type: 'ambari-bandwidth' },
         {iColumn: '6', elementId: 'input_filter', type: 'ambari-bandwidth' },
         {iColumn: '7', elementId: 'output_filter', type: 'ambari-bandwidth' },
         {iColumn: '8', elementId: 'duration_filter', type: 'time' },
-        //{iColumn: '9', elementId: 'rundate_filter', type: 'ambari-date' }
+        {iColumn: '9', elementId: 'rundate_filter', type: 'ambari-datetime' }
       ];
       var match = true;
       for (var i = 0; i < inputFilters.length; i++) {
+        var cellValue = jQuery('#' + inputFilters[i].elementId).val();
+        if(cellValue === undefined){
+          continue;
+        }
         switch (inputFilters[i].type) {
-          case 'ambari-date':
-            if (jQuery('#' + inputFilters[i].elementId).val() !== 'Any' && match) {
-              dateFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+          case 'ambari-datetime':
+            if (cellValue !== 'Any' && match) {
+              ambariDateFilter(cellValue, aData[inputFilters[i].iColumn]);
             }
             break;
           case 'number':
-            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
-              numberFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            if (cellValue && match) {
+              numberFilter(cellValue, aData[inputFilters[i].iColumn]);
             }
             break;
           case 'multiple':
-            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
-              multipleFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            if (cellValue && match) {
+              multipleFilter(cellValue, aData[inputFilters[i].iColumn]);
             }
             break;
           case 'time':
-            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
-              timeFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            if (cellValue && match) {
+              timeFilter(cellValue, aData[inputFilters[i].iColumn]);
             }
             break;
           case 'ambari-bandwidth':
-            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
-              bandwidthFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            if (cellValue && match) {
+              bandwidthFilter(cellValue, aData[inputFilters[i].iColumn]);
             }
             break;
           case 'star':
-            if (jQuery('#' + inputFilters[i].elementId).val() && match) {
-              starFilter(jQuery('#' + inputFilters[i].elementId).val(), aData[inputFilters[i].iColumn]);
+            if (cellValue && match) {
+              starFilter(cellValue, aData[inputFilters[i].iColumn]);
             }
             break;
         }
@@ -211,7 +216,7 @@ jQuery.extend($.fn.dataTableExt.afnFiltering.push(
         match = false;
         rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
         for (var i = 0; i < options.length; i++) {
-          if (options[i] === rowValue) match = true;
+          if (rowValue.indexOf(options[i]) !== -1) match = true;
         }
       }
 
@@ -288,41 +293,58 @@ jQuery.extend($.fn.dataTableExt.afnFiltering.push(
         }
       }
 
-      function dateFilter(condition, rowValue) {
-        refinedRowValue = $.trim(rowValue.replace(/<script[^>]*?>.*<\/script>/g, ''));
-        var nowTime = new Date().getTime();
-        var oneDayPast = nowTime - 86400000;
-        var twoDaysPast = nowTime - 172800000;
-        var sevenDaysPast = nowTime - 604800000;
-        var fourteenDaysPast = nowTime - 1209600000;
-        var thirtyDaysPast = nowTime - 2592000000;
-        rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
-        var lastChar = rowValue.charAt(rowValue.length - 1);
-        rowValue = lastChar === '*' ? rowValue.substr(0, rowValue.length - 1) : rowValue;
-        rowValue = new Date(refinedRowValue).getTime();
-        match = false;
-        switch (condition) {
-          case 'Any':
-            match = true;
-            break;
-          case 'Past 1 Day':
-            if (nowTime > rowValue && rowValue > oneDayPast) match = true;
-            break;
-          case 'Past 2 Days':
-            if (nowTime > rowValue && rowValue > twoDaysPast) match = true;
-            break;
-          case 'Past 7 Days':
-            if (nowTime > rowValue && rowValue > sevenDaysPast) match = true;
-            break;
-          case 'Past 14 Days':
-            if (nowTime > rowValue && rowValue > fourteenDaysPast) match = true;
-            break;
-          case 'Past 30 Days':
-            if (nowTime > rowValue && rowValue > thirtyDaysPast) match = true;
-            break;
-          case 'Running Now':
-            if (lastChar === '*') match = true;
-            break;
+      function ambariDateFilter(condition, rowValue) {
+        if (typeof rowValue !== 'undefined') {
+          var refinedRowValue = $.trim(rowValue.replace(/<script[^>]*?>.*<\/script>/g, '').replace('&nbsp;', ''));
+
+          var nowTime = new Date().getTime();
+          var oneDayPast = nowTime - 86400000;
+          var twoDaysPast = nowTime - 172800000;
+          var sevenDaysPast = nowTime - 604800000;
+          var fourteenDaysPast = nowTime - 1209600000;
+          var thirtyDaysPast = nowTime - 2592000000;
+          rowValue = (jQuery(rowValue).text()) ? jQuery(rowValue).text() : rowValue;
+          var lastChar = rowValue.charAt(rowValue.length - 1);
+          rowValue = lastChar === '*' ? rowValue.substr(0, rowValue.length - 1) : rowValue;
+          rowValue = new Date(refinedRowValue).getTime();
+
+          match = false;
+          switch (condition) {
+            case 'Any':
+              match = true;
+              break;
+            case 'Past 1 Day':
+              if (nowTime > rowValue && rowValue > oneDayPast) match = true;
+              break;
+            case 'Past 2 Days':
+              if (nowTime > rowValue && rowValue > twoDaysPast) match = true;
+              break;
+            case 'Past 7 Days':
+              if (nowTime > rowValue && rowValue > sevenDaysPast) match = true;
+              break;
+            case 'Past 14 Days':
+              if (nowTime > rowValue && rowValue > fourteenDaysPast) match = true;
+              break;
+            case 'Past 30 Days':
+              if (nowTime > rowValue && rowValue > thirtyDaysPast) match = true;
+              break;
+            case 'Running Now':
+              if (lastChar === '*') match = true;
+              break;
+            case 'Custom':
+              var options = jQuery('#custom_rundate_filter').val();
+              var optionsArray = options.split(',');
+
+              if (optionsArray.length == 1) {
+                equal = optionsArray[0];
+                if (equal == rowValue) match = true;
+              } else if (optionsArray.length == 2) {
+                lowerBound = optionsArray[0];
+                upperBound = optionsArray[1];
+                if (upperBound > rowValue && rowValue > lowerBound) match = true;
+              }
+              break;
+          }
         }
       }
 

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

@@ -127,3 +127,4 @@ require('views/wizard/step7_view');
 require('views/wizard/step8_view');
 require('views/wizard/step9_view');
 require('views/wizard/step10_view');
+require('views/loading');

+ 10 - 8
ambari-web/app/views/common/chart/linear_time.js

@@ -222,10 +222,12 @@ App.ChartLinearTimeView = Ember.View.extend({
             var chartOverlayId = "#" + this.id + "-overlay";
             var xaxisElementId = "#" + this.id + "-xaxis";
             var yaxisElementId = "#" + this.id + "-yaxis";
+            var legendElementId = "#" + this.id + "-legend";
             var chartElement = document.querySelector(chartId);
             var overlayElement = document.querySelector(chartOverlayId);
             var xaxisElement = document.querySelector(xaxisElementId);
             var yaxisElement = document.querySelector(yaxisElementId);
+            var legendElement = document.querySelector(legendElementId);
 
             this._graph = new Rickshaw.Graph({
               height: 150,
@@ -249,24 +251,24 @@ App.ChartLinearTimeView = Ember.View.extend({
 
             overlayElement.addEventListener('mousemove', function () {
               $(xaxisElement).removeClass('hide');
-              $(yaxisElement).removeClass('hide');
+              $(legendElement).removeClass('hide');
               $(chartElement).children("div").removeClass('hide');
             });
             overlayElement.addEventListener('mouseout', function () {
-              $(xaxisElement).addClass('hide');
-              $(yaxisElement).addClass('hide');
-              $(chartElement).children("div").addClass('hide');
+              //$(xaxisElement).addClass('hide');
+              $(legendElement).addClass('hide');
+              //$(chartElement).children("div").addClass('hide');
             });
             // Hide axes
             this._graph.onUpdate(function () {
-              $(xaxisElement).addClass('hide');
-              $(yaxisElement).addClass('hide');
-              $(chartElement).children('div').addClass('hide');
+              //$(xaxisElement).addClass('hide');
+              $(legendElement).addClass('hide');
+              //$(chartElement).children('div').addClass('hide');
             });
 
             new Rickshaw.Graph.Legend({
               graph: this._graph,
-              element: xaxisElement
+              element: legendElement
             });
 
             // The below code will be needed if we ever use curve

+ 24 - 0
ambari-web/app/views/loading.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.LoadingView = Em.View.extend({
+    tagName: 'h2',
+    template: Ember.Handlebars.compile('Loading...')
+});

+ 6 - 40
ambari-web/app/views/main.js

@@ -24,48 +24,14 @@ App.MainView = Em.View.extend({
 });
 
 App.MainBackgroundOperation = Em.View.extend({
-  content:null,
-  classNames:['background-operations'],
-  classNameBindings:['isOpen'],
-  isOpen:false,
-  iconClass:function () {
+  content: null,
+  classNames: ['background-operations'],
+  classNameBindings: ['isOpen'],
+  isOpen: false,
+  iconClass: function(){
     return this.get('isOpen') ? 'icon-minus' : 'icon-plus';
   }.property('isOpen'),
-  showOperationLog:function () {
+  showOperationLog:function(){
     this.set('isOpen', !this.get('isOpen'))
-    var operation = this.get('content');
-    var self = this;
-    if (!this.get('isOpenShowLog') && !this.get('logDetails')) {
-
-      jQuery.getJSON('data/hosts/background_operations/logs/task' + operation.taskId + '.json',
-        function (data) {
-          var fields = ['stdout', 'stderror'];
-          if (data) {
-            fields.forEach(function (fieldName) {
-              if (data.fieldName) {
-                data.fieldName = data.fieldName.highlight(["fail", "err"]);
-              }
-            });
-          }
-
-          if (App.testMode) {
-            var stdError = "Donec quis error tincidunt dolor. word Proin vel fail dignissim metus. In hac err habitasse platea dictumst. Err Mauris error tortor dui, commodo vitae failure placerat ut, venenatis nec err dolor. failure Lorem ipsum dolor sit amet, fail err consectetur fail adipiscing elit. Error Vivamus vel velit ipsum, id laoreet velit. Nullam vel err augue a tortor mattis semper fail, in nec neque.";
-            stdError = stdError.highlight(["err", "fail"]);
-            data.stderror = stdError;
-          }
-
-          self.set('logDetails', data);
-        }
-      );
-    }
-
-    if (App.testMode) {
-      var stdError = "Donec quis error tincidunt dolor. word Proin vel fail dignissim metus. In hac err habitasse platea dictumst. Err Mauris error tortor dui, commodo vitae failure placerat ut, venenatis nec err dolor. failure Lorem ipsum dolor sit amet, fail err consectetur fail adipiscing elit. Error Vivamus vel velit ipsum, id laoreet velit. Nullam vel err augue a tortor mattis semper fail, in nec neque.";
-      stdError = stdError.highlight(["err", "fail"]);
-      var data = {stderror:stdError};
-      self.set('logDetails', data);
-    }
-
-    this.set('isOpenShowLog', !this.get('isOpenShowLog'));
   }
 });

+ 142 - 6
ambari-web/app/views/main/apps_view.js

@@ -345,8 +345,8 @@ App.MainAppsView = Em.View.extend({
       "oLanguage": {
         "sSearch": "Search:",
         "sLengthMenu": "Show: _MENU_",
-        "sInfo": "_START_ - _END_ of _TOTAL_ (_TOTAL_ total)",
-        "sInfoEmpty": "0 - _END_ of _TOTAL_ (_TOTAL_ total)",
+        "sInfo": "_START_ - _END_ of _TOTAL_",
+        "sInfoEmpty": "0 - _END_ of _TOTAL_",
         "sInfoFiltered": "",
         "oPaginate":{
           "sPrevious": "<i class='icon-arrow-left'></i>",
@@ -366,7 +366,7 @@ App.MainAppsView = Em.View.extend({
         { "sType":"ambari-bandwidth" },
         { "sType":"ambari-bandwidth" },
         null,
-        { "sType":"ambari-date" }
+        { "sType":"ambari-datetime" }
       ]
     });
     this.set('oTable', oTable);
@@ -484,7 +484,7 @@ App.MainAppsView = Em.View.extend({
    * Filter-field for RunDate
    */
   rundateSelectView: Em.Select.extend({
-    content: ['Any', 'Running Now', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days'],
+    content: ['Any', 'Running Now', 'Past 1 Day', 'Past 2 Days', 'Past 7 Days', 'Past 14 Days', 'Past 30 Days', 'Custom'],
     selected: 'Any',
     classNames:['input-medium'],
     elementId: 'rundate_filter',
@@ -495,7 +495,143 @@ App.MainAppsView = Em.View.extend({
       else {
         this.$().closest('th').removeClass('notActive');
       }
-      this.get('parentView').get('applyFilter')(this.get('parentView'), 9);
+
+      if (this.get('selection') == 'Custom') {
+        this.customFilter();
+      } else {
+        this.get('parentView').get('applyFilter')(this.get('parentView'), 9);
+      }
+    },
+
+    /**
+     * Custom filter for RunDate
+     */
+    customFilter: function(){
+      var rundateSelect = this;
+      App.ModalPopup.show({
+        lowerBound: null,
+        upperBound: null,
+        equal: null,
+        header: Em.I18n.t('apps.filters.customRunDate'),
+        bodyClass: Ember.View.extend({
+          templateName: require('templates/main/apps/custom_rundate_popup'),
+
+          lowerBoundView: Ember.TextField.extend({
+            elementId: 'lowerBound',
+            classNames: 'lowerBound',
+            attributeBindings:['readonly'],
+            readonly: true,
+            didInsertElement: function() {
+              var textField = this;
+              var popupBody = this.get('parentView');
+              this.$().datetimepicker({
+                dateFormat: 'D, M dd, yy',
+                timeFormat: 'hh:mm',
+                maxDate: new Date(),
+                onClose:function (dateText, inst) {
+                  var endDateTextBox = $('#upperBound');
+                  if (endDateTextBox.val() != '') {
+                    var testStartDate = new Date(dateText);
+                    var testEndDate = new Date(endDateTextBox.val());
+                    if (testStartDate > testEndDate)
+                      endDateTextBox.val(dateText);
+                  } else {
+                    endDateTextBox.val(dateText);
+                  }
+                },
+                onSelect:function (selectedDateTime) {
+                  var start = $(this).datetimepicker('getDate');
+                  $('#upperBound').datetimepicker('option', 'minDate', new Date(start.getTime()));
+                  popupBody.get('parentView').set('lowerBound', selectedDateTime);
+                }
+              });
+            }
+          }),
+
+          upperBoundView: Ember.TextField.extend({
+            elementId: 'upperBound',
+            classNames: 'upperBound',
+            attributeBindings:['readonly'],
+            readonly: true,
+            didInsertElement: function() {
+              var textField = this;
+              var popupBody = this.get('parentView');
+              this.$().datetimepicker({
+                dateFormat: 'D, M dd, yy',
+                timeFormat: 'hh:mm',
+                maxDate: new Date(),
+                onClose:function (dateText, inst) {
+                  var startDateTextBox = $('#lowerBound');
+                  if (startDateTextBox.val() != '') {
+                    var testStartDate = new Date(startDateTextBox.val());
+                    var testEndDate = new Date(dateText);
+                    if (testStartDate > testEndDate)
+                      startDateTextBox.val(dateText);
+                  } else {
+                    startDateTextBox.val(dateText);
+                  }
+                },
+                onSelect:function (selectedDateTime) {
+                  var end = $(this).datetimepicker('getDate');
+                  $('#lowerBound').datetimepicker('option', 'maxDate', new Date(end.getTime()));
+                  popupBody.get('parentView').set('upperBound', selectedDateTime);
+                }
+              });
+            }
+          }),
+
+          equalView: Ember.TextField.extend({
+            attributeBindings:['readonly'],
+            readonly: true,
+            didInsertElement: function() {
+              var textField = this;
+              var popupBody = this.get('parentView');
+              this.$().datetimepicker({
+                dateFormat: 'D, M dd, yy',
+                timeFormat: 'hh:mm',
+                maxDate: new Date(),
+                onSelect:function (selectedDateTime) {
+                  popupBody.get('parentView').set('equal', selectedDateTime);
+                }
+              });
+            }
+          })
+        }),
+        applyFilter:function(lowerBound, upperBound, equal) {
+          if (arguments.length == 1) {
+            equal = new Date(arguments[0]).getTime();
+            jQuery('#custom_rundate_filter').val(equal);
+          } else if (arguments.length == 2) {
+            lowerBound = new Date(arguments[0]).getTime();
+            upperBound = new Date(arguments[1]).getTime();
+            var range = [lowerBound, upperBound];
+            jQuery('#custom_rundate_filter').val(range);
+          }
+          rundateSelect.get('parentView').get('applyFilter')(rundateSelect.get('parentView'), 9);
+          if (arguments.length == 0) {
+            rundateSelect.$().closest('th').addClass('notActive');
+          }
+          else {
+            rundateSelect.$().closest('th').removeClass('notActive');
+          }
+        },
+        onPrimary: function() {
+          if (this.get('lowerBound') && this.get('upperBound')) {
+            if (this.get('lowerBound') === this.get('upperBound')) {
+              alert('Range points must be different!');
+            } else {
+              this.applyFilter(this.get('lowerBound'), this.get('upperBound'));
+              this.hide();
+            }
+          } else if (this.get('equal')) {
+            this.applyFilter(this.get('equal'));
+            this.hide();
+          } else {
+            alert('Please specify date and time range or exact date and time!');
+          }
+        },
+        secondary : null
+      });
     }
   }),
   /**
@@ -642,7 +778,7 @@ App.MainAppsView = Em.View.extend({
       '<li><label class="checkbox">' +
       '{{view Ember.Checkbox checkedBinding="view.allComponentsChecked"}} All</label></li>'+
       '{{#each user in view.users}}<li><label class="checkbox">' +
-      '{{view Ember.Checkbox checkedBinding="user.checked"}}{{user.name}}'+
+      '{{view Ember.Checkbox checkedBinding="user.checked"}} {{user.name}}'+
       '</label></li>{{/each}}'+
       '<li>' +
       '<button class="btn" {{action "closeFilter" target="view"}}>' +

+ 5 - 1
ambari-web/app/views/main/dashboard/service/hbase.js

@@ -56,5 +56,9 @@ App.MainDashboardServiceHbaseView = App.MainDashboardServiceView.extend({
 
   regionServerComponent: function () {
     return App.Component.find().findProperty('componentName', 'HBASE_REGIONSERVER');
-  }.property('components')
+  }.property('components'),
+
+  toggleInfoView: function() {
+    $('#hbase-info').toggle('blind', 1000);
+  }
 });

+ 5 - 1
ambari-web/app/views/main/dashboard/service/hdfs.js

@@ -71,5 +71,9 @@ App.MainDashboardServiceHdfsView = App.MainDashboardServiceView.extend({
   
   dataNodeComponent: function(){
     return App.Component.find().findProperty('componentName', 'DATANODE');
-  }.property('+')
+  }.property('+'),
+
+  toggleInfoView: function() {
+    $('#hdfs-info').toggle('blind', 1000);
+  }
 });

+ 5 - 1
ambari-web/app/views/main/dashboard/service/mapreduce.js

@@ -101,5 +101,9 @@ App.MainDashboardServiceMapreduceView = App.MainDashboardServiceView.extend({
 
   taskTrackerComponent: function () {
     return App.Component.find().findProperty('componentName', 'TASKTRACKER');
-  }.property('components')
+  }.property('components'),
+
+  toggleInfoView: function() {
+    $('#mapreduce-info').toggle('blind', 1000);
+  }
 });

+ 162 - 73
ambari-web/app/views/main/host.js

@@ -21,52 +21,13 @@ require('utils/data_table');
 
 App.MainHostView = Em.View.extend({
   templateName:require('templates/main/host'),
-  filterByName:"",
   controller:function () {
     return App.router.get('mainHostController');
   }.property(),
   content:function () {
     return App.router.get('mainHostController.content');
   }.property('App.router.mainHostController.content'),
-  componentsIds:[1, 2, 3, 4, 5, 6, 7, 8],
   oTable: null,
-  isFilterOpen:false,
-
-//  isApplyDisabled:function () {
-//    return !this.get('isFilterOpen')
-//  }.property('isFilterOpen'),
-
-  btnGroupClass:function () {
-    return this.get('isFilterOpen') ? 'btn-group open' : 'btn-group';
-  }.property('isFilterOpen'),
-
-  applyFilters:function () {
-    this.set('isFilterOpen', false);
-    $(document).unbind('click');
-    App.router.get('mainHostController').filterByComponentsIds();
-  },
-
-  allComponentsChecked:false,
-  toggleAllComponents:function () {
-    this.set('masterComponentsChecked', this.get('allComponentsChecked'));
-    this.set('slaveComponentsChecked', this.get('allComponentsChecked'));
-  }.observes('allComponentsChecked'),
-
-  masterComponentsChecked:false,
-  toggleMasterComponents:function () {
-    var checked = this.get('masterComponentsChecked');
-    this.get('controller.masterComponents').forEach(function (comp) {
-      comp.set('checkedForHostFilter', checked);
-    });
-  }.observes('masterComponentsChecked'),
-
-  slaveComponentsChecked:false,
-  toggleSlaveComponents:function () {
-    var checked = this.get('slaveComponentsChecked');
-    this.get('controller.slaveComponents').forEach(function (comp) {
-      comp.set('checkedForHostFilter', checked);
-    });
-  }.observes('slaveComponentsChecked'),
 
   didInsertElement:function () {
     var oTable = $('#hosts-table').dataTable({
@@ -74,8 +35,8 @@ App.MainHostView = Em.View.extend({
       "oLanguage": {
         "sSearch": "Search:",
         "sLengthMenu": "Show: _MENU_",
-        "sInfo": "_START_ - _END_ of _TOTAL_ (_TOTAL_ total)",
-        "sInfoEmpty": "0 - _END_ of _TOTAL_ (_TOTAL_ total)",
+        "sInfo": "_START_ - _END_ of _TOTAL_",
+        "sInfoEmpty": "0 - _END_ of _TOTAL_",
         "sInfoFiltered": "",
         "oPaginate":{
           "sPrevious": "<i class='icon-arrow-left'></i>",
@@ -99,38 +60,6 @@ App.MainHostView = Em.View.extend({
     this.set('allComponentsChecked', true); // select all components (checkboxes) on start.
   },
 
-  applyHostFilter:function () {
-    App.router.get('mainHostController').filterHostsBy('hostName', this.get('filterByName'));
-  }.observes('filterByName'),
-
-  closeFilters:function () {
-    $(document).unbind('click');
-    this.clickFilterButton();
-  },
-
-  clickFilterButton:function () {
-    var self = this;
-    this.set('isFilterOpen', !this.get('isFilterOpen'));
-    if (this.get('isFilterOpen')) {
-      var filters = App.router.get('mainHostController.filters.components');
-      $('.filter-component').each(function () {
-        var componentId = parseInt($(this).attr('id').replace('component-', ''));
-        var index = filters.indexOf(componentId);
-        $(this).attr('checked', index == -1);
-      });
-//      this.set('componentsIds', filters.toArray());
-
-      var dropDown = $('#filter-dropdown');
-      var firstClick = true;
-      $(document).bind('click', function (e) {
-        if (!firstClick && $(e.target).closest(dropDown).length == 0) {
-          self.set('isFilterOpen', false);
-          $(document).unbind('click');
-        }
-        firstClick = false;
-      });
-    }
-  },
   HostView:Em.View.extend({
     content:null,
 
@@ -248,6 +177,166 @@ App.MainHostView = Em.View.extend({
       this.get('parentView').get('applyFilter')(this.get('parentView'), 3);
     }.observes('value')
   }),
+  /**
+   * Filter-list for Components
+   */
+  componentsFilterView: Em.View.extend({
+    classNames:['btn-group'],
+    classNameBindings: ['open'],
+    multiple:true,
+    open: false,
+
+    isFilterOpen:false,
+
+    btnGroupClass:function () {
+      return this.get('isFilterOpen') ? 'btn-group open' : 'btn-group';
+    }.property('isFilterOpen'),
+
+    allComponentsChecked:false,
+    toggleAllComponents:function () {
+      this.set('masterComponentsChecked', this.get('allComponentsChecked'));
+      this.set('slaveComponentsChecked', this.get('allComponentsChecked'));
+    }.observes('allComponentsChecked'),
+
+    masterComponentsChecked:false,
+    toggleMasterComponents:function () {
+      var checked = this.get('masterComponentsChecked');
+      this.get('masterComponents').forEach(function (comp) {
+        comp.set('checkedForHostFilter', checked);
+      });
+    }.observes('masterComponentsChecked'),
+
+    slaveComponentsChecked:false,
+    toggleSlaveComponents:function () {
+      var checked = this.get('slaveComponentsChecked');
+      this.get('slaveComponents').forEach(function (comp) {
+        comp.set('checkedForHostFilter', checked);
+      });
+    }.observes('slaveComponentsChecked'),
+
+    masterComponents:function(){
+      var masterComponents = [];
+      for(var i = 0; i < this.get('parentView').get('controller.masterComponents').length; i++) {
+        masterComponents.push(this.get('parentView').get('controller.masterComponents')[i]);
+      }
+      return masterComponents;
+    }.property('parentView.controller.masterComponents'),
+
+    slaveComponents:function(){
+      var slaveComponents = [];
+      for(var i = 0; i < this.get('parentView').get('controller.slaveComponents').length; i++) {
+        slaveComponents.push(this.get('parentView').get('controller.slaveComponents')[i]);
+      }
+      return slaveComponents;
+    }.property('parentView.controller.slaveComponents'),
+
+    template: Ember.Handlebars.compile('<div {{bindAttr class="view.btnGroupClass"}} >'+
+      '<button class="btn btn-info single-btn-group" {{action "clickFilterButton" target="view"}}>' +
+        'Components ' +
+        '<span class="caret"></span>' +
+       '</button>' +
+        '<ul class="dropdown-menu filter-components" id="filter-dropdown">' +
+          '<li>' +
+            '<label class="checkbox">' +
+              '{{view Ember.Checkbox checkedBinding="view.allComponentsChecked"}} All' +
+            '</label>' +
+          '</li>' +
+          '<li>' +
+            '<label class="checkbox">' +
+              '{{view Ember.Checkbox checkedBinding="view.masterComponentsChecked"}} Master Components:' +
+            '</label>' +
+            '<ul>' +
+              '{{#each component in masterComponents}}' +
+                '<li>' +
+                  '<label class="checkbox">' +
+                    '{{view Ember.Checkbox checkedBinding="component.checkedForHostFilter" }} {{unbound component.displayName}}' +
+                  '</label>' +
+                ' </li>' +
+              '{{/each}}' +
+            '</ul>' +
+          '</li>' +
+          '<li>' +
+            '<label class="checkbox">' +
+              '{{view Ember.Checkbox checkedBinding="view.slaveComponentsChecked"}} Slave Components:' +
+            '</label>' +
+            '<ul>' +
+              '{{#each component in slaveComponents}}' +
+                '<li>' +
+                  '<label class="checkbox">' +
+                    '{{view Ember.Checkbox checkedBinding="component.checkedForHostFilter" }} {{unbound component.displayName}}' +
+                  '</label>' +
+                '</li>' +
+              '{{/each}}' +
+            '</ul>' +
+          '</li>' +
+          '<li class="align-right">' +
+            '<button class="btn" {{action "closeFilters" target="view"}}>' +
+              'Cancel' +
+            '</button> ' +
+            '<button class="btn btn-primary" {{action "applyFilter" target="view"}}>' +
+              'Apply' +
+            '</button>' +
+          '</li>' +
+        '</ul>' +
+      '</div>'),
+
+    clearFilter:function(self) {
+      self.set('allComponentsChecked', true);
+      self.set('allComponentsChecked', false);
+      jQuery('#components_filter').val([]);
+      self.get('parentView').get('oTable').fnFilter('', 6);
+      jQuery('#components_filter').closest('th').addClass('notActive');
+    },
+
+    closeFilters:function () {
+      $(document).unbind('click');
+      this.clickFilterButton();
+    },
+
+    clickFilterButton:function () {
+      var self = this;
+      this.set('isFilterOpen', !this.get('isFilterOpen'));
+      if (this.get('isFilterOpen')) {
+        var filters = App.router.get('mainHostController.filters.components');
+        $('.filter-component').each(function() {
+          var componentId = parseInt($(this).attr('id').replace('component-', ''));
+          var index = filters.indexOf(componentId);
+          $(this).attr('checked', index == -1);
+        });
+
+        var dropDown = $('#filter-dropdown');
+        var firstClick = true;
+        $(document).bind('click', function (e) {
+          if (!firstClick && $(e.target).closest(dropDown).length == 0) {
+            self.set('isFilterOpen', false);
+            $(document).unbind('click');
+          }
+          firstClick = false;
+        });
+      }
+    },
+    /*closeFilter: function(){
+     this.set('open', false);
+     },*/
+    applyFilter:function() {
+      var chosenComponents = new Array();
+      this.set('open', !this.get('open'));
+      this.get('masterComponents').forEach(function(item){
+        if(item.get('checkedForHostFilter')) chosenComponents.push(item.get('displayName'));
+      });
+      this.get('slaveComponents').forEach(function(item){
+        if(item.get('checkedForHostFilter')) chosenComponents.push(item.get('displayName'));
+      });
+      jQuery('#components_filter').val(chosenComponents);
+      this.get('parentView').get('applyFilter')(this.get('parentView'), 6);
+      if (chosenComponents.length == 0) {
+        this.$().closest('th').addClass('notActive');
+      }
+      else {
+        this.$().closest('th').removeClass('notActive');
+      }
+    }
+  }),
   /**
    * Clear selected filter
    * @param event