You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2014/01/22 12:34:37 UTC

git commit: AMBARI-4380. Hosts API-calls. (onechiporenko)

Updated Branches:
  refs/heads/trunk a2d7c9e57 -> 26fbaef39


AMBARI-4380. Hosts API-calls. (onechiporenko)


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

Branch: refs/heads/trunk
Commit: 26fbaef393288000fd666886fd89bcdabf52df77
Parents: a2d7c9e
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Wed Jan 22 13:17:18 2014 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Wed Jan 22 13:17:18 2014 +0200

----------------------------------------------------------------------
 ambari-web/app/controllers/main/host.js         |  64 +++++
 ambari-web/app/messages.js                      |   1 +
 .../templates/main/host/bulk_operation_menu.hbs |   4 +-
 ambari-web/app/utils/ajax.js                    |  21 ++
 ambari-web/app/views/common/table_view.js       |   8 +
 ambari-web/app/views/main/host.js               |   7 +
 .../views/main/host/hosts_table_menu_view.js    |  42 +++-
 ambari-web/test/controllers/main/host_test.js   | 249 +++++++++++++++----
 8 files changed, 339 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/controllers/main/host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/host.js b/ambari-web/app/controllers/main/host.js
index 5536742..97c9358 100644
--- a/ambari-web/app/controllers/main/host.js
+++ b/ambari-web/app/controllers/main/host.js
@@ -156,6 +156,70 @@ App.MainHostController = Em.ArrayController.extend({
         }
       }
     }
+    else {
+      if (operationData.action === 'RESTART') {
+        this.bulkOperationForHostsRestart(operationData, hosts);
+      }
+      else {
+        this.bulkOperationForHosts(operationData, hosts);
+      }
+    }
+  },
+
+  /**
+   * Do bulk operation for selected hosts
+   * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
+   * @param {Array} hosts - list of affected hosts
+   */
+  bulkOperationForHosts: function(operationData, hosts) {
+    var query = [];
+    hosts.forEach(function(host) {
+      var subQuery = '(HostRoles/component_name.in(%@)&HostRoles/host_name=' + host.get('hostName') + ')';
+      var components = [];
+      host.get('hostComponents').forEach(function(hostComponent) {
+        if (hostComponent.get('isMaster') || hostComponent.get('isSlave')) {
+          if (hostComponent.get('workStatus') === operationData.actionToCheck) {
+            components.push(hostComponent.get('componentName'));
+          }
+        }
+      });
+      if (components.length) {
+        query.push(subQuery.fmt(components.join(',')));
+      }
+    });
+    if (query.length) {
+      query = query.join('|');
+      App.ajax.send({
+        name: 'bulk_request.hosts.all_components',
+        sender: this,
+        data: {
+          query: query,
+          state: operationData.action,
+          requestInfo: operationData.message
+        },
+        success: 'bulkOperationForHostComponentsSuccessCallback'
+      });
+    }
+    else {
+      App.ModalPopup.show({
+        header: Em.I18n.t('rolling.nothingToDo.header'),
+        body: Em.I18n.t('rolling.nothingToDo.body').format(Em.I18n.t('hosts.host.maintainance.allComponents.context')),
+        secondary: false
+      });
+    }
+  },
+
+  /**
+   * Bulk restart for selected hosts
+   * @param {Object} operationData - data about bulk operation (action, hostComponents etc)
+   * @param {Array} hosts - list of affected hosts
+   */
+  bulkOperationForHostsRestart: function(operationData, hosts) {
+    var hostComponents = [];
+    hosts.forEach(function(host) {
+      hostComponents.pushObjects(host.get('hostComponents').toArray());
+    });
+    batchUtils.restartHostComponents(hostComponents);
   },
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 77eb4b4..a2bc6fc 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1412,6 +1412,7 @@ Em.I18n.translations = {
   'hosts.host.healthStatusCategory.orange': "Slave Down",
   'hosts.host.healthStatusCategory.yellow': "Lost Heartbeat",
   'hosts.host.alerts.label': 'Alerts',
+  'hosts.host.maintainance.allComponents.context': 'All Host Components',
   'hosts.host.maintainance.stopAllComponents.context': 'Stop All Host Components',
   'hosts.host.maintainance.startAllComponents.context': 'Start All Host Components',
   'hosts.host.alerts.st':'&nbsp;!&nbsp;',

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/templates/main/host/bulk_operation_menu.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host/bulk_operation_menu.hbs b/ambari-web/app/templates/main/host/bulk_operation_menu.hbs
index d54d64f..e9d96d8 100644
--- a/ambari-web/app/templates/main/host/bulk_operation_menu.hbs
+++ b/ambari-web/app/templates/main/host/bulk_operation_menu.hbs
@@ -22,7 +22,7 @@
     <li class="dropdown-submenu">
       <a tabindex="-1" href="javascript:void(null);">{{view.menuItems.s.label}}
         ({{view.parentView.parentView.selectedCategory.hostsCount}})</a>
-      <ul class="dropdown-menu">
+      <ul {{bindAttr class="view.parentView.parentView.selectedCategory.hasHosts::hidden :dropdown-menu"}}>
         {{#each subMenuItem in view.menuItems.s.submenu}}
           <li class="dropdown-submenu">
             <a href="javascript:void(null);">{{subMenuItem.label}}</a>
@@ -40,7 +40,7 @@
     <li class="dropdown-submenu">
       <a tabindex="-1" href="javascript:void(null);">{{view.menuItems.f.label}}
         ({{view.parentView.parentView.filteredContent.length}})</a>
-      <ul class="dropdown-menu">
+      <ul {{bindAttr class="view.parentView.parentView.hasFilteredItems::hidden :dropdown-menu"}}>
         {{#each subMenuItem in view.menuItems.f.submenu}}
           <li class="dropdown-submenu">
             <a href="javascript:void(null);">{{subMenuItem.label}}</a>

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index dc8aa5d..ed2b77d 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -1356,6 +1356,27 @@ var urls = {
     }
   },
 
+  'bulk_request.hosts.all_components': {
+    'real': '/clusters/{clusterName}/host_components',
+    'mock': '',
+    'format': function(data) {
+      return {
+        type: 'PUT',
+        data: JSON.stringify({
+          RequestInfo: {
+            context: data.requestInfo,
+            query: data.query
+          },
+          Body: {
+            HostRoles: {
+              state: data.state
+            }
+          }
+        })
+      }
+    }
+  },
+
   'bulk_request.decommission': {
     'real' : '/clusters/{clusterName}/requests',
     'mock' : '',

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/views/common/table_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/table_view.js b/ambari-web/app/views/common/table_view.js
index e5751dd..1eabd75 100644
--- a/ambari-web/app/views/common/table_view.js
+++ b/ambari-web/app/views/common/table_view.js
@@ -342,6 +342,14 @@ App.TableView = Em.View.extend({
   filteredContent: [],
 
   /**
+   * Determine if <code>filteredContent</code> is empty or not
+   * @type {Boolean}
+   */
+  hasFilteredItems: function() {
+    return !!this.get('filteredContent.length');
+  }.property('filteredContent.length'),
+
+  /**
    * Contains content to show on the current page of data page view
    * @type {Array}
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/views/main/host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host.js b/ambari-web/app/views/main/host.js
index 340e71a..8057a4f 100644
--- a/ambari-web/app/views/main/host.js
+++ b/ambari-web/app/views/main/host.js
@@ -333,6 +333,12 @@ App.MainHostView = App.TableView.extend({
     hostsCount: 0,
 
     /**
+     * Determine if category has hosts
+     * @type {Boolean}
+     */
+    hasHosts: false,
+
+    /**
      * Add "active" class for category span-wrapper if current category is selected
      * @type {String}
      */
@@ -362,6 +368,7 @@ App.MainHostView = App.TableView.extend({
       else {
         this.set('hostsCount', this.get('view.content').filterProperty(this.get('hostProperty')).get('length'));
       }
+      this.set('hasHosts', !!this.get('hostsCount'));
     },
 
     /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/app/views/main/host/hosts_table_menu_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host/hosts_table_menu_view.js b/ambari-web/app/views/main/host/hosts_table_menu_view.js
index b6bd742..9fbbdef 100644
--- a/ambari-web/app/views/main/host/hosts_table_menu_view.js
+++ b/ambari-web/app/views/main/host/hosts_table_menu_view.js
@@ -24,6 +24,20 @@ App.HostTableMenuView = Em.View.extend({
 
   /**
    * Get third-level menu items for slave components
+   * @param {String} componentNameForDecommission for decommission and recommission used another component name
+   * @param {String} componentNameForOtherActions host component name that should be processed
+   * operationData format:
+   * <code>
+   *  {
+   *    action: 'STARTED|INSTALLED|RESTART|DECOMMISSION|DECOMMISSION_OFF', // action for selected host components
+   *    message: 'some text', // just text to BG popup
+   *    componentName: 'DATANODE|NODEMANAGER...', //component name that should be processed
+   *    realComponentName: 'DATANODE|NODEMANAGER...', // used only for decommission(_off) actions
+   *    serviceName: 'HDFS|YARN|HBASE...', // service name of the processed component
+   *    componentNameFormatted: 'DataNodes|NodeManagers...' // "user-friendly" string with component name (used in BG popup)
+   *  }
+   *  </code>
+   *
    * @returns {Array}
    */
   getSlaveItemsTemplate: function(componentNameForDecommission, componentNameForOtherActions) {
@@ -75,6 +89,14 @@ App.HostTableMenuView = Em.View.extend({
 
   /**
    * Get third-level menu items for Hosts
+   * operationData format:
+   * <code>
+   *  {
+   *    action: 'STARTED|INSTALLED|RESTART', // action for selected hosts (will be applied for each host component in selected hosts)
+   *    actionToCheck: 'INSTALLED|STARTED' // state to filter host components should be processed
+   *    message: 'some text', // just text to BG popup
+   *  }
+   *  </code>
    * @returns {Array}
    */
   getHostItemsTemplate: function() {
@@ -82,25 +104,28 @@ App.HostTableMenuView = Em.View.extend({
       Em.Object.create({
         label: Em.I18n.t('hosts.host.details.startAllComponents'),
         operationData: Em.Object.create({
-          action: 'start_all',
+          action: 'STARTED',
+          actionToCheck: 'INSTALLED',
           message: Em.I18n.t('hosts.host.details.startAllComponents')
         })
       }),
       Em.Object.create({
         label: Em.I18n.t('hosts.host.details.stopAllComponents'),
         operationData: Em.Object.create({
-          action: 'stop_all',
+          action: 'INSTALLED',
+          actionToCheck: 'STARTED',
           message: Em.I18n.t('hosts.host.details.stopAllComponents')
         })
       }),
       Em.Object.create({
         label: Em.I18n.t('hosts.table.menu.l2.restartAllComponents'),
         operationData: Em.Object.create({
-          action: 'restart_all',
+          action: 'RESTART',
           message: Em.I18n.t('hosts.table.menu.l2.restartAllComponents')
         })
-      }),
-      Em.Object.create({
+      })
+      //@todo uncomment when API will be ready
+      /*Em.Object.create({
         label: Em.I18n.t('maintenance.turnOn'),
         operationData: Em.Object.create({
           action: 'turn_on_maintenance',
@@ -113,13 +138,18 @@ App.HostTableMenuView = Em.View.extend({
           action: 'turn_off_maintenance',
           message: Em.I18n.t('maintenance.turnOff')
         })
-      })
+      })*/
     ]);
   },
 
   /**
    * Get second-level menu
    * @param {String} selection
+   * <code>
+   *   "s" - selected hosts
+   *   "a" - all hosts
+   *   "f" - filtered hosts
+   * </code>
    * @returns {Array}
    */
   getSubMenuItemsTemplate: function(selection) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/26fbaef3/ambari-web/test/controllers/main/host_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/host_test.js b/ambari-web/test/controllers/main/host_test.js
index 1260935..f18e92a 100644
--- a/ambari-web/test/controllers/main/host_test.js
+++ b/ambari-web/test/controllers/main/host_test.js
@@ -16,70 +16,221 @@
  * limitations under the License.
  */
 
-/*
 var App = require('app');
-require('models/cluster');
-require('models/service');
-require('models/pagination');
+var validator = require('utils/validator');
+require('utils/component');
+require('utils/batch_scheduled_requests');
 require('controllers/main/host');
 
 describe('MainHostController', function () {
-    describe('#sortByName()', function () {
-        it('should change isSort value to true', function () {
-            var mainHostController = App.MainHostController.create();
-            mainHostController.set('isSort', false);
-            mainHostController.sortByName();
-            expect(mainHostController.get('isSort')).to.equal(true);
-        });
 
+  var hostController;
 
-        it('should inverse sortingAsc ', function () {
-            var mainHostController = App.MainHostController.create();
-            mainHostController.set('sortingAsc', false);
-            mainHostController.sortByName();
-            expect(mainHostController.get('sortingAsc')).to.equal(true);
-            mainHostController.sortByName();
-            expect(mainHostController.get('sortingAsc')).to.equal(false);
-        })
+  describe('#bulkOperation', function() {
+
+    beforeEach(function() {
+      hostController = App.MainHostController.create({
+        bulkOperationForHostsRestart: function(){},
+        bulkOperationForHosts: function(){},
+        bulkOperationForHostComponentsRestart: function(){},
+        bulkOperationForHostComponentsDecommission: function(){},
+        bulkOperationForHostComponents: function(){}
+      });
+      sinon.spy(hostController, 'bulkOperationForHostsRestart');
+      sinon.spy(hostController, 'bulkOperationForHosts');
+      sinon.spy(hostController, 'bulkOperationForHostComponentsRestart');
+      sinon.spy(hostController, 'bulkOperationForHostComponentsDecommission');
+      sinon.spy(hostController, 'bulkOperationForHostComponents');
+    });
+
+    afterEach(function() {
+      hostController.bulkOperationForHosts.restore();
+      hostController.bulkOperationForHostsRestart.restore();
+      hostController.bulkOperationForHostComponentsRestart.restore();
+      hostController.bulkOperationForHostComponentsDecommission.restore();
+      hostController.bulkOperationForHostComponents.restore();
+    });
+
+    it('RESTART for hosts', function() {
+      var operationData = {
+        action: 'RESTART'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostsRestart.calledOnce).to.be.true;
+    });
+
+    it('START for hosts', function() {
+      var operationData = {
+        action: 'STARTED'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHosts.calledOnce).to.be.true;
+    });
+
+    it('STOP for hosts', function() {
+      var operationData = {
+        action: 'INSTALLED'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHosts.calledOnce).to.be.true;
+    });
+
+    it('RESTART for hostComponents', function() {
+      var operationData = {
+        action: 'RESTART',
+        componentNameFormatted: 'DataNodes'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostComponentsRestart.calledOnce).to.be.true;
+    });
+
+    it('START for hostComponents', function() {
+      var operationData = {
+        action: 'STARTED',
+        componentNameFormatted: 'DataNodes'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostComponents.calledOnce).to.be.true;
+    });
+
+    it('STOP for hostComponents', function() {
+      var operationData = {
+        action: 'INSTALLED',
+        componentNameFormatted: 'DataNodes'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostComponents.calledOnce).to.be.true;
+    });
+
+    it('DECOMMISSION for hostComponents', function() {
+      var operationData = {
+        action: 'DECOMMISSION',
+        componentNameFormatted: 'DataNodes'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostComponentsDecommission.calledOnce).to.be.true;
+    });
+
+    it('RECOMMISSION for hostComponents', function() {
+      var operationData = {
+        action: 'DECOMMISSION_OFF',
+        componentNameFormatted: 'DataNodes'
+      };
+      hostController.bulkOperation(operationData, []);
+      expect(hostController.bulkOperationForHostComponentsDecommission.calledOnce).to.be.true;
+    });
+
+  });
+
+  describe('#bulkOperationForHosts', function() {
+
+    beforeEach(function(){
+      hostController = App.MainHostController.create({});
+      sinon.spy($, 'ajax');
+    });
+
+    afterEach(function() {
+      $.ajax.restore();
     });
 
+    var tests = [
+      {
+        operationData: {},
+        hosts: [],
+        m: 'no hosts',
+        e: false
+      },
+      {
+        operationData: {
+          actionToCheck: 'STARTED'
+        },
+        hosts: [
+          Em.Object.create({
+            hostComponents: Em.A([
+              Em.Object.create({isMaster: true, isSlave: false, host: {hostName:'host1'}, workStatus: 'STARTED', componentName: 'NAMENODE'}),
+              Em.Object.create({isMaster: false, isSlave: true, host: {hostName:'host1'}, workStatus: 'STARTED', componentName: 'DATANODE'})
+            ])
+          })
+        ],
+        m: '1 host. components are in proper state',
+        e: true
+      },
+      {
+        operationData: {
+          actionToCheck: 'INSTALLED'
+        },
+        hosts: [
+          Em.Object.create({
+            hostComponents: Em.A([
+              Em.Object.create({isMaster: true, isSlave: false, host: {hostName:'host1'}, workStatus: 'STARTED', componentName: 'NAMENODE'}),
+              Em.Object.create({isMaster: false, isSlave: true, host: {hostName:'host1'}, workStatus: 'STARTED', componentName: 'DATANODE'})
+            ])
+          })
+        ],
+        m: '1 host. components are not in proper state',
+        e: false
+      },
+      {
+        operationData: {
+          actionToCheck: 'INSTALLED'
+        },
+        hosts: [
+          Em.Object.create({
+            hostComponents: Em.A([
+              Em.Object.create({isMaster: true, isSlave: false, host: {hostName:'host1'}, workStatus: 'INSTALLED', componentName: 'NAMENODE'}),
+              Em.Object.create({isMaster: false, isSlave: true, host: {hostName:'host1'}, workStatus: 'STARTED', componentName: 'DATANODE'})
+            ])
+          })
+        ],
+        m: '1 host. some components are in proper state',
+        e: true
+      }
+    ];
 
-    describe('#showNextPage, #showPreviousPage()', function () {
-        it('should change rangeStart according to page', function () {
-            var mainHostController = App.MainHostController.create();
-            mainHostController.set('pageSize', 3);
-            mainHostController.showNextPage();
-            expect(mainHostController.get('rangeStart')).to.equal(3);
-            mainHostController.showPreviousPage();
-            expect(mainHostController.get('rangeStart')).to.equal(0);
-        })
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        hostController.bulkOperationForHosts(test.operationData, test.hosts);
+        expect($.ajax.called).to.equal(test.e);
+      });
     });
 
+  });
+
+  describe('#bulkOperationForHostsRestart', function() {
+
+    beforeEach(function(){
+      hostController = App.MainHostController.create({});
+      sinon.spy($, 'ajax');
+    });
 
-    describe('#sortClass()', function () {
-        it('should return \'icon-arrow-down\' if sortingAsc is true', function () {
-            var mainHostController = App.MainHostController.create({});
-            mainHostController.set('sortingAsc', true);
-            expect(mainHostController.get('sortClass')).to.equal('icon-arrow-down');
-        });
-        it('should return \'icon-arrow-up\' if sortingAsc is false', function () {
-            var mainHostController = App.MainHostController.create({});
-            mainHostController.set('sortingAsc', false);
-            expect(mainHostController.get('sortClass')).to.equal('icon-arrow-up');
-        })
+    afterEach(function() {
+      $.ajax.restore();
     });
 
+    var tests = [
+      {
+        hosts: Em.A([]),
+        m: 'No hosts',
+        e: false
+      },
+      {
+        hosts: Em.A([
+          Em.Object.create({
+            hostComponents: Em.A([Em.Object.create({}), Em.Object.create({})])
+          })
+        ]),
+        m: 'One host',
+        e: true
+      }
+    ];
 
-    describe('#allChecked', function () {
-        it('should fill selectedhostsids array', function () {
-            var mainHostController = App.MainHostController.create();
-            mainHostController.set('allChecked', false);
-            expect(mainHostController.get('selectedHostsIds').length).to.equal(0);
-            mainHostController.set('allChecked', true);
-            expect(!!(mainHostController.get('selectedHostsIds').length)).to.equal(true);
-        })
+    tests.forEach(function(test) {
+      it(test.m, function() {
+        hostController.bulkOperationForHostsRestart({}, test.hosts);
+        expect($.ajax.calledOnce).to.equal(test.e)
+      });
     });
 
+  });
 
-});
-*/
+});
\ No newline at end of file