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':' ! ',
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