Allow interfaces configuration for provisioned node

Closes-Bug: #1518391

Change-Id: Ia122d2f3d9130b2650648de3e31814360c8b0e67
This commit is contained in:
Julia Aranovich 2015-12-04 14:38:57 +03:00
parent 218e8a2ab3
commit a972bd58ff
8 changed files with 47 additions and 40 deletions

View File

@ -13,7 +13,16 @@
* License for the specific language governing permissions and limitations
* under the License.
**/
define(['jquery', 'underscore', 'backbone', 'utils', 'i18n', 'dispatcher', 'react', 'react.backbone'], function($, _, Backbone, utils, i18n, dispatcher, React) {
define([
'jquery',
'underscore',
'backbone',
'utils',
'i18n',
'dispatcher',
'react',
'react.backbone'
], function($, _, Backbone, utils, i18n, dispatcher, React) {
'use strict';
return {
@ -144,27 +153,6 @@ define(['jquery', 'underscore', 'backbone', 'utils', 'i18n', 'dispatcher', 'reac
});
}
};
},
nodeConfigurationScreenMixin: {
getNodeList: function(options) {
var utils = require('utils'),
models = require('models'),
nodeIds = utils.deserializeTabOptions(options.screenOptions[0]).nodes,
ids = nodeIds ? nodeIds.split(',').map(function(id) {return parseInt(id, 10);}) : [],
nodes = new models.Nodes(options.cluster.get('nodes').getByIds(ids));
if (nodes.length && nodes.length == ids.length) {return nodes;}
},
isLocked: function() {
return !!this.props.cluster.task({group: 'deployment', active: true}) || !_.all(this.props.nodes.invoke('isConfigurable'));
},
renderBackToNodeListButton: function() {
return (
<a className='btn btn-default' href={'#cluster/' + this.props.cluster.id + '/nodes'} disabled={this.state.actionInProgress}>
{i18n('cluster_page.nodes_tab.back_to_nodes_button')}
</a>
);
}
}
};
});

View File

@ -432,8 +432,13 @@ define([
this.get('pending_deletion') ||
this.get('cluster') && !!this.get('pending_roles').length;
},
isConfigurable: function() {
return this.get('pending_addition') || this.get('status') == 'error';
areDisksConfigurable: function() {
var status = this.get('status');
return status == 'discover' || status == 'error';
},
areInterfacesConfigurable: function() {
var status = this.get('status');
return status == 'discover' || status == 'error' || status == 'provisioned';
},
getRolesSummary: function(releaseRoles) {
return _.map(this.sortedRoles(releaseRoles.pluck('name')), function(role) {

View File

@ -47,6 +47,13 @@ define([
return option.split(':');
}));
},
getNodeListFromTabOptions: function(options) {
var nodeIds = utils.deserializeTabOptions(options.screenOptions[0]).nodes,
ids = nodeIds ? nodeIds.split(',').map((id) => parseInt(id, 10)) : [],
models = require('models'),
nodes = new models.Nodes(options.cluster.get('nodes').getByIds(ids));
if (nodes.length == ids.length) return nodes;
},
linebreaks: function(text) {
return text.replace(/\n/g, '<br/>');
},

View File

@ -30,7 +30,6 @@ function($, _, i18n, Backbone, React, utils, models, ComponentMixins, controls)
var EditNodeDisksScreen = React.createClass({
mixins: [
ComponentMixins.nodeConfigurationScreenMixin,
ComponentMixins.backboneMixin('cluster', 'change:status change:nodes sync'),
ComponentMixins.backboneMixin('nodes', 'change sync'),
ComponentMixins.backboneMixin('disks', 'reset change'),
@ -38,7 +37,7 @@ function($, _, i18n, Backbone, React, utils, models, ComponentMixins, controls)
],
statics: {
fetchData: function(options) {
var nodes = ComponentMixins.nodeConfigurationScreenMixin.getNodeList(options);
var nodes = utils.getNodeListFromTabOptions(options);
if (!nodes || !nodes.areDisksConfigurable()) {
return $.Deferred().reject();
@ -66,6 +65,10 @@ function($, _, i18n, Backbone, React, utils, models, ComponentMixins, controls)
componentWillMount: function() {
this.updateInitialData();
},
isLocked: function() {
return !!this.props.cluster.task({group: 'deployment', active: true}) ||
!_.all(this.props.nodes.invoke('areDisksConfigurable'));
},
updateInitialData: function() {
this.setState({initialDisks: _.cloneDeep(this.props.nodes.at(0).disks.toJSON())});
},
@ -192,7 +195,9 @@ function($, _, i18n, Backbone, React, utils, models, ComponentMixins, controls)
<div className='col-xs-12 page-buttons content-elements'>
<div className='well clearfix'>
<div className='btn-group'>
{this.renderBackToNodeListButton()}
<a className='btn btn-default' href={'#cluster/' + this.props.cluster.id + '/nodes'} disabled={this.state.actionInProgress}>
{i18n('cluster_page.nodes_tab.back_to_nodes_button')}
</a>
</div>
{!locked && !!this.props.disks.length &&
<div className='btn-group pull-right'>

View File

@ -36,7 +36,6 @@ function($, _, Backbone, React, i18n, utils, models, dispatcher, dialogs, contro
var EditNodeInterfacesScreen = React.createClass({
mixins: [
ComponentMixins.nodeConfigurationScreenMixin,
ComponentMixins.backboneMixin('interfaces', 'change reset update'),
ComponentMixins.backboneMixin('cluster'),
ComponentMixins.backboneMixin('nodes', 'change reset update'),
@ -45,7 +44,7 @@ function($, _, Backbone, React, i18n, utils, models, dispatcher, dialogs, contro
statics: {
fetchData: function(options) {
var cluster = options.cluster,
nodes = ComponentMixins.nodeConfigurationScreenMixin.getNodeList(options);
nodes = utils.getNodeListFromTabOptions(options);
if (!nodes || !nodes.areInterfacesConfigurable()) {
return $.Deferred().reject();
@ -94,6 +93,10 @@ function($, _, Backbone, React, i18n, utils, models, dispatcher, dialogs, contro
componentDidMount: function() {
this.validate();
},
isLocked: function() {
return !!this.props.cluster.task({group: 'deployment', active: true}) ||
!_.all(this.props.nodes.invoke('areInterfacesConfigurable'));
},
initInterfacesMap: function() {
this.setState({interfacesMap: _.range(0, this.props.interfaces.length)});
},
@ -522,7 +525,9 @@ function($, _, Backbone, React, i18n, utils, models, dispatcher, dialogs, contro
<div className='col-xs-12 page-buttons content-elements'>
<div className='well clearfix'>
<div className='btn-group'>
{this.renderBackToNodeListButton()}
<a className='btn btn-default' href={'#cluster/' + this.props.cluster.id + '/nodes'} disabled={this.state.actionInProgress}>
{i18n('cluster_page.nodes_tab.back_to_nodes_button')}
</a>
</div>
{!locked &&
<div className='btn-group pull-right'>

View File

@ -20,17 +20,16 @@ define(
'react',
'models',
'utils',
'component_mixins',
'views/cluster_page_tabs/nodes_tab_screens/node_list_screen'
],
function($, _, React, models, utils, ComponentMixins, NodeListScreen) {
function($, _, React, models, utils, NodeListScreen) {
'use strict';
var EditNodesScreen = React.createClass({
statics: {
fetchData: function(options) {
var cluster = options.cluster,
nodes = ComponentMixins.nodeConfigurationScreenMixin.getNodeList(options);
nodes = utils.getNodeListFromTabOptions(options);
if (!nodes) {
return $.Deferred().reject();

View File

@ -910,8 +910,7 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, controls, dialo
render: function() {
var ns = 'cluster_page.nodes_tab.node_management_panel.';
var configurationAvailable = _.all(this.props.nodes.invoke('isConfigurable')),
disksConflict, interfaceConflict;
var disksConflict, interfaceConflict;
if (this.props.mode == 'list' && this.props.nodes.length) {
disksConflict = !this.props.nodes.areDisksConfigurable();
interfaceConflict = !this.props.nodes.areInterfacesConfigurable();
@ -1069,7 +1068,7 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, controls, dialo
onClick={_.bind(this.goToConfigurationScreen, this, 'disks', disksConflict)}
>
{disksConflict && <i className='glyphicon glyphicon-danger-sign' />}
{i18n('dialog.show_node.disk_configuration' + (configurationAvailable ? '_action' : ''))}
{i18n('dialog.show_node.disk_configuration' + (_.all(this.props.nodes.invoke('areDisksConfigurable')) ? '_action' : ''))}
</button>
<button
className='btn btn-default btn-configure-interfaces'
@ -1077,7 +1076,7 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, controls, dialo
onClick={_.bind(this.goToConfigurationScreen, this, 'interfaces', interfaceConflict)}
>
{interfaceConflict && <i className='glyphicon glyphicon-danger-sign' />}
{i18n('dialog.show_node.network_configuration' + (configurationAvailable ? '_action' : ''))}
{i18n('dialog.show_node.network_configuration' + (_.all(this.props.nodes.invoke('areInterfacesConfigurable')) ? '_action' : ''))}
</button>
</div>,
<div className='btn-group' role='group' key='role-management-buttons'>

View File

@ -768,16 +768,15 @@ function($, _, i18n, Backbone, React, utils, models, dispatcher, controls, compo
);
},
renderFooter: function() {
var isConfigurable = this.props.node.isConfigurable();
return (
<div>
{this.props.renderActionButtons && this.props.node.get('cluster') &&
<div className='btn-group' role='group'>
<button className='btn btn-default btn-edit-disks' onClick={_.partial(this.goToConfigurationScreen, 'disks')}>
{i18n('dialog.show_node.disk_configuration' + (isConfigurable ? '_action' : ''))}
{i18n('dialog.show_node.disk_configuration' + (this.props.node.areDisksConfigurable() ? '_action' : ''))}
</button>
<button className='btn btn-default btn-edit-networks' onClick={_.partial(this.goToConfigurationScreen, 'interfaces')}>
{i18n('dialog.show_node.network_configuration' + (isConfigurable ? '_action' : ''))}
{i18n('dialog.show_node.network_configuration' + (this.props.node.areInterfacesConfigurable() ? '_action' : ''))}
</button>
</div>
}