Browse Source

AMBARI-1059. Refactor cluster management. (yusaku)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1418901 13f79535-47bb-0310-9956-ffa450edef68
Yusaku Sako 12 years ago
parent
commit
744a1c9ab2
36 changed files with 684 additions and 478 deletions
  1. 6 1
      AMBARI-666-CHANGES.txt
  2. 11 0
      ambari-web/app/assets/data/clusters/info.json
  3. 4 1
      ambari-web/app/controllers.js
  4. 86 0
      ambari-web/app/controllers/cluster_controller.js
  5. 194 0
      ambari-web/app/controllers/global/background_operations_controller.js
  6. 13 100
      ambari-web/app/controllers/main.js
  7. 5 1
      ambari-web/app/controllers/main/apps_controller.js
  8. 2 2
      ambari-web/app/controllers/main/charts/horizon_chart.js
  9. 2 1
      ambari-web/app/controllers/main/host.js
  10. 1 1
      ambari-web/app/controllers/main/service/info/audit.js
  11. 5 89
      ambari-web/app/controllers/main/service/item.js
  12. 1 0
      ambari-web/app/initialize.js
  13. 1 17
      ambari-web/app/mappers/hosts_mapper.js
  14. 39 2
      ambari-web/app/mappers/server_data_mapper.js
  15. 0 41
      ambari-web/app/mappers/services_mapper.js
  16. 1 2
      ambari-web/app/router.js
  17. 1 1
      ambari-web/app/routes/main.js
  18. 147 126
      ambari-web/app/styles/application.less
  19. 12 4
      ambari-web/app/templates/main.hbs
  20. 1 1
      ambari-web/app/templates/main/background_operations_popup.hbs
  21. 10 1
      ambari-web/app/templates/main/host.hbs
  22. 0 40
      ambari-web/app/templates/main/service/background_operations_popup.hbs
  23. 2 2
      ambari-web/app/templates/main/service/menu_item.hbs
  24. 0 4
      ambari-web/app/utils/http_client.js
  25. 1 0
      ambari-web/app/views.js
  26. 9 1
      ambari-web/app/views/common/combobox.js
  27. 24 0
      ambari-web/app/views/common/empty_view.js
  28. 2 0
      ambari-web/app/views/main/apps_view.js
  29. 12 2
      ambari-web/app/views/main/host.js
  30. 3 4
      ambari-web/app/views/main/service/menu.js
  31. 5 1
      ambari-web/app/views/main/test.js
  32. 10 2
      ambari-web/app/views/wizard/step2_view.js
  33. 25 24
      ambari-web/package.json
  34. 12 4
      ambari-web/pom.xml
  35. 36 3
      ambari-web/vendor/scripts/bootstrap-combobox.js
  36. 1 0
      ambari-web/vendor/styles/bootstrap-combobox.css

+ 6 - 1
AMBARI-666-CHANGES.txt

@@ -12,6 +12,9 @@ AMBARI-666 branch (unreleased changes)
 
 
   NEW FEATURES
   NEW FEATURES
 
 
+  AMBARI-976.  Hook HDFS/MapReduce/HBase/Host graphs to backend API
+  (Srimanth Gunturi via yusaku)
+
   AMBARI-964. Implement summary page of installer wizard. (Jaimin Jetly
   AMBARI-964. Implement summary page of installer wizard. (Jaimin Jetly
   via yusaku)
   via yusaku)
 
 
@@ -392,7 +395,9 @@ AMBARI-666 branch (unreleased changes)
 
 
   IMPROVEMENTS
   IMPROVEMENTS
 
 
-  AMBARI-1058. Implement data loading (yusaku)
+  AMBARI-1059. Refactor cluster management. (yusaku)
+
+  AMBARI-1058. Implement data loading. (yusaku)
 
 
   AMBARI-956. On unavailability of non-master components, host with least
   AMBARI-956. On unavailability of non-master components, host with least
   number of master components should install all slave and client components. 
   number of master components should install all slave and client components. 

+ 11 - 0
ambari-web/app/assets/data/clusters/info.json

@@ -0,0 +1,11 @@
+{
+  "href" : "http://ec2-107-21-153-79.compute-1.amazonaws.com:9998/clusters",
+  "items" : [
+    {
+      "href" : "http://ec2-107-21-153-79.compute-1.amazonaws.com:9998/clusters/mycluster",
+      "Clusters" : {
+        "cluster_name" : "mycluster"
+      }
+    }
+  ]
+}

+ 4 - 1
ambari-web/app/controllers.js

@@ -22,6 +22,7 @@
 require('controllers/application');
 require('controllers/application');
 require('controllers/login_controller');
 require('controllers/login_controller');
 require('controllers/installer');
 require('controllers/installer');
+require('controllers/global/background_operations_controller');
 require('controllers/main');
 require('controllers/main');
 require('controllers/main/admin');
 require('controllers/main/admin');
 require('controllers/main/admin/item');
 require('controllers/main/admin/item');
@@ -58,4 +59,6 @@ require('controllers/wizard/step6_controller');
 require('controllers/wizard/step7_controller');
 require('controllers/wizard/step7_controller');
 require('controllers/wizard/step8_controller');
 require('controllers/wizard/step8_controller');
 require('controllers/wizard/step9_controller');
 require('controllers/wizard/step9_controller');
-require('controllers/wizard/step10_controller');
+require('controllers/wizard/step10_controller');
+require('controllers/cluster_controller');
+require('controllers/cluster_controller');

+ 86 - 0
ambari-web/app/controllers/cluster_controller.js

@@ -0,0 +1,86 @@
+/**
+ * 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.ClusterController = Em.Controller.extend({
+  name: 'clusterController',
+  cluster: null,
+  isLoaded: function(){
+    return true;
+    var loadList = this.get('dataLoadList');
+    var loaded = true;
+    for(var i in loadList){
+      if(loadList.hasOwnProperty(i) && !loadList[i]){
+        loaded = false;
+      }
+    }
+
+    return loaded;
+  }.property('dataLoadList'),
+  dataLoadList: Em.Object.create({
+    'hosts': true,
+    'services': false
+  }),
+  /**
+   * load cluster name
+   */
+  loadClusterName: function(){
+    var self = this;
+    var url = (App.testMode) ? '/data/clusters/info.json' : '/api/clusters';
+    $.ajax({
+      type: "GET",
+      url: url,
+      dataType: 'json',
+      timeout: 5000,
+      success: function (data) {
+        self.set('cluster', data.items[0]);
+        self.loadClusterData();
+      },
+      error: function (request, ajaxOptions, error) {
+        //do something
+        console.log('failed on loading cluster name')
+      },
+      statusCode: require('data/statusCodes')
+    });
+  },
+   /**
+   *
+   *  load all data and update load status
+   */
+  loadClusterData: function(){
+    var self = this;
+    if(!this.get('clusterName')){
+        return;
+    }
+    // TODO: load all models
+    /*App.HttpClient.get("/data/hosts/hosts.json", App.hostsMapper,{
+      complete:function(jqXHR, textStatus){
+        self.set('dataLoadList.hosts', true);
+      }
+    });*/
+    App.HttpClient.get("/data/dashboard/services.json", App.servicesMapper,{
+      complete:function(jqXHR, textStatus){
+        self.set('dataLoadList.services', true);
+      }
+    });
+  }.observes('clusterName'),
+  clusterName: function(){
+    return (this.get('cluster')) ? this.get('cluster').Clusters.cluster_name : 'mycluster';
+  }.property('cluster')
+})

+ 194 - 0
ambari-web/app/controllers/global/background_operations_controller.js

@@ -0,0 +1,194 @@
+/**
+ * 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.BackgroundOperationsController = Em.Controller.extend({
+  name: 'backgroundOperationsController',
+
+  /**
+   * Whether we need to refresh background operations or not
+   */
+  isWorking : false,
+
+  allOperations: [],
+  allOperationsCount : 0,
+
+  /**
+   * Update it every time when background operations for services are changed
+   */
+  serviceOperationsChangeTime: function(){
+    return (new Date().getTime());
+  }.property('hdfsOperations', 'mapReduceOperations'),
+
+  hdfsOperations : function(){
+    var all = this.get('allOperations');
+    var result = [];
+
+    all.forEach(function(item){
+      if( ['NAMENODE', 'SECONDARY_NAMENODE', 'DATANODE', 'HDFS_CLIENT', 'HDFS_SERVICE_CHECK'].contains(item.role)){
+        result.push(item);
+      }
+    })
+    return result;
+  }.property('allOperations.@each'),
+
+  mapReduceOperations : function(){
+    var all = this.get('allOperations');
+    var result = [];
+
+    all.forEach(function(item){
+      if( ['MAPREDUCE_CLIENT', 'JOBTRACKER', 'TASKTRACKER', 'MAPREDUCE_SERVICE_CHECK'].contains(item.role)){
+        result.push(item);
+      }
+    })
+    return result;
+
+  }.property('allOperations.@each'),
+
+  getOperationsFor: function(serviceName){
+    switch(serviceName.toUpperCase()){
+      case 'HDFS':
+        return this.get('hdfsOperations');
+      case 'MAPREDUCE':
+        return this.get('mapReduceOperations');
+      default:
+        return [];
+    }
+  },
+
+  updateInterval: 6000,
+  url : '',
+
+  generateUrl: function(){
+    var url = App.testMode ?
+      '/data/background_operations/list_on_start.json' :
+      '/api/clusters/' + App.router.getClusterName() + '/requests/?fields=tasks/*&tasks/Tasks/status!=COMPLETED';
+
+    this.set('url', url);
+    return url;
+  },
+
+  /**
+   * Reload operations
+   * We can call it manually <code>controller.loadOperations();</code>
+   * or it fires automatically, when <code>isWorking</code> becomes <code>true</code>
+   */
+  loadOperations : function(){
+
+    if(!this.get('isWorking')){
+      return;
+    }
+    var self = this;
+
+    var url = this.get('url');
+    if(!url){
+      url = this.generateUrl();
+    }
+
+    $.ajax({
+      type: "GET",
+      url: url,
+      dataType: 'json',
+      timeout: 5000,
+      success: function (data) {
+        //refresh model
+        self.updateBackgroundOperations(data);
+
+        //load data again if isWorking = true
+        if(self.get('isWorking')){
+          setTimeout(function(){
+            self.loadOperations();
+          }, self.get('updateInterval'));
+        }
+      },
+
+      error: function (request, ajaxOptions, error) {
+        console.log('cannot load background operations array');
+
+        //next code is temporary code to fix testMode issues
+        self.set('url', '/data/background_operations/list_on_start.json');
+        //load data again if isWorking = true
+        if(self.get('isWorking')){
+          setTimeout(function(){
+            self.loadOperations();
+          }, self.get('updateInterval'));
+        }
+      },
+
+      statusCode: require('data/statusCodes')
+    });
+
+  }.observes('isWorking'),
+
+  /**
+   * Add new operations to <code>this.allOperations</code> variable
+   * @param data json loaded from server
+   */
+  updateBackgroundOperations : function(data){
+    var runningTasks = [];
+    data.items.forEach(function (item) {
+      item.tasks.forEach(function (task) {
+        if (task.Tasks.status == 'QUEUED' || task.Tasks.status == 'PENDING') {
+          runningTasks.push(task.Tasks);
+        }
+      });
+    });
+
+    var currentTasks = this.get('allOperations');
+
+    runningTasks.forEach(function(item){
+      var task = currentTasks.findProperty('id', item.id);
+      if(task){
+        currentTasks[currentTasks.indexOf(task)] = item;
+      } else {
+        currentTasks.pushObject(item);
+      }
+    });
+
+    for(var i = currentTasks.length-1; i>=0; i--){
+      var isTaskFinished = !runningTasks.someProperty('id', currentTasks[i].id);
+      if(isTaskFinished){
+        currentTasks.removeAt(i);
+      }
+    }
+
+    this.set('allOperationsCount', currentTasks.length);
+  },
+
+  /**
+   * Onclick handler for background operations number located right to logo
+   */
+  showPopup: function(){
+    App.ModalPopup.show({
+      headerClass: Ember.View.extend({
+        controllerBinding: 'App.router.backgroundOperationsController',
+        template:Ember.Handlebars.compile('{{allOperationsCount}} Background Operations Running')
+      }),
+      bodyClass: Ember.View.extend({
+        controllerBinding: 'App.router.backgroundOperationsController',
+        templateName: require('templates/main/background_operations_popup')
+      }),
+      onPrimary: function() {
+        this.hide();
+      },
+      secondary : null
+    });
+  }
+
+});

+ 13 - 100
ambari-web/app/controllers/main.js

@@ -21,108 +21,21 @@ require('models/background_operation');
 
 
 App.MainController = Em.Controller.extend({
 App.MainController = Em.Controller.extend({
   name: 'mainController',
   name: 'mainController',
-  backgroundOperations: [],
-  backgroundOperationsCount : 0,
-  backgroundOperationsUrl : '',
-  intervalId: false,
-  updateOperationsInterval: 6000,
-  clusters: App.Cluster.find(),
-  cluster: function(){
-    var clusters = this.get('clusters');
-    if(clusters){
-      var cluster = clusters.objectAt(0);
-      return cluster;
-    }
-  }.property('clusters'),
-  
-  startLoadOperationsPeriodically: function() {
-    this.loadBackgroundOperations();
-    this.intervalId = setInterval(this.loadBackgroundOperations, this.get('updateOperationsInterval'));
-  },
-  stopLoadOperationsPeriodically:function () {
-    if(this.intervalId) {
-      clearInterval(this.intervalId);
-    }
-    this.intervalId = false;
-  },
-  loadBackgroundOperations: function(){
-    var self = App.router.get('mainController');
-
-    var url = self.get('backgroundOperationsUrl');
-    if(!url){
-      //cache url, not to execute <code>getClusterName</code> everytime
-      url = (App.testMode) ?
-        '/data/background_operations/list_on_start.json' :
-        '/api/clusters/' + App.router.getClusterName() + '/requests/?fields=tasks/*&tasks/Tasks/status!=COMPLETED';
-      self.set('backgroundOperationsUrl', url);
-    }
-
-    $.ajax({
-      type: "GET",
-      url: url,
-      dataType: 'json',
-      timeout: 5000,
-      success: function (data) {
-        self.updateBackgroundOperations(data);
-      },
-
-      error: function (request, ajaxOptions, error) {
-        //do something
-      },
-
-      statusCode: require('data/statusCodes')
-    });
-  },
-
+  isClusterDataLoaded: function(){
+      return App.router.get('clusterController.isLoaded');
+  }.property('App.router.clusterController.isLoaded'),
   /**
   /**
-   * Add new operations to <code>this.backgroundOperations</code> variable
-   * @param data json loaded from server
+   * run all processes and cluster's data loading
    */
    */
-  updateBackgroundOperations : function(data){
-    var runningTasks = [];
-    data.items.forEach(function (item) {
-      item.tasks.forEach(function (task) {
-        if (task.Tasks.status == 'QUEUED') {
-          runningTasks.push(task.Tasks);
-        }
-      });
-    });
-
-    var currentTasks = this.get('backgroundOperations');
-
-    runningTasks.forEach(function(item){
-      var task = currentTasks.findProperty('id', item.id);
-      if(task){
-        currentTasks[currentTasks.indexOf(task)] = item;
-      } else {
-        currentTasks.pushObject(item);
-      }
-    });
-
-    for(var i = currentTasks.length-1; i>=0; i--){
-      var isTaskFinished = !runningTasks.someProperty('id', currentTasks[i].id);
-      if(isTaskFinished){
-        currentTasks.removeAt(i);
-      }
-    }
-
-    this.set('backgroundOperationsCount', currentTasks.length);
-
+  initialize: function(){
+    this.startLoadOperationsPeriodically();
+    App.router.get('clusterController').loadClusterName();
   },
   },
-
-  showBackgroundOperationsPopup: function(){
-    App.ModalPopup.show({
-      headerClass: Ember.View.extend({
-        controllerBinding: 'App.router.mainController',
-        template:Ember.Handlebars.compile('{{backgroundOperationsCount}} Background Operations Running')
-      }),
-      bodyClass: Ember.View.extend({
-        controllerBinding: 'App.router.mainController',
-        templateName: require('templates/main/background_operations_popup')
-      }),
-      onPrimary: function() {
-        this.hide();
-      }
-    });
+  startLoadOperationsPeriodically: function() {
+      App.router.get('backgroundOperationsController').set('isWorking', true);
+  },
+  stopLoadOperationsPeriodically:function () {
+    App.router.get('backgroundOperationsController').set('isWorking', false);
   }
   }
+
 })
 })

+ 5 - 1
ambari-web/app/controllers/main/apps_controller.js

@@ -60,7 +60,7 @@ App.MainAppsController = Em.ArrayController.extend({
    * @return {Boolean} true - record with this id exists, false - not exists
    * @return {Boolean} true - record with this id exists, false - not exists
    */
    */
   issetStaredRun: function(id) {
   issetStaredRun: function(id) {
-    r = false;
+    var r = false;
     this.get('staredRuns').forEach(function(item){
     this.get('staredRuns').forEach(function(item){
       if (item.get('id') == id) {
       if (item.get('id') == id) {
         r = true;
         r = true;
@@ -68,12 +68,16 @@ App.MainAppsController = Em.ArrayController.extend({
     });
     });
     return r;
     return r;
   },
   },
+  /**
+   * Identifier of the last starred/unstarred run
+   */
   lastStarClicked: -1,
   lastStarClicked: -1,
   /**
   /**
    * Click on star on table row
    * Click on star on table row
    * @return {Boolean} false for prevent default event handler
    * @return {Boolean} false for prevent default event handler
    */
    */
   starClick: function(event) {
   starClick: function(event) {
+    $(event.target).closest('table').find('.containerRow').remove(); // hack for valid "turning-off" star in table, where graphs row where enabled. We remove it
     event.target.classList.toggle('stared');
     event.target.classList.toggle('stared');
     var cell = event.target.parentNode.parentNode;
     var cell = event.target.parentNode.parentNode;
     var row = cell.parentNode;
     var row = cell.parentNode;

+ 2 - 2
ambari-web/app/controllers/main/charts/horizon_chart.js

@@ -19,5 +19,5 @@
 var App = require('app');
 var App = require('app');
 
 
 App.MainChartsHorizonChartController = Em.Controller.extend({
 App.MainChartsHorizonChartController = Em.Controller.extend({
-  name:'mainChartsHorizonChartController'
-})
+  name: 'mainChartsHorizonChartController'
+});

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

@@ -36,6 +36,7 @@ App.MainHostController = Em.ArrayController.extend(App.Pagination, {
   allChecked:false,
   allChecked:false,
   selectedHostsIds:[],
   selectedHostsIds:[],
   selectedRack:null,
   selectedRack:null,
+
   assignHostsToRack:function () {
   assignHostsToRack:function () {
     var selectedRack = this.get('selectedRack');
     var selectedRack = this.get('selectedRack');
     var sureMessage = this.t('hosts.assignToRack.sure');
     var sureMessage = this.t('hosts.assignToRack.sure');
@@ -54,7 +55,7 @@ App.MainHostController = Em.ArrayController.extend(App.Pagination, {
       this.set('selectedHostsIds', []);
       this.set('selectedHostsIds', []);
     }
     }
 
 
-  }.observes('selectedRack'),
+  },
 
 
   sortingAsc:true,
   sortingAsc:true,
   isSort:false,
   isSort:false,

+ 1 - 1
ambari-web/app/controllers/main/service/info/audit.js

@@ -20,4 +20,4 @@ var App = require('app');
 
 
 App.MainServiceInfoAuditController = Em.Controller.extend({
 App.MainServiceInfoAuditController = Em.Controller.extend({
   name: 'mainServiceInfoAuditController'
   name: 'mainServiceInfoAuditController'
-})
+});

+ 5 - 89
ambari-web/app/controllers/main/service/item.js

@@ -20,55 +20,7 @@ var App = require('app');
 
 
 App.MainServiceItemController = Em.Controller.extend({
 App.MainServiceItemController = Em.Controller.extend({
   name: 'mainServiceItemController',
   name: 'mainServiceItemController',
-  backgroundOperations: [],
-  taskId: 0,
-  intervalId: false,
-  checkOperationsInterval: 5000,
-  init: function(){
-    this._super();
-    this.startCheckOperationsLifeTime();
-  },
-  startCheckOperationsLifeTime: function () {
-    this.intervalId = setInterval(this.checkOperationsLifeTime, this.get('checkOperationsInterval'));
-  },
-  stopCheckOperationsLifeTime:function () {
-    if(this.intervalId) {
-      clearInterval(this.intervalId);
-    }
-    this.intervalId = false;
-  },
 
 
-  checkOperationsLifeTime: function () {
-    var self = App.router.get('mainServiceItemController');
-    var backgroundOperations = self.get('backgroundOperations');
-    var time = new Date().getTime();
-    if(backgroundOperations.length){
-      backgroundOperations.forEach(function (operation) {
-        if (time - operation.startTime >= 60*1000){
-          backgroundOperations.removeObject(operation);
-        }
-      })
-    }
-  },
-  createBackgroundOperation: function (role, command) {
-    var newTaskId = this.get('taskId') + 1;
-    this.set('taskId', newTaskId);
-    var operation = Em.Object.create({
-      taskId: newTaskId,
-      stageId: null,
-      serviceName: this.content.get('serviceName'),
-      role: role,
-      command: command,
-      status: null,
-      exitcode: 404,
-      stderror: 27,
-      stdout: 501,
-      startTime: new Date().getTime(),
-      attemptCount: null
-    })
-
-    return operation;
-  },
   /**
   /**
    * Send specific command to server
    * Send specific command to server
    * @param url
    * @param url
@@ -123,9 +75,7 @@ App.MainServiceItemController = Em.Controller.extend({
           }
           }
         });
         });
 
 
-        var newOperation = self.createBackgroundOperation('Service', 'Start');
-        newOperation.detail = "Another detail info";
-        self.addBackgroundOperation(newOperation);
+        App.router.get('backgroundOperationsController').showPopup();
         this.hide();
         this.hide();
       },
       },
       onSecondary: function() {
       onSecondary: function() {
@@ -158,9 +108,7 @@ App.MainServiceItemController = Em.Controller.extend({
           }
           }
         });
         });
 
 
-        var newOperation = self.createBackgroundOperation('Service', 'Stop');
-        newOperation.detail = "Another detail info";
-        self.addBackgroundOperation(newOperation);
+        App.router.get('backgroundOperationsController').showPopup();
         this.hide();
         this.hide();
       },
       },
       onSecondary: function() {
       onSecondary: function() {
@@ -177,9 +125,7 @@ App.MainServiceItemController = Em.Controller.extend({
       secondary: 'No',
       secondary: 'No',
       onPrimary: function() {
       onPrimary: function() {
         self.content.set('runRebalancer', true);
         self.content.set('runRebalancer', true);
-        var newOperation = self.createBackgroundOperation('Service', 'Run Rebalancer');
-        newOperation.detail = "Some detail info";
-        self.addBackgroundOperation(newOperation);
+        App.router.get('backgroundOperationsController').showPopup();
         this.hide();
         this.hide();
       },
       },
       onSecondary: function() {
       onSecondary: function() {
@@ -196,8 +142,7 @@ App.MainServiceItemController = Em.Controller.extend({
       secondary: 'No',
       secondary: 'No',
       onPrimary: function() {
       onPrimary: function() {
         self.content.set('runCompaction', true);
         self.content.set('runCompaction', true);
-        var newOperation = self.createBackgroundOperation('Service', 'Run Compaction');
-        self.addBackgroundOperation(newOperation);
+        App.router.get('backgroundOperationsController').showPopup();
         this.hide();
         this.hide();
       },
       },
       onSecondary: function() {
       onSecondary: function() {
@@ -214,8 +159,7 @@ App.MainServiceItemController = Em.Controller.extend({
       secondary: 'No',
       secondary: 'No',
       onPrimary: function() {
       onPrimary: function() {
         self.content.set('runSmokeTest', true);
         self.content.set('runSmokeTest', true);
-        var newOperation = self.createBackgroundOperation('Service', 'Run Smoke Test');
-        self.addBackgroundOperation(newOperation);
+        App.router.get('backgroundOperationsController').showPopup();
         this.hide();
         this.hide();
       },
       },
       onSecondary: function() {
       onSecondary: function() {
@@ -236,33 +180,5 @@ App.MainServiceItemController = Em.Controller.extend({
         this.runSmokeTest();
         this.runSmokeTest();
         break;
         break;
     }
     }
-  },
-  serviceOperations: function(){
-    var serviceName = this.get('content.serviceName');
-    return this.get('backgroundOperations').filterProperty('serviceName', serviceName);
-  }.property('backgroundOperations.length', 'content'),
-  serviceOperationsCount: function() {
-    return this.get('serviceOperations.length');
-  }.property('serviceOperations'),
-  showBackgroundOperationsPopup: function(){
-    console.log(this.get('backgroundOperations'));
-    App.ModalPopup.show({
-      headerClass: Ember.View.extend({
-        controllerBinding: 'App.router.mainServiceItemController',
-        template:Ember.Handlebars.compile('{{serviceOperationsCount}} Background Operations Running')
-      }),
-      bodyClass: Ember.View.extend({
-        controllerBinding: 'App.router.mainServiceItemController',
-        templateName: require('templates/main/service/background_operations_popup')
-      }),
-      onPrimary: function() {
-        this.hide();
-      }
-    });
-  },
-  addBackgroundOperation: function (operation) {
-    var backgroundOperations = this.get('backgroundOperations');
-    backgroundOperations.pushObject(operation);
-    this.showBackgroundOperationsPopup();
   }
   }
 })
 })

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

@@ -33,6 +33,7 @@ require('router');
 
 
 require('mappers/server_data_mapper');
 require('mappers/server_data_mapper');
 require('mappers/services_mapper');
 require('mappers/services_mapper');
+require('mappers/hosts_mapper');
 
 
 require('utils/http_client');
 require('utils/http_client');
 
 

+ 1 - 17
ambari-web/app/mappers/hosts_mapper.js

@@ -18,21 +18,5 @@
 
 
 //todo: refactor it
 //todo: refactor it
 App.hostsMapper = App.ServerDataMapper.create({
 App.hostsMapper = App.ServerDataMapper.create({
-  map: function (json) {
-    if (json.items) {
-      json.items.forEach(function (data) {
-        if (data.Hosts) {
-          App.store.createRecord(App.Host, {
-            id: data.Hosts.host_name,
-            hostName: data.Hosts.host_name,
-            cpuCount: data.Hosts.cpu_count,
-            totalMem: data.Hosts.total_mem,
-            osArch: data.Hosts.os_arch,
-            osType: data.Hosts.os_type,
-            ip: data.Hosts.ip
-          });
-        }
-      });
-    }
-  }
+  config: {}
 });
 });

+ 39 - 2
ambari-web/app/mappers/server_data_mapper.js

@@ -39,7 +39,44 @@ App.ServerDataMapper = Em.Object.extend({
 
 
 App.QuickDataMapper = App.ServerDataMapper.extend({
 App.QuickDataMapper = App.ServerDataMapper.extend({
   config : {},
   config : {},
-  map : function(json){
-    //todo: move code here
+  map:function(json){
+    if(json.items){
+      var result = [];
+      json.items.forEach(function(item){
+        result.push(this.parseIt(item, this.config));
+      }, this)
+    App.store.loadMany(App.Service1, result);
+    }
+  },
+  parseIt : function(data, config){
+    var result = {};
+    for(var i in config){
+      if(i.substr(-4) !== '_key' && typeof config[i] == 'string'){
+        result[i] = this.getJsonProperty(data, config[i]);
+      } else if(typeof config[i] == 'object'){
+      result[i] = [];
+      var _data = data[config[i+'_key']];
+      var l = _data.length;
+      for(var index = 0; index<l; index++){
+        result[i].push(this.parseIt(_data[index], config[i]));
+      }
+      }
+    }
+    return result;
+  },
+  getJsonProperty:function(json, path){
+    var pathArr = path.split('.');
+    var current = json;
+    while(pathArr.length){
+      if(pathArr[0].substr(-1) == ']'){
+        var index = parseInt(pathArr[0].substr(-2,1));
+        var attr = pathArr[0].substr(0, pathArr[0].length-3);
+        current = current[attr][index];
+      } else {
+        current = current[pathArr[0]];
+      }
+      pathArr.splice(0,1);
+    }
+    return current;
   }
   }
 });
 });

+ 0 - 41
ambari-web/app/mappers/services_mapper.js

@@ -71,46 +71,5 @@ App.servicesMapper = App.QuickDataMapper.create({
         state: 'host_components[0].HostRoles.state',
         state: 'host_components[0].HostRoles.state',
         host_name: 'host_components[0].HostRoles.host_name'
         host_name: 'host_components[0].HostRoles.host_name'
       }
       }
-  },
-  map:function(json){
-    if(json.items){
-      var result = [];
-      json.items.forEach(function(item){
-        result.push(this.parseIt(item, this.config));
-      }, this)
-
-      App.store.loadMany(App.Service1, result);
-    }
-  },
-  parseIt : function(data, config){
-    var result = {};
-    for(var i in config){
-      if(i.substr(-4) !== '_key' && typeof config[i] == 'string'){
-        result[i] = this.getJsonProperty(data, config[i]);
-      } else if(typeof config[i] == 'object'){
-        result[i] = [];
-        var _data = data[config[i+'_key']];
-        var l = _data.length;
-        for(var index = 0; index<l; index++){
-          result[i].push(this.parseIt(_data[index], config[i]));
-        }
-      }
-    }
-    return result;
-  },
-  getJsonProperty:function(json, path){
-    var pathArr = path.split('.');
-    var current = json;
-    while(pathArr.length){
-      if(pathArr[0].substr(-1) == ']'){
-        var index = parseInt(pathArr[0].substr(-2,1));
-        var attr = pathArr[0].substr(0, pathArr[0].length-3);
-        current = current[attr][index];
-      } else {
-        current = current[pathArr[0]];
-      }
-      pathArr.splice(0,1);
-    }
-    return current;
   }
   }
 });
 });

+ 1 - 2
ambari-web/app/router.js

@@ -32,7 +32,6 @@ App.Router = Em.Router.extend({
     var previousStep = parseInt(this.getInstallerCurrentStep());
     var previousStep = parseInt(this.getInstallerCurrentStep());
     this.set('isFwdNavigation', newStep >= previousStep);
     this.set('isFwdNavigation', newStep >= previousStep);
   },
   },
-
   clearAllSteps: function() {
   clearAllSteps: function() {
     this.get('installerController.content').set('cluster',null);
     this.get('installerController.content').set('cluster',null);
     this.get('wizardStep2Controller').set('hasSubmitted',false);
     this.get('wizardStep2Controller').set('hasSubmitted',false);
@@ -54,7 +53,7 @@ App.Router = Em.Router.extend({
    * @return {*}
    * @return {*}
    */
    */
   getClusterName: function(){
   getClusterName: function(){
-    return App.db.getClusterName();
+    return App.router.get('clusterController').get('clusterName');
   },
   },
 
 
 
 

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

@@ -21,7 +21,7 @@ module.exports = Em.Route.extend({
   enter:function (router) {
   enter:function (router) {
     console.log('in /main:enter');
     console.log('in /main:enter');
     if (router.getAuthenticated()) {
     if (router.getAuthenticated()) {
-      router.get('mainController').startLoadOperationsPeriodically();
+      router.get('mainController').initialize();
       // TODO: redirect to last known state
       // TODO: redirect to last known state
     } else {
     } else {
       Ember.run.next(function () {
       Ember.run.next(function () {

+ 147 - 126
ambari-web/app/styles/application.less

@@ -219,15 +219,20 @@ h1 {
       border: 1px solid #ccc;
       border: 1px solid #ccc;
       margin-top: 5px;
       margin-top: 5px;
       padding: 8px;
       padding: 8px;
-      font-family: Consolas,"Liberation Mono",Courier,monospace;
+      font-family: Consolas, "Liberation Mono", Courier, monospace;
+    }
+    .sshKey-file-view.disabled {
+      background-color: #eee;
     }
     }
     .manual-install {
     .manual-install {
       margin-top: 10px;
       margin-top: 10px;
+      width: 504px;
+      height: auto;
     }
     }
     #targetHosts {
     #targetHosts {
 
 
     }
     }
-    .span6{
+    .span6 {
       min-width: 504px;
       min-width: 504px;
     }
     }
     #hostConnectivity {
     #hostConnectivity {
@@ -769,12 +774,12 @@ a:focus {
   .health-status-DEAD-YELLOW {
   .health-status-DEAD-YELLOW {
     background-image: @status-dead-yellow-marker;
     background-image: @status-dead-yellow-marker;
   }
   }
-  .host-name-search{
+  .host-name-search {
     position: relative;
     position: relative;
     top: 0px;
     top: 0px;
     left: 10px;
     left: 10px;
   }
   }
-  .host-name-pos{
+  .host-name-pos {
     position: relative;
     position: relative;
     top: 10px;
     top: 10px;
   }
   }
@@ -882,138 +887,140 @@ a:focus {
       }
       }
     }
     }
   }
   }
-  }
+}
 
 
-  #host-details {
+#host-details {
 
 
-    margin-top: 27px;
+  margin-top: 27px;
 
 
-    .component-operation-button{
-      background-color: #E5E5E5;
-      background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#E5E5E5), to(#F1F1F1));
-      background-image: -webkit-linear-gradient(top, #E5E5E5, #F1F1F1);
-      background-image: -o-linear-gradient(top, #E5E5E5, #F1F1F1);
-      background-image: linear-gradient(to bottom, #E5E5E5, #F1F1F1);
-      background-image: -moz-linear-gradient(top, #E5E5E5, #F1F1F1);
-      background-repeat: repeat-x;
-      color:#000000;
-    }
-    .caret{
-      border-top-color: #000000;
-      border-bottom-color: #000000;
-    }
-    .health-status-STARTED, .health-status-STARTING {
-      background-image: @status-live-marker;
-      background-repeat: no-repeat;
-      background-position: 0px 4px;
-    }
-    .health-status-STOPPED, .health-status-STOPPING {
-      background-image: @status-dead-marker;
-      background-repeat: no-repeat;
-      background-position: 0px 4px;
-    }
-    .span3.host-components {
-        width: 300px;
-    }
+  .component-operation-button {
+    background-color: #E5E5E5;
+    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#E5E5E5), to(#F1F1F1));
+    background-image: -webkit-linear-gradient(top, #E5E5E5, #F1F1F1);
+    background-image: -o-linear-gradient(top, #E5E5E5, #F1F1F1);
+    background-image: linear-gradient(to bottom, #E5E5E5, #F1F1F1);
+    background-image: -moz-linear-gradient(top, #E5E5E5, #F1F1F1);
+    background-repeat: repeat-x;
+    color: #000000;
+  }
+  .caret {
+    border-top-color: #000000;
+    border-bottom-color: #000000;
+  }
+  .health-status-STARTED, .health-status-STARTING {
+    background-image: @status-live-marker;
+    background-repeat: no-repeat;
+    background-position: 0px 4px;
+  }
+  .health-status-STOPPED, .health-status-STOPPING {
+    background-image: @status-dead-marker;
+    background-repeat: no-repeat;
+    background-position: 0px 4px;
+  }
+  .span3.host-components {
+    width: 300px;
+  }
 
 
-    .health-status-LIVE {
-      background-image: @status-live-marker;
-      background-repeat: no-repeat;
-      background-position: 0px 4px;
-    }
-    .health-status-DEAD {
-      background-image: @status-dead-marker;
-      background-repeat: no-repeat;
-      background-position: 0px 4px;
-    }
-    .health-status-DEAD-ORANGE {
-      background-image: @status-dead-orange-marker;
-      background-repeat: no-repeat;
-      background-position: 0px 4px;
-    }
-    .health-status-DEAD-YELLOW {
-      background-image: @status-dead-yellow-marker;
-      background-repeat: no-repeat;
-      background-position: 0px 4px;
-    }
-    .back {
-      display: block;
-      width: 105px;
-      margin-bottom: 5px;
-    }
-    .box-header .host-title {
-      margin: 0;
-      padding-left: 17px;
-    }
-    .box-header .button-section {
-      margin-bottom: 5px;
-    }
-    hr {
-      margin-bottom: 0;
-      clear: both;
-    }
-    .content {
-      padding: 10px;
-    }
-    .host-configuration .dl-horizontal dt {
-      width: 90px;
-      line-height: 20px;
-    }
-    .host-configuration .dl-horizontal dd {
-      margin-left: 100px;
-      line-height: 20px;
-    }
-    .host-metrics{
-      [class*="span"] {
-        float: left;
-        margin-left: 10px;
-      }
-      .chart-container {
-        .chart-x-axis {
-          left: 30%;
-          width: 40%;
-        }
-      }
+  .health-status-LIVE {
+    background-image: @status-live-marker;
+    background-repeat: no-repeat;
+    background-position: 0px 4px;
+  }
+  .health-status-DEAD {
+    background-image: @status-dead-marker;
+    background-repeat: no-repeat;
+    background-position: 0px 4px;
+  }
+  .health-status-DEAD-ORANGE {
+    background-image: @status-dead-orange-marker;
+    background-repeat: no-repeat;
+    background-position: 0px 4px;
+  }
+  .health-status-DEAD-YELLOW {
+    background-image: @status-dead-yellow-marker;
+    background-repeat: no-repeat;
+    background-position: 0px 4px;
+  }
+  .back {
+    display: block;
+    width: 105px;
+    margin-bottom: 5px;
+  }
+  .box-header .host-title {
+    margin: 0;
+    padding-left: 17px;
+  }
+  .box-header .button-section {
+    margin-bottom: 5px;
+  }
+  hr {
+    margin-bottom: 0;
+    clear: both;
+  }
+  .content {
+    padding: 10px;
+  }
+  .host-configuration .dl-horizontal dt {
+    width: 90px;
+    line-height: 20px;
+  }
+  .host-configuration .dl-horizontal dd {
+    margin-left: 100px;
+    line-height: 20px;
+  }
+  .host-metrics {
+    [class*="span"] {
+      float: left;
+      margin-left: 10px;
     }
     }
-    
-    .host-components {
-      padding: 10px;
-      padding-bottom: 0;
-      border: 1px solid #DEDEDE;
-      border-radius: 4px;
-      background: #F5F5F5;
-    }
-    .host-components .btn-group {
-      margin: 0 5px 10px 0;
-      .components-health{
-        margin-top: 4px;
-        width:13px;
-        height: 13px;
-        float:left;
-        background-position: center center;
+    .chart-container {
+      .chart-x-axis {
+        left: 30%;
+        width: 40%;
       }
       }
     }
     }
   }
   }
-  .background-operations {
-    .open-details {
-      clear: left;
-      display: block;
+
+  .host-components {
+    padding: 10px;
+    padding-bottom: 0;
+    border: 1px solid #DEDEDE;
+    border-radius: 4px;
+    background: #F5F5F5;
+  }
+  .host-components .btn-group {
+    margin: 0 5px 10px 0;
+    .components-health {
+      margin-top: 4px;
+      width: 13px;
+      height: 13px;
       float: left;
       float: left;
-      text-decoration: none;
-      width: 16px;
-    }
-    .operation-details {
-      padding-left: 16px;
-      padding-top: 5px;
-      display: none;
+      background-position: center center;
     }
     }
-    margin-bottom: 10px;
   }
   }
-  .background-operations.is-open {
-    .operation-details {
-      display: block;
-    }
+}
+
+.background-operations {
+  .open-details {
+    clear: left;
+    display: block;
+    float: left;
+    text-decoration: none;
+    width: 16px;
+  }
+  .operation-details {
+    padding-left: 16px;
+    padding-top: 5px;
+    display: none;
+  }
+  margin-bottom: 10px;
+}
+
+.background-operations.is-open {
+  .operation-details {
+    display: block;
   }
   }
+}
 
 
 /*End Hosts*/
 /*End Hosts*/
 
 
@@ -1645,13 +1652,13 @@ ul.inline li {
 
 
 /* End Carousel*/
 /* End Carousel*/
 
 
-#add-host .back{
+#add-host .back {
   display: block;
   display: block;
   width: 105px;
   width: 105px;
   margin-bottom: 10px;
   margin-bottom: 10px;
 }
 }
 
 
-#add-service .back{
+#add-service .back {
   display: block;
   display: block;
   width: 130px;
   width: 130px;
   margin-bottom: 10px;
   margin-bottom: 10px;
@@ -1660,6 +1667,20 @@ ul.inline li {
 #step8-content {
 #step8-content {
   max-height: 440px;
   max-height: 440px;
 }
 }
+
 #step10-content {
 #step10-content {
   max-height: 440px;
   max-height: 440px;
 }
 }
+
+//bootstrap
+//.dropdown-menu .active > a, .dropdown-menu .active > a:hover {
+//  background-color: #49AFCD;
+//  background-image: -moz-linear-gradient(center top, #5BC0DE, #2F96B4);
+//}
+//bootstrap end
+
+// COMBOBOX FIXES
+.combobox-container .btn:hover {
+  background-position: 0;
+}
+// COMBOBOX FIXES END

+ 12 - 4
ambari-web/app/templates/main.hbs

@@ -15,17 +15,25 @@
 * See the License for the specific language governing permissions and
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * limitations under the License.
 -->
 -->
+{{#if isClusterDataLoaded}}
 <div id="main-nav">
 <div id="main-nav">
   <div class="navbar">
   <div class="navbar">
     <div class="navbar-inner">
     <div class="navbar-inner">
       <a class="brand" href="#">
       <a class="brand" href="#">
         My Cluster
         My Cluster
-          {{#if backgroundOperationsCount}}
-            <span class="label operations-count" {{action "showBackgroundOperationsPopup" target="controller"}}>{{backgroundOperationsCount}}</span>
-          {{/if}}
+
+          {{#view App.EmptyView controllerBinding="App.router.backgroundOperationsController"}}
+            {{#if allOperationsCount}}
+              <span class="label operations-count" {{action "showPopup" target="controller"}}>{{allOperationsCount}}</span>
+            {{/if}}
+          {{/view}}
+
       </a>
       </a>
       {{view App.MainMenuView}}
       {{view App.MainMenuView}}
     </div>
     </div>
   </div>
   </div>
 </div>
 </div>
-{{outlet}}
+{{outlet}}
+{{else}}
+<h2>loading...</h2>
+{{/if}}

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

@@ -15,7 +15,7 @@
 * See the License for the specific language governing permissions and
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * limitations under the License.
 -->
 -->
-{{#each operation in backgroundOperations}}
+{{#each operation in allOperations}}
   {{#view App.MainBackgroundOperation contentBinding="operation"}}
   {{#view App.MainBackgroundOperation contentBinding="operation"}}
     <a class="open-details" {{action openDetails target="view"}} href="#">
     <a class="open-details" {{action openDetails target="view"}} href="#">
       <i {{bindAttr class="view.iconClass"}}></i>
       <i {{bindAttr class="view.iconClass"}}></i>

+ 10 - 1
ambari-web/app/templates/main/host.hbs

@@ -18,7 +18,16 @@
 <div id="hosts" class="box">
 <div id="hosts" class="box">
   <div class="box-header">
   <div class="box-header">
     <div class="button-section">
     <div class="button-section">
-      {{view view.RackCombobox}}
+      <div>
+        <div class="span3">
+          {{view view.RackCombobox}}
+        </div>
+        <div class="span">
+          <button {{bindAttr disabled="view.assignRackButtonDisabled"}} class="btn" {{action assignHostsToRack target="App.router.mainHostController"}}>
+            Assign
+          </button>
+        </div>
+      </div>
       <button class="btn btn-inverse add-host-button" {{action addHost}}>
       <button class="btn btn-inverse add-host-button" {{action addHost}}>
         <i class="icon-plus icon-white"></i>
         <i class="icon-plus icon-white"></i>
         Add New Hosts
         Add New Hosts

+ 0 - 40
ambari-web/app/templates/main/service/background_operations_popup.hbs

@@ -1,40 +0,0 @@
-<!--
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance
-* with the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
--->
-{{#each operation in serviceOperations}}
-  {{#view App.MainServiceItemOperations contentBinding="operation"}}
-  <a class="open-details" {{action openDetails target="view"}} href="#">
-    <i {{bindAttr class="view.iconClass"}}></i>
-  </a>
-  {{operation.command}} on "{{operation.serviceName}}" {{operation.role}}
-  <div class="operation-details">
-    <p>{{operation.detail}}</p>
-    <a {{action showOperationLog target="view"}} href="#">
-      {{#if view.isOpenShowLog}}Hide{{else}}Show{{/if}} operation log
-    </a>
-    {{#if view.isOpenShowLog}}
-    <div class="operation-log">
-      <dl class="dl-horizontal">
-        <dt>Exit Code:</dt><dd>{{operation.exitcode}}</dd>
-        <dt>Std Out:</dt><dd>{{operation.stdout}}</dd>
-        <dt>Error:</dt><dd>{{operation.stderror}}</dd>
-      </dl>
-    </div>
-    {{/if}}
-  </div>
-  {{/view}}
-{{/each}}

+ 2 - 2
ambari-web/app/templates/main/service/menu_item.hbs

@@ -17,9 +17,9 @@
 -->
 -->
 
 
 <a href="#/main/services/{{unbound view.content.id}}/summary">
 <a href="#/main/services/{{unbound view.content.id}}/summary">
-  {{view App.MainDashboardServiceHealthView serviceBinding="view.content"}}&nbsp;<span>{{unbound view.content.displayName}}</span>
+  {{view App.MainDashboardServiceHealthView serviceBinding="view.content"}}&nbsp;<span>{{unbound view.content.label}}</span>
   {{#if view.serviceOperationsCount}}
   {{#if view.serviceOperationsCount}}
-    <span class="label operations-count" {{action "showBackgroundOperationsPopup" target="App.router.mainServiceItemController"}}>
+    <span class="label operations-count" {{action "showPopup" target="App.router.backgroundOperationsController"}}>
       {{view.serviceOperationsCount}}
       {{view.serviceOperationsCount}}
     </span>
     </span>
   {{/if}}
   {{/if}}

+ 0 - 4
ambari-web/app/utils/http_client.js

@@ -108,10 +108,6 @@ App.HttpClient = Em.Object.create({
 //  }
 //  }
 });
 });
 
 
-//App.HttpClient.get("/data/hosts/hosts.json", App.hostsMapper);
-
-App.HttpClient.get("/data/dashboard/services.json", App.servicesMapper);
-
 /*App.HttpClient.get(
 /*App.HttpClient.get(
   'http://nagiosserver/hdp/nagios/nagios_alerts.php?q1=alerts&alert_type=all',
   'http://nagiosserver/hdp/nagios/nagios_alerts.php?q1=alerts&alert_type=all',
   App.alertsMapper,
   App.alertsMapper,

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

@@ -31,6 +31,7 @@ require('views/common/metric');
 require('views/common/time_range');
 require('views/common/time_range');
 require('views/common/form/field');
 require('views/common/form/field');
 require('views/common/quick_view_link_view');
 require('views/common/quick_view_link_view');
+require('views/common/empty_view');
 require('views/login');
 require('views/login');
 require('views/main');
 require('views/main');
 require('views/main/test');
 require('views/main/test');

+ 9 - 1
ambari-web/app/views/common/combobox.js

@@ -59,9 +59,17 @@ App.Combobox = Em.Select.extend({
     this.get('combobox').clearTarget();
     this.get('combobox').clearTarget();
 
 
   },
   },
+
+  test:function () {
+    console.warn("qwerty");
+  },
+
   didInsertElement:function () {
   didInsertElement:function () {
     this._super();
     this._super();
-    this.set('combobox', this.$().combobox().data('combobox'));
+
+    this.set('combobox', this.$().combobox({
+      template:'<div class="combobox-container"><input type="text" autocomplete="off" /><button class="add-on btn dropdown-toggle" data-dropdown="dropdown"><span class="caret"/><span class="combobox-clear"><i class="icon-remove"/></span></button></div>'
+    }).data('combobox'));
 
 
     this.clearTextFieldValue(); // fix of script tags in
     this.clearTextFieldValue(); // fix of script tags in
 
 

+ 24 - 0
ambari-web/app/views/common/empty_view.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.EmptyView = Em.View.extend({
+  tagName : ''
+});
+

+ 2 - 0
ambari-web/app/views/main/apps_view.js

@@ -237,6 +237,8 @@ App.MainAppsView = Em.View.extend({
     if (perPage !== -1) { // Change page after reDraw (if show All is selected this will not happens)
     if (perPage !== -1) { // Change page after reDraw (if show All is selected this will not happens)
       this.get('oTable').fnPageChange(Math.floor(rowIndex / perPage));
       this.get('oTable').fnPageChange(Math.floor(rowIndex / perPage));
     }
     }
+    var d = this.get('oTable').fnGetData();
+    console.log(this.get('oTable').fnSettings()['aiDisplay']);
   }.observes('controller.lastStarClicked'),
   }.observes('controller.lastStarClicked'),
   /**
   /**
    * Flush all starred runs
    * Flush all starred runs

+ 12 - 2
ambari-web/app/views/main/host.js

@@ -143,6 +143,16 @@ App.MainHostView = Em.View.extend({
     placeholderText:Em.I18n.t('hosts.assignRack'),
     placeholderText:Em.I18n.t('hosts.assignRack'),
     selectionBinding:"App.router.mainHostController.selectedRack",
     selectionBinding:"App.router.mainHostController.selectedRack",
     optionLabelPath:"content.clusterName",
     optionLabelPath:"content.clusterName",
-    optionValuePath:"content.id"
-  })
+    optionValuePath:"content.id",
+    didInsertElement:function () {
+      this._super();
+      App.router.get('mainHostController').propertyDidChange('selectedHostsIds');
+    }
+  }),
+
+  assignRackButtonDisabled:function () {
+    var selectedHostsIds = App.router.get('mainHostController.selectedHostsIds');
+    var rack = App.router.get('mainHostController.selectedRack');
+    return (selectedHostsIds.length && rack && rack.constructor == 'App.Cluster') ? false : "disabled";
+  }.property('App.router.mainHostController.selectedHostsIds', 'App.router.mainHostController.selectedRack')
 });
 });

+ 3 - 4
ambari-web/app/views/main/service/menu.js

@@ -62,10 +62,9 @@ App.MainServiceMenuView = Em.CollectionView.extend({
     classNameBindings: ["active"],
     classNameBindings: ["active"],
     active: "",
     active: "",
     serviceOperationsCount: function () {
     serviceOperationsCount: function () {
-      if (this.get('content.id') == App.router.get('mainServiceItemController.content.id')) {
-        return App.router.get('mainServiceItemController.serviceOperationsCount');
-      }
-    }.property('App.router.mainServiceItemController.serviceOperationsCount'),
+      var operations = App.router.get('backgroundOperationsController').getOperationsFor(this.get('content.serviceName'));
+      return operations.length;
+    }.property('App.router.backgroundOperationsController.serviceOperationsChangeTime'),
 
 
     templateName: require('templates/main/service/menu_item')
     templateName: require('templates/main/service/menu_item')
   })
   })

+ 5 - 1
ambari-web/app/views/main/test.js

@@ -20,5 +20,9 @@ var App = require('app');
 
 
 App.MainTestView = Em.View.extend({
 App.MainTestView = Em.View.extend({
   templateName: require('templates/main/test'),
   templateName: require('templates/main/test'),
-  services: App.Service1.find()
+  services: App.Service1.find(),
+  hosts: function(){
+      console.log(App.Host.find());
+      return 'hosts';
+  }.property()
 });
 });

+ 10 - 2
ambari-web/app/views/wizard/step2_view.js

@@ -75,8 +75,16 @@ App.WizardStep2View = Em.View.extend({
   }.property(),
   }.property(),
 
 
   sshKeyPreviewClass: function() {
   sshKeyPreviewClass: function() {
-    return (this.get('controller.content.sshKey').trim() != '') ? 'sshKey-file-view help-inline' : 'hidden';
-  }.property('controller.content.sshKey')
+    if (this.get('controller.content.sshKey').trim() != '') {
+      if (this.get('controller.content.manualInstall')) {
+        return 'sshKey-file-view disabled';
+      } else {
+        return 'sshKey-file-view';
+      }
+    } else {
+      return 'hidden';
+    }
+  }.property('controller.content.sshKey', 'controller.content.manualInstall')
 
 
 });
 });
 
 

+ 25 - 24
ambari-web/package.json

@@ -1,31 +1,32 @@
 {
 {
-  "name": "Ambari",
-  "description": "Front-end package for the Apache Ambari Project",
-  "version": "1.0.0",
-  "homepage": "",
-  "repository": {
-    "type": "git",
-    "url": ""
+  "name":"Ambari",
+  "description":"Front-end package for the Apache Ambari Project",
+  "version":"1.0.0",
+  "homepage":"",
+  "repository":{
+    "type":"git",
+    "url":""
   },
   },
-  "engines": {
-    "node": "~0.6.10 || 0.8 || 0.9"
+  "engines":{
+    "node":"~0.6.10 || 0.8 || 0.9"
   },
   },
-  "scripts": {
-    "start": "brunch watch --server"
+  "scripts":{
+    "start":"brunch watch --server"
   },
   },
-  "dependencies": {
-    "javascript-brunch": ">= 1.0 < 1.5",
-    "css-brunch": ">= 1.0 < 1.5",
-    "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"
+  "dependencies":{
+    "javascript-brunch":">= 1.0 < 1.5",
+    "css-brunch":">= 1.0 < 1.5",
+    "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"
   },
   },
-  "devDependencies": {
-    "mocha": "0.14.0",
-    "chai": "1.2.0",
-    "sinon": "1.4.2",
-    "sinon-chai": "2.1.2",
-    "express": "2.5.8"
+  "devDependencies":{
+    "mocha":"0.14.0",
+    "chai":"1.2.0",
+    "sinon":"1.4.2",
+    "sinon-chai":"2.1.2",
+    "express":"2.5.8",
+    "jsdom":"0.2.19"
   }
   }
 }
 }

+ 12 - 4
ambari-web/pom.xml

@@ -9,14 +9,19 @@
   License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
   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 
   OF ANY KIND, either express or implied. See the License for the specific 
   language governing permissions and limitations under the License. -->
   language governing permissions and limitations under the License. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.apache.ambari</groupId>
+    <artifactId>ambari-project</artifactId>
+    <version>1.0.3-SNAPSHOT</version>
+    <relativePath>../ambari-project</relativePath>
+  </parent>
   <modelVersion>4.0.0</modelVersion>
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.apache.ambari</groupId>
   <groupId>org.apache.ambari</groupId>
   <artifactId>ambari-web</artifactId>
   <artifactId>ambari-web</artifactId>
   <packaging>pom</packaging>
   <packaging>pom</packaging>
   <name>Ambari Web</name>
   <name>Ambari Web</name>
-  <version>1.0.3</version>
+  <version>1.0.3-SNAPSHOT</version>
   <description>Ambari Web</description>
   <description>Ambari Web</description>
   <dependencies>
   <dependencies>
     <dependency>
     <dependency>
@@ -40,6 +45,9 @@
             </goals>
             </goals>
             <configuration>
             <configuration>
               <target name="ambari-web-compile">
               <target name="ambari-web-compile">
+                <exec dir="${basedir}" executable="npm" failonerror="false">
+                  <arg value="install"/>
+                </exec>
                 <exec dir="${basedir}" executable="brunch" failonerror="false">
                 <exec dir="${basedir}" executable="brunch" failonerror="false">
                   <arg value="build"/>
                   <arg value="build"/>
                 </exec>
                 </exec>
@@ -73,7 +81,7 @@
                   <fileset dir="${basedir}/../ambari-web/public"/>
                   <fileset dir="${basedir}/../ambari-web/public"/>
                 </copy>
                 </copy>
                 -->
                 -->
-                <symlink overwrite="true" link="${basedir}/../ambari-server/target/ambari-server-${project.version}/ambari-server-${project.version}/web" resource="${basedir}/public" failonerror="false" />
+                <symlink overwrite="true" link="${basedir}/../ambari-server/target/ambari-server-${project.version}-dist/ambari-server-${project.version}/web" resource="${basedir}/public" failonerror="false"/>
               </target>
               </target>
             </configuration>
             </configuration>
           </execution>
           </execution>

+ 36 - 3
ambari-web/vendor/scripts/bootstrap-combobox.js

@@ -135,6 +135,14 @@
         this.$element.on('keydown', $.proxy(this.keypress, this))
         this.$element.on('keydown', $.proxy(this.keypress, this))
       }
       }
 
 
+//      hide menu hack
+      this.$button.on('mouseenter', $.proxy(this.addClassOnMouseEnter, this.$button))
+        .on('mouseleave', $.proxy(this.addClassOnMouseLeave, this.$button));
+
+      $(window).on('click', $.proxy(this.hideList, this));
+//      hide menu hack end
+
+
       this.$menu
       this.$menu
         .on('click', $.proxy(this.click, this))
         .on('click', $.proxy(this.click, this))
         .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
         .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
@@ -173,10 +181,20 @@
 
 
       e.stopPropagation()
       e.stopPropagation()
       e.preventDefault()
       e.preventDefault()
-    }
+    },
+
+    addClassOnMouseEnter:function (e) {
+      console.warn("Enter");
+      this.addClass('hover');
+    },
+
+    addClassOnMouseLeave:function (e) {
+      console.warn("Leave");
+      this.removeClass('hover');
+    },
 
 
     // modified typeahead function to only hide menu if it is visible
     // modified typeahead function to only hide menu if it is visible
-    , blur:function (e) {
+    blur:function (e) {
       var that = this
       var that = this
       e.stopPropagation()
       e.stopPropagation()
       e.preventDefault()
       e.preventDefault()
@@ -190,6 +208,21 @@
           that.hide()
           that.hide()
         }, 150)
         }, 150)
       }
       }
+    },
+
+    /**
+     * hide list
+     * @param e
+     */
+    hideList:function (e) {
+      if (!this.$button.is(".hover")) {
+        var that = this;
+        if (this.shown) {
+          setTimeout(function () {
+            that.hide()
+          }, 150)
+        }
+      }
     }
     }
   })
   })
 
 
@@ -207,7 +240,7 @@
   }
   }
 
 
   $.fn.combobox.defaults = {
   $.fn.combobox.defaults = {
-    template:'<div class="combobox-container"><input type="text" autocomplete="off" /><button class="add-on btn dropdown-toggle" data-dropdown="dropdown"><span class="caret"/><span class="combobox-clear"><i class="icon-remove"/></span></button></div>',
+    template:'<div class="combobox-container"><input type="text" autocomplete="off" /><span class="add-on btn dropdown-toggle" data-dropdown="dropdown"><span class="caret"/><span class="combobox-clear"><i class="icon-remove"/></span></span></div>',
     menu:'<ul class="typeahead typeahead-long dropdown-menu"></ul>',
     menu:'<ul class="typeahead typeahead-long dropdown-menu"></ul>',
     item:'<li><a href="#"></a></li>', placeholder:null
     item:'<li><a href="#"></a></li>', placeholder:null
   }
   }

+ 1 - 0
ambari-web/vendor/styles/bootstrap-combobox.css

@@ -140,6 +140,7 @@
 .open.btn-group .combobox-clear {
 .open.btn-group .combobox-clear {
     opacity: 1;
     opacity: 1;
     filter: alpha(opacity = 100);
     filter: alpha(opacity = 100);
+
 }
 }
 
 
 .typeahead-long {
 .typeahead-long {