730 lines
34 KiB
JavaScript
730 lines
34 KiB
JavaScript
/*
|
|
* Copyright 2013 Mirantis, Inc.
|
|
*
|
|
* Licensed 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.
|
|
**/
|
|
define(
|
|
[
|
|
'require',
|
|
'utils',
|
|
'models',
|
|
'view_mixins',
|
|
'views/dialogs',
|
|
'text!templates/dialogs/create_cluster_wizard.html',
|
|
'text!templates/dialogs/create_cluster_wizard/name_and_release.html',
|
|
'text!templates/dialogs/create_cluster_wizard/common_wizard_panel.html',
|
|
'text!templates/dialogs/create_cluster_wizard/mode.html',
|
|
'text!templates/dialogs/create_cluster_wizard/storage.html',
|
|
'text!templates/dialogs/create_cluster_wizard/ready.html',
|
|
'text!templates/dialogs/create_cluster_wizard/control_template.html',
|
|
'text!templates/dialogs/create_cluster_wizard/warning.html',
|
|
'text!templates/dialogs/create_cluster_wizard/text_input.html'
|
|
],
|
|
function(require, utils, models, viewMixins, dialogs, createClusterWizardTemplate, clusterNameAndReleasePaneTemplate, commonWizardTemplate, modePaneTemplate, storagePaneTemplate, clusterReadyPaneTemplate, controlTemplate, warningTemplate, textInputTemplate) {
|
|
'use strict';
|
|
|
|
var views = {};
|
|
|
|
var clusterWizardPanes = {};
|
|
|
|
views.CreateClusterWizard = dialogs.Dialog.extend({
|
|
className: 'modal fade create-cluster-modal',
|
|
template: _.template(createClusterWizardTemplate),
|
|
modalOptions: {backdrop: 'static'},
|
|
events: {
|
|
'keydown': 'onInputKeydown',
|
|
'click .next-pane-btn': 'nextPane',
|
|
'click .prev-pane-btn': 'prevPane',
|
|
'click .wizard-step.available': 'onStepClick',
|
|
'click .finish-btn': 'createCluster'
|
|
},
|
|
composeStickitBindings: function() {
|
|
var bindings = {};
|
|
_.each(this.panesConstructors, function(PaneConstructor, paneIndex) {
|
|
bindings['.wizard-step[data-pane=' + paneIndex + ']'] = {
|
|
attributes: [{
|
|
name: 'class',
|
|
observe: PaneConstructor.prototype.constructorName
|
|
}]
|
|
};
|
|
}, this);
|
|
bindings['.prev-pane-btn'] = {
|
|
attributes: [{
|
|
name: 'disabled',
|
|
observe: 'activePaneIndex',
|
|
onGet: function(value) {
|
|
return value == 0;
|
|
}
|
|
}]
|
|
};
|
|
bindings['.next-pane-btn'] = {
|
|
observe: 'activePaneIndex',
|
|
visible: function(value) {
|
|
return value != this.panesConstructors.length - 1;
|
|
},
|
|
attributes: [{
|
|
name: 'disabled',
|
|
observe: 'invalid'
|
|
}]
|
|
};
|
|
bindings['.finish-btn'] = {
|
|
observe: 'activePaneIndex',
|
|
visible: function(value) {
|
|
return value == this.panesConstructors.length - 1;
|
|
}
|
|
};
|
|
this.stickit(this.panesModel, bindings);
|
|
},
|
|
initialize: function(options) {
|
|
_.defaults(this, options);
|
|
this.config = {
|
|
"NameAndRelease": {
|
|
"name": {
|
|
"type": "custom",
|
|
"value": "",
|
|
"bind": "cluster:name"
|
|
},
|
|
"release" : {
|
|
"type": "custom",
|
|
"bind": {"id": "cluster:release"},
|
|
"aliases": {
|
|
"operating_system": "NameAndRelease.release_operating_system",
|
|
"roles": "NameAndRelease.release_roles",
|
|
"name": "NameAndRelease.release_name"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
this.panesModel = new Backbone.Model({
|
|
activePaneIndex: 0,
|
|
maxAvailablePaneIndex: 0
|
|
});
|
|
this.updatePanesStatuses();
|
|
this.settings = new models.Settings();
|
|
this.panesModel.on('change:activePaneIndex', this.handlePaneIndexChange, this);
|
|
this.panesModel.on('change:maxAvailablePaneIndex', function() {
|
|
this.updatePanesStatuses();
|
|
//FIXME: this should be moved to view method
|
|
this.$('.wizard-footer .btn-success:visible').focus();
|
|
}, this);
|
|
this.model = new models.WizardModel(this.config);
|
|
this.model.processConfig(this.config);
|
|
this.processRestrictions();
|
|
this.attachModelListeners();
|
|
},
|
|
attachModelListeners: function() {
|
|
_.each(this.restrictions, function(paneConfig) {
|
|
_.each(paneConfig, function(paneRestrictions) {
|
|
_.each(paneRestrictions, function(restriction) {
|
|
var evaluatedExpression = utils.evaluateExpression(restriction.condition, {default: this.model}, {strict: false});
|
|
_.invoke(evaluatedExpression.modelPaths, 'change', _.bind(this.handleTrackedAttributeChange, this));
|
|
}, this);
|
|
}, this);
|
|
}, this);
|
|
},
|
|
handleTrackedAttributeChange: function() {
|
|
var maxIndex = this.panesModel.get('maxAvailablePaneIndex');
|
|
var currentIndex = this.panesModel.get('activePaneIndex');
|
|
if (maxIndex > currentIndex) {
|
|
this.panesModel.set('maxAvailablePaneIndex', currentIndex);
|
|
var listOfPanesToRestoreDefaults = this.getListOfPanesToRestore(currentIndex, maxIndex);
|
|
this.model.restoreDefaultValues(listOfPanesToRestoreDefaults);
|
|
}
|
|
},
|
|
processRestrictions: function() {
|
|
var restrictions = this.restrictions = {};
|
|
function processControlRestrictions(config, paneName, attribute) {
|
|
var expandedRestrictions = config.restrictions = _.map(config.restrictions, utils.expandRestriction);
|
|
restrictions[paneName][attribute] =
|
|
_.uniq(_.union(restrictions[paneName][attribute], expandedRestrictions), 'message');
|
|
}
|
|
_.each(this.config, function(paneConfig, paneName) {
|
|
restrictions[paneName] = {};
|
|
_.each(paneConfig, function(attributeConfig, attribute) {
|
|
if (attributeConfig.type == 'radio') {
|
|
_.each(attributeConfig.values, function(attributeValueConfig) {
|
|
processControlRestrictions(attributeValueConfig, paneName, attribute);
|
|
}, this);
|
|
} else {
|
|
processControlRestrictions(attributeConfig, paneName, attribute);
|
|
}
|
|
}, this);
|
|
}, this);
|
|
},
|
|
handlePaneIndexChange: function() {
|
|
this.renderPane(this.panesConstructors[this.panesModel.get('activePaneIndex')]);
|
|
},
|
|
beforeClusterCreation: function() {
|
|
var success = this.processBinds('cluster');
|
|
return $.Deferred()[success ? 'resolve' : 'reject']();
|
|
},
|
|
beforeSettingsSaving: function(settings) {
|
|
var success = this.processBinds('settings');
|
|
return $.Deferred()[success ? 'resolve' : 'reject']();
|
|
},
|
|
processBinds: function(prefix) {
|
|
var result = true;
|
|
var configModels = {settings: this.settings, cluster: this.cluster};
|
|
function processBind(path, value) {
|
|
if (path.slice(0, prefix.length) == prefix) {
|
|
utils.parseModelPath(path, configModels).set(value);
|
|
}
|
|
}
|
|
_.each(this.config, function(paneConfig, paneName) {
|
|
_.each(paneConfig, function(attributeConfig, attribute) {
|
|
var bind = attributeConfig.bind;
|
|
var value = this.model.get(paneName + '.' + attribute);
|
|
if (_.isString(bind)) {
|
|
// simple binding declaration - just copy the value
|
|
processBind(bind, value);
|
|
} else if (_.isPlainObject(bind)) {
|
|
// binding declaration for models
|
|
processBind(_.values(bind)[0], value.get(_.keys(bind)[0]));
|
|
}
|
|
if (attributeConfig.type == 'radio') {
|
|
// radiobuttons can have values with their own bindings
|
|
_.each(_.find(attributeConfig.values, {data: value}).bind, function(bind) {
|
|
processBind(_.keys(bind)[0], _.values(bind)[0]);
|
|
});
|
|
}
|
|
}, this);
|
|
}, this);
|
|
return result;
|
|
},
|
|
onInputKeydown: function(e) {
|
|
if (e.which == 13) {
|
|
e.preventDefault();
|
|
this.nextPane();
|
|
}
|
|
},
|
|
goToPane: function(index) {
|
|
this.panesModel.set({
|
|
invalid: false,
|
|
activePaneIndex: index,
|
|
maxAvailablePaneIndex: _.max([this.panesModel.get('maxAvailablePaneIndex'), this.panesModel.get('activePaneIndex'), index])
|
|
});
|
|
},
|
|
prevPane: function() {
|
|
this.goToPane(this.panesModel.get('activePaneIndex') - 1);
|
|
},
|
|
nextPane: function() {
|
|
this.activePane.processPaneData().done(_.bind(function() {
|
|
this.goToPane(this.panesModel.get('activePaneIndex') + 1);
|
|
}, this));
|
|
},
|
|
onStepClick: function(e) {
|
|
var paneIndex = parseInt($(e.currentTarget).data('pane'), 10);
|
|
if (this.panesModel.get('activePaneIndex') > this.panesConstructors.length - 1) {
|
|
this.activePane.processPaneData().done(_.bind(function() {
|
|
this.goToPane(paneIndex);
|
|
}, this));
|
|
}
|
|
else {
|
|
this.goToPane(paneIndex);
|
|
}
|
|
},
|
|
getListOfPanesToRestore: function(currentIndex, maxIndex) {
|
|
var panesNames = [];
|
|
_.each(this.panesConstructors, function(PaneConstructor, paneIndex) {
|
|
if ((paneIndex <= maxIndex) && (paneIndex > currentIndex)) {
|
|
panesNames.push(PaneConstructor.prototype.constructorName);
|
|
}
|
|
}, this);
|
|
return panesNames;
|
|
},
|
|
updatePanesStatuses: function() {
|
|
_.each(this.panesConstructors, function(PaneConstructor, paneIndex) {
|
|
var paneName = PaneConstructor.prototype.constructorName;
|
|
if (paneIndex == this.panesModel.get('activePaneIndex')) {
|
|
this.panesModel.set(paneName, 'current');
|
|
} else if (paneIndex <= this.panesModel.get('maxAvailablePaneIndex')) {
|
|
this.panesModel.set(paneName, 'available');
|
|
} else {
|
|
this.panesModel.set(paneName, 'unavailable');
|
|
}
|
|
}, this);
|
|
},
|
|
renderPane: function(Pane) {
|
|
this.tearDownRegisteredSubViews();
|
|
var pane = this.activePane = new Pane({
|
|
wizard: this,
|
|
config: this.config[Pane.prototype.constructorName]
|
|
});
|
|
this.registerSubView(pane);
|
|
this.updatePanesStatuses();
|
|
this.$('.pane-content').html('').append(pane.render().el);
|
|
},
|
|
createCluster: function() {
|
|
var cluster = this.cluster;
|
|
this.beforeClusterCreation();
|
|
var deferred = cluster.save();
|
|
if (deferred) {
|
|
this.$('.wizard-footer button').prop('disabled', true);
|
|
deferred
|
|
.done(_.bind(function() {
|
|
this.collection.add(cluster);
|
|
this.settings.url = _.result(cluster, 'url') + '/attributes';
|
|
this.settings.fetch()
|
|
.then(_.bind(this.beforeSettingsSaving, this))
|
|
.then(_.bind(function() {
|
|
return this.settings.save(this.settings.attributes, {validate: false});
|
|
}, this))
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
}, this))
|
|
.fail(_.bind(function() {
|
|
this.displayError({message: $.t('dialog.create_cluster_wizard.configuration_failed_warning')});
|
|
}, this));
|
|
}, this))
|
|
.fail(_.bind(function(response) {
|
|
if (response.status == 409) {
|
|
this.$('.wizard-footer button').prop('disabled', false);
|
|
this.panesModel.set('activePaneIndex', 0);
|
|
cluster.trigger('invalid', cluster, {name: response.responseText});
|
|
} else {
|
|
var options = {
|
|
title: $.t('dialog.create_cluster_wizard.create_cluster_error.title'),
|
|
message: response.status == 400 ? response.responseText : undefined
|
|
};
|
|
this.displayError(options);
|
|
}
|
|
}, this));
|
|
}
|
|
},
|
|
render: function() {
|
|
var panesTitles = _.map(this.panesConstructors, function(PaneConstructor) {
|
|
return PaneConstructor.prototype.title;
|
|
});
|
|
this.constructor.__super__.render.call(this, _.extend({
|
|
panesTitles: panesTitles,
|
|
currentStep: this.panesModel.get('activePaneIndex'),
|
|
maxAvailableStep: this.panesModel.get('maxAvailablePaneIndex')
|
|
}));
|
|
this.$('.wizard-footer .btn-success:visible').focus();
|
|
this.renderPane(this.panesConstructors[this.panesModel.get('activePaneIndex')]);
|
|
this.composeStickitBindings();
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.WizardPane = Backbone.View.extend({
|
|
template: _.template(commonWizardTemplate),
|
|
constructorName: 'WizardPane',
|
|
mixins: [viewMixins.toggleablePassword],
|
|
initialize: function(options) {
|
|
_.defaults(this, options);
|
|
this.attachWarningListeners();
|
|
this.processPaneAliases();
|
|
this.wizard.model.on('invalid', function(model, errors) {
|
|
_.each(errors, function(error) {
|
|
var input = this.$('input[name="' + error.field + '"]');
|
|
input.addClass('error');
|
|
input.parent().siblings('.description').addClass('hide');
|
|
input.parent().siblings('.validation-error').text(error.message).removeClass('hide');
|
|
this.wizard.panesModel.set('invalid', true);
|
|
}, this);
|
|
}, this);
|
|
},
|
|
renderControls: function(config) {
|
|
var controlsHtml = '';
|
|
var configToUse = _.defaults(config, {
|
|
labelClasses: '',
|
|
descriptionClasses: '',
|
|
description: ''
|
|
});
|
|
var controlTpl = _.template(controlTemplate);
|
|
var sortedConfig = _.sortBy(_.pairs(this.config), function(configEntry) {
|
|
return configEntry[1].weight;
|
|
});
|
|
_.each(sortedConfig, function(configEntry) {
|
|
var attribute = configEntry[0];
|
|
var attributeConfig = configEntry[1];
|
|
switch (attributeConfig.type) {
|
|
case 'checkbox':
|
|
controlsHtml += (controlTpl(_.extend(attributeConfig, {
|
|
pane: attribute,
|
|
labelClasses: configToUse.labelClasses,
|
|
descriptionClasses: configToUse.descriptionClasses,
|
|
label: attributeConfig.label,
|
|
hasDescription: _.isUndefined(configToUse.hasDescription) ? false : configToUse.hasDescription ,
|
|
description: attributeConfig.description
|
|
})));
|
|
break;
|
|
case 'radio':
|
|
_.each(attributeConfig.values, function(value) {
|
|
var shouldBeAdded = _.isUndefined(configToUse.additionalAttribute) ? true : attribute == configToUse.additionalAttribute;
|
|
if (shouldBeAdded) {
|
|
controlsHtml += (controlTpl(_.extend(attributeConfig, {
|
|
value: value.data,
|
|
pane: attribute,
|
|
labelClasses: configToUse.labelClasses || '',
|
|
descriptionClasses: configToUse.descriptionClasses || '',
|
|
label: value.label,
|
|
hasDescription: _.isUndefined(configToUse.hasDescription) ? false : configToUse.hasDescription,
|
|
description: value.description || ''
|
|
})));
|
|
}
|
|
}, this);
|
|
break;
|
|
case 'text':
|
|
case 'password':
|
|
var newControlTemplate = _.template(textInputTemplate);
|
|
var newControlsHtml = '';
|
|
newControlsHtml = (newControlTemplate(_.extend(attributeConfig, {attribute: attribute})));
|
|
controlsHtml += newControlsHtml;
|
|
break;
|
|
}
|
|
}, this);
|
|
return controlsHtml;
|
|
},
|
|
attachWarningListeners: function() {
|
|
var attributesToObserve = [];
|
|
_.each(this.wizard.restrictions[this.constructorName], function(paneConfig) {
|
|
_.each(paneConfig, function(paneRestriction) {
|
|
var evaluatedExpression = utils.evaluateExpression(paneRestriction.condition, {default: this.wizard.model}, {strict: false});
|
|
_.each(evaluatedExpression.modelPaths, function(modelPath) {
|
|
attributesToObserve.push(modelPath.attribute);
|
|
}, this);
|
|
}, this);
|
|
}, this);
|
|
_.each(_.uniq(attributesToObserve), function(condition) {
|
|
this.wizard.model.on('change:' + condition, this.handleWarnings, this);
|
|
}, this);
|
|
},
|
|
composePaneBindings: function() {
|
|
this.bindings = {};
|
|
_.each(this.config, function(attributeConfig, attribute) {
|
|
this.bindings['[name=' + attribute + ']'] = {observe: this.constructorName + '.' + attribute};
|
|
switch (attributeConfig.type) {
|
|
case 'radio':
|
|
_.each(attributeConfig.values, function(value) {
|
|
this.createRestrictionBindings(value.restrictions, {name: attribute, value: value.data});
|
|
}, this);
|
|
break;
|
|
case 'checkbox':
|
|
this.createRestrictionBindings(attributeConfig.restrictions, {name: attribute});
|
|
break;
|
|
case 'text':
|
|
case 'password':
|
|
this.createRestrictionBindings(attributeConfig.restrictions, {'data-attribute': attribute});
|
|
break;
|
|
}
|
|
}, this);
|
|
this.stickit(this.wizard.model);
|
|
},
|
|
createRestrictionBindings: function(controlRestrictions, selectorOptions) {
|
|
_.each(_.groupBy(controlRestrictions, 'action'), function(restrictions, action) {
|
|
var conditions = _.pluck(restrictions, 'condition');
|
|
var attributesToObserve = _.uniq(_.flatten(_.map(conditions, function(condition) {
|
|
return _.keys(utils.evaluateExpression(condition, {default: this.wizard.model}).modelPaths);
|
|
}, this)));
|
|
var selector = _.map(selectorOptions, function(value, key) {
|
|
return '[' + key + '=' + value + ']';
|
|
}, this).join('');
|
|
switch (action) {
|
|
case 'disable':
|
|
this.bindings[selector] = _.extend(this.bindings[selector] || {}, {
|
|
attributes: [{
|
|
name: 'disabled',
|
|
observe: attributesToObserve,
|
|
onGet: function() {
|
|
return _.any(conditions, function(condition) {
|
|
return utils.evaluateExpression(condition, {default: this.wizard.model}).value;
|
|
}, this);
|
|
}
|
|
}]
|
|
});
|
|
break;
|
|
case 'hide':
|
|
this.bindings[selector] = _.extend(this.bindings[selector] || {}, {
|
|
observe: attributesToObserve,
|
|
visible: function() {
|
|
return !_.any(conditions, function(condition) {
|
|
return utils.evaluateExpression(condition, {default: this.wizard.model}).value;
|
|
}, this);
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
}, this);
|
|
},
|
|
processPaneAliases: function() {
|
|
_.each(this.config, function(attributeConfig, attribute) {
|
|
_.each(attributeConfig.aliases, function(wizardModelAttribute, modelAttribute) {
|
|
this.wizard.model.on('change:' + this.constructorName + '.' + attribute, function(wizardModel, model) {
|
|
wizardModel.set(wizardModelAttribute, model.get(modelAttribute));
|
|
}, this);
|
|
}, this);
|
|
}, this);
|
|
},
|
|
processPaneData: function() {
|
|
if (this.wizard.model.isValid({
|
|
config: this.config,
|
|
paneName: this.constructorName
|
|
})) {
|
|
return $.Deferred().resolve();
|
|
}
|
|
return $.Deferred().reject();
|
|
},
|
|
buildTranslationParams: function() {
|
|
var result = {};
|
|
_.each(this.wizard.model.attributes, function(paneConfig, paneName) {
|
|
_.each(paneConfig, function(value, attribute) {
|
|
if (!_.isObject(value)) {
|
|
var attributeConfig = this.wizard.config[paneName][attribute];
|
|
if (attributeConfig && attributeConfig.type == 'radio') {
|
|
result[paneName + '.' + attribute] = $.t(_.find(attributeConfig.values, {data: value}).label);
|
|
} else if (attributeConfig && attributeConfig.label) {
|
|
result[paneName + '.' + attribute] = $.t(attributeConfig.label);
|
|
} else {
|
|
result[paneName + '.' + attribute] = value;
|
|
}
|
|
}
|
|
}, this);
|
|
}, this);
|
|
return result;
|
|
},
|
|
handleWarnings: function() {
|
|
this.$('.alert').remove();
|
|
var messages = [];
|
|
_.each(this.wizard.restrictions[this.constructorName], function (paneConfig) {
|
|
_.each(paneConfig, function(paneRestriction) {
|
|
var result = utils.evaluateExpression(paneRestriction.condition, {default: this.wizard.model}).value;
|
|
if (result) {
|
|
messages.push(paneRestriction.message);
|
|
}
|
|
}, this);
|
|
}, this);
|
|
if (messages.length) {
|
|
var translationParams = this.buildTranslationParams();
|
|
_.each(_.compact(_.uniq(messages)), function(message) {
|
|
this.showWarning($.t(message, translationParams));
|
|
}, this);
|
|
}
|
|
},
|
|
showWarning: function(message) {
|
|
this.$('.form-horizontal').before(_.template(warningTemplate, {message: message}));
|
|
},
|
|
renderCustomElements: function() {
|
|
this.$('.control-group').append(this.renderControls({}));
|
|
},
|
|
onWizardChange: function(model, changes) {
|
|
this.$('input.error').removeClass('error');
|
|
this.$('.parameter-description').removeClass('hide');
|
|
this.$('.validation-error').addClass('hide');
|
|
this.wizard.panesModel.set('invalid', false);
|
|
if (this.$('input[type=text], input[type=password]').is(':focus')) {
|
|
this.wizard.model.isValid({
|
|
config: this.config,
|
|
paneName: this.constructorName
|
|
});
|
|
}
|
|
},
|
|
render: function() {
|
|
this.$el.html(this.template());
|
|
this.renderCustomElements();
|
|
this.$el.i18n();
|
|
this.handleWarnings();
|
|
this.composePaneBindings();
|
|
return this;
|
|
}
|
|
});
|
|
|
|
clusterWizardPanes.NameAndRelease = views.WizardPane.extend({
|
|
constructorName: 'NameAndRelease',
|
|
title:'dialog.create_cluster_wizard.name_release.title',
|
|
template: _.template(clusterNameAndReleasePaneTemplate),
|
|
events: {
|
|
'keydown input': 'onInputKeydown'
|
|
},
|
|
processPaneData: function() {
|
|
var success = this.createCluster();
|
|
return $.Deferred()[success ? 'resolve' : 'reject']();
|
|
},
|
|
createCluster: function() {
|
|
this.$('.control-group').removeClass('error').find('.help-inline').text('');
|
|
var success = true;
|
|
var name = this.wizard.model.get('NameAndRelease.name');
|
|
var release = this.wizard.model.get('NameAndRelease.release').id;
|
|
this.wizard.cluster = new models.Cluster();
|
|
this.wizard.cluster.on('invalid', function(model, error) {
|
|
success = false;
|
|
_.each(error, function(message, field) {
|
|
this.$('*[name=' + field + ']').closest('.control-group').addClass('error').find('.help-inline').text(message);
|
|
}, this);
|
|
this.wizard.panesModel.set('invalid', true);
|
|
}, this);
|
|
if (this.wizard.collection.findWhere({name: name})) {
|
|
this.wizard.cluster.trigger('invalid', this.wizard.cluster, {name: $.t('dialog.create_cluster_wizard.name_release.existing_environment', {name: name})});
|
|
}
|
|
success = success && this.wizard.cluster.set({
|
|
name: name,
|
|
release: release
|
|
}, {validate: true});
|
|
return success;
|
|
},
|
|
onInputKeydown: function() {
|
|
this.$('.control-group.error').removeClass('error');
|
|
this.$('.help-inline').html('');
|
|
this.wizard.panesModel.set('invalid', false);
|
|
},
|
|
composePaneBindings: function() {
|
|
this.constructor.__super__.composePaneBindings.apply(this, arguments);
|
|
this.bindings['[name=release]'].selectOptions = {
|
|
collection: function() {
|
|
return this.releases.map(function(release) {
|
|
return {
|
|
value: release,
|
|
label: release.get('name') + ' (' + release.get('version') + ')'
|
|
};
|
|
});
|
|
}
|
|
};
|
|
this.stickit(this.wizard.model);
|
|
},
|
|
initialize: function(options) {
|
|
this.constructor.__super__.initialize.apply(this, arguments);
|
|
this.releases = this.wizard.releases || new models.Releases();
|
|
if (!this.releases.length) {
|
|
this.wizard.releases = this.releases;
|
|
this.releases.fetch();
|
|
}
|
|
this.releases.on('sync', this.render, this);
|
|
this.wizard.model.on('change:NameAndRelease.release', this.updateRelease, this);
|
|
},
|
|
updateConfig: function(config) {
|
|
var name = this.wizard.model.get('NameAndRelease.name');
|
|
var release = this.wizard.model.get('NameAndRelease.release');
|
|
_.extend(this.wizard.config, _.cloneDeep(config));
|
|
this.wizard.model.off(null, null, this);
|
|
this.wizard.model.initialize(this.wizard.config);
|
|
this.wizard.model.processConfig(this.wizard.config);
|
|
this.wizard.model.set({
|
|
'NameAndRelease.name': name,
|
|
'NameAndRelease.release': release
|
|
});
|
|
this.wizard.panesModel.set({
|
|
activePaneIndex: 0,
|
|
maxAvailablePaneIndex: 0
|
|
});
|
|
this.wizard.processRestrictions();
|
|
this.wizard.attachModelListeners();
|
|
this.wizard.renderPane(this.constructor);
|
|
},
|
|
updateRelease: function() {
|
|
var release = this.wizard.model.get('NameAndRelease.release');
|
|
this.updateConfig(release.get('wizard_metadata'));
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this);
|
|
if (this.releases.length) {
|
|
var release = this.wizard.model.get('NameAndRelease.release');
|
|
if (!release) {
|
|
this.wizard.model.set('NameAndRelease.release', this.releases.first());
|
|
} else {
|
|
this.$('.release-description').text(release.get('description'));
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
});
|
|
|
|
clusterWizardPanes.Mode = views.WizardPane.extend({
|
|
constructorName: 'Mode',
|
|
template: _.template(modePaneTemplate),
|
|
title: 'dialog.create_cluster_wizard.mode.title',
|
|
initialize: function(options) {
|
|
this.constructor.__super__.initialize.call(this, options);
|
|
this.wizard.model.on('change:Mode.mode', this.updateModeDescription, this);
|
|
},
|
|
updateModeDescription: function() {
|
|
var description = this.wizard.model.get('NameAndRelease.release').get('modes_metadata')[this.wizard.model.get('Mode.mode')].description;
|
|
this.$('.mode-description').text(description);
|
|
},
|
|
renderCustomElements: function() {
|
|
this.$('.mode-control-group .span5').append(this.renderControls({labelClasses: 'setting', descriptionClasses:'openstack-sub-title'})).i18n();
|
|
this.updateModeDescription();
|
|
}
|
|
});
|
|
|
|
clusterWizardPanes.Compute = views.WizardPane.extend({
|
|
constructorName: 'Compute',
|
|
title: 'dialog.create_cluster_wizard.compute.title',
|
|
initialize: function(options) {
|
|
this.constructor.__super__.initialize.call(this, options);
|
|
this.wizard.model.on('change:Compute.*', this.onWizardChange, this);
|
|
this.events = _.extend(this.events, {
|
|
'focus input': 'onWizardChange'
|
|
});
|
|
},
|
|
renderCustomElements: function() {
|
|
this.$('.control-group').append(this.renderControls({hasDescription: true})).i18n();
|
|
}
|
|
});
|
|
|
|
clusterWizardPanes.Network = views.WizardPane.extend({
|
|
constructorName: 'Network',
|
|
template: _.template(commonWizardTemplate),
|
|
title: 'dialog.create_cluster_wizard.network.title'
|
|
});
|
|
|
|
clusterWizardPanes.Storage = views.WizardPane.extend({
|
|
constructorName: 'Storage',
|
|
template: _.template(storagePaneTemplate),
|
|
title: 'dialog.create_cluster_wizard.storage.title',
|
|
renderCustomElements: function() {
|
|
this.$('.control-group .cinder h5').after(this.renderControls({
|
|
hasDescription: false,
|
|
additionalAttribute: 'cinder'
|
|
}));
|
|
this.$('.control-group .glance h5').after(this.renderControls({
|
|
hasDescription: false,
|
|
additionalAttribute: 'glance'
|
|
}));
|
|
}
|
|
});
|
|
|
|
clusterWizardPanes.AdditionalServices = views.WizardPane.extend({
|
|
constructorName: 'AdditionalServices',
|
|
title: 'dialog.create_cluster_wizard.additional.title',
|
|
renderCustomElements: function() {
|
|
this.$('.control-group').append(this.renderControls({
|
|
hasDescription: true,
|
|
additionalAttribute: 'services'
|
|
})).i18n();
|
|
}
|
|
});
|
|
|
|
clusterWizardPanes.Ready = views.WizardPane.extend({
|
|
constructorName: 'Ready',
|
|
title: 'dialog.create_cluster_wizard.ready.title',
|
|
template: _.template(clusterReadyPaneTemplate),
|
|
processPaneData: function() {
|
|
var success = this.wizard.createCluster();
|
|
return $.Deferred()[success ? 'resolve' : 'reject']();
|
|
}
|
|
});
|
|
|
|
views.CreateClusterWizard.prototype.panesConstructors = [
|
|
clusterWizardPanes.NameAndRelease,
|
|
clusterWizardPanes.Mode,
|
|
clusterWizardPanes.Compute,
|
|
clusterWizardPanes.Network,
|
|
clusterWizardPanes.Storage,
|
|
clusterWizardPanes.AdditionalServices,
|
|
clusterWizardPanes.Ready
|
|
];
|
|
|
|
return views;
|
|
});
|