457 lines
21 KiB
JavaScript
457 lines
21 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',
|
|
'text!templates/dialogs/base_dialog.html',
|
|
'text!templates/dialogs/discard_changes.html',
|
|
'text!templates/dialogs/display_changes.html',
|
|
'text!templates/dialogs/remove_cluster.html',
|
|
'text!templates/dialogs/stop_deployment.html',
|
|
'text!templates/dialogs/reset_environment.html',
|
|
'text!templates/dialogs/update_environment.html',
|
|
'text!templates/dialogs/show_node.html',
|
|
'text!templates/dialogs/dismiss_settings.html',
|
|
'text!templates/dialogs/delete_nodes.html',
|
|
'text!templates/dialogs/change_password.html'
|
|
],
|
|
function(require, utils, models, viewMixins, baseDialogTemplate, discardChangesDialogTemplate, displayChangesDialogTemplate, removeClusterDialogTemplate, stopDeploymentDialogTemplate, resetEnvironmentDialogTemplate, updateEnvironmentDialogTemplate, showNodeInfoTemplate, discardSettingsChangesTemplate, deleteNodesTemplate, changePasswordTemplate) {
|
|
'use strict';
|
|
|
|
var views = {};
|
|
|
|
views.Dialog = Backbone.View.extend({
|
|
className: 'modal fade',
|
|
template: _.template(baseDialogTemplate),
|
|
modalBound: false,
|
|
beforeTearDown: function() {
|
|
this.unstickit();
|
|
this.$el.modal('hide');
|
|
},
|
|
displayError: function(options) {
|
|
var logsLink;
|
|
var cluster = app.page.model;
|
|
if (!options.hideLogsLink && cluster && cluster.constructor == models.Cluster) {
|
|
var logOptions = {type: 'local', source: 'api', level: 'error'};
|
|
logsLink = '#cluster/' + cluster.id + '/logs/' + utils.serializeTabOptions(logOptions);
|
|
}
|
|
var dialogOptions = _.defaults(options, {
|
|
error: true,
|
|
title: $.t('dialog.error_dialog.title'),
|
|
message: $.t('dialog.error_dialog.warning'),
|
|
logsLink: logsLink
|
|
});
|
|
this.$el.removeClass().addClass('modal').html(views.Dialog.prototype.template(dialogOptions)).i18n();
|
|
},
|
|
initialize: function(options) {
|
|
_.defaults(this, options);
|
|
},
|
|
render: function(options) {
|
|
this.$el.attr('tabindex', -1);
|
|
if (options && options.error) {
|
|
this.displayError(options);
|
|
} else {
|
|
var templateOptions = _.extend({title: '', message: '', error: false, logsLink: ''}, options);
|
|
this.$el.html(this.template(templateOptions)).i18n();
|
|
}
|
|
if (!this.modalBound) {
|
|
this.$el.on('hidden', _.bind(this.tearDown, this));
|
|
this.$el.on('shown', _.bind(function() {
|
|
this.$('[autofocus]:first').focus();
|
|
}, this));
|
|
this.$el.modal(_.extend({}, this.modalOptions));
|
|
this.modalBound = true;
|
|
}
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.DiscardChangesDialog = views.Dialog.extend({
|
|
template: _.template(discardChangesDialogTemplate),
|
|
events: {
|
|
'click .discard-btn:not(.disabled)': 'discardChanges'
|
|
},
|
|
discardChanges: function() {
|
|
this.$('.discard-btn').addClass('disabled');
|
|
var pendingNodes = this.model.get('nodes').filter(function(node) {
|
|
return node.get('pending_addition') || node.get('pending_deletion') || node.get('pending_roles').length;
|
|
});
|
|
var nodes = new models.Nodes(pendingNodes);
|
|
nodes.each(function(node) {
|
|
node.set({pending_roles: []}, {silent: true});
|
|
if (node.get('pending_addition')) {
|
|
node.set({
|
|
cluster_id: null,
|
|
pending_addition: false
|
|
}, {silent: true});
|
|
} else {
|
|
node.set({pending_deletion: false}, {silent: true});
|
|
}
|
|
});
|
|
nodes.toJSON = function() {
|
|
return this.map(function(node) {
|
|
return _.pick(node.attributes, 'id', 'cluster_id', 'pending_addition', 'pending_deletion', 'pending_roles');
|
|
});
|
|
};
|
|
Backbone.sync('update', nodes)
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
this.model.get('nodes').fetch({data: {cluster_id: this.model.id}});
|
|
// we set node flags silently, so trigger resize event to redraw node list
|
|
this.model.get('nodes').trigger('resize');
|
|
app.navbar.refresh();
|
|
}, this))
|
|
.fail(_.bind(this.displayError, this));
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, {cluster: this.model});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.DisplayChangesDialog = views.Dialog.extend({
|
|
template: _.template(displayChangesDialogTemplate),
|
|
events: {
|
|
'click .start-deployment-btn:not(.disabled)': 'deployCluster'
|
|
},
|
|
deployCluster: function() {
|
|
this.$('.btn').addClass('disabled');
|
|
app.page.removeFinishedDeploymentTasks();
|
|
var task = new models.Task();
|
|
task.save({}, {url: _.result(this.model, 'url') + '/changes', type: 'PUT'})
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
app.page.deploymentTaskStarted();
|
|
}, this))
|
|
.fail(_.bind(this.displayError, this));
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, {
|
|
cluster: this.model,
|
|
size: 1
|
|
});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.RemoveClusterDialog = views.Dialog.extend({
|
|
template: _.template(removeClusterDialogTemplate),
|
|
events: {
|
|
'click .remove-cluster-btn:not(.disabled)': 'removeCluster'
|
|
},
|
|
removeCluster: function() {
|
|
this.$('.remove-cluster-btn').addClass('disabled');
|
|
this.model.destroy({wait: true})
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
app.navbar.refresh();
|
|
app.navigate('#clusters', {trigger: true});
|
|
}, this))
|
|
.fail(_.bind(this.displayError, this));
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, {cluster: this.model});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.StopDeploymentDialog = views.Dialog.extend({
|
|
template: _.template(stopDeploymentDialogTemplate),
|
|
events: {
|
|
'click .stop-deployment-btn:not(:disabled)': 'stopDeployment'
|
|
},
|
|
stopDeployment: function() {
|
|
this.$('.stop-deployment-btn').attr('disabled', true);
|
|
var task = new models.Task();
|
|
task.save({}, {url: _.result(this.model, 'url') + '/stop_deployment', type: 'PUT'})
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
app.page.deploymentTaskStarted();
|
|
}, this))
|
|
.fail(_.bind(function(response) {
|
|
this.displayError({
|
|
title: $.t('dialog.stop_deployment.stop_deployment_error.title'),
|
|
message: utils.getResponseText(response) || $.t('dialog.stop_deployment.stop_deployment_error.stop_deployment_warning')
|
|
});
|
|
}, this));
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, {cluster: this.model});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.ResetEnvironmentDialog = views.Dialog.extend({
|
|
template: _.template(resetEnvironmentDialogTemplate),
|
|
events: {
|
|
'click .reset-environment-btn:not(:disabled)': 'resetEnvironment'
|
|
},
|
|
resetEnvironment: function() {
|
|
this.$('.reset-environment-btn').attr('disabled', true);
|
|
app.page.removeFinishedDeploymentTasks();
|
|
var task = new models.Task();
|
|
task.save({}, {url: _.result(this.model, 'url') + '/reset', type: 'PUT'})
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
app.page.deploymentTaskStarted();
|
|
}, this))
|
|
.fail(_.bind(this.displayError, this));
|
|
}
|
|
});
|
|
|
|
views.UpdateEnvironmentDialog = views.Dialog.extend({
|
|
template: _.template(updateEnvironmentDialogTemplate),
|
|
events: {
|
|
'click .update-environment-btn:not(:disabled)': 'updateEnvironment'
|
|
},
|
|
updateEnvironment: function() {
|
|
this.$('.update-environment-btn').attr('disabled', true);
|
|
var deferred = this.model.save({
|
|
pending_release_id: this.action == 'update' ? this.model.get('pending_release_id') : this.model.get('release_id')
|
|
}, {patch: true, wait: true});
|
|
if (deferred) {
|
|
deferred.done(_.bind(function() {
|
|
app.page.removeFinishedDeploymentTasks();
|
|
var task = new models.Task();
|
|
task.save({}, {url: _.result(this.model, 'url') + '/update', type: 'PUT'})
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
app.page.deploymentTaskStarted();
|
|
}, this))
|
|
.fail(_.bind(this.displayError, this));
|
|
}, this))
|
|
.fail(_.bind(this.displayError, this));
|
|
}
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, {cluster: this.model, action: this.action, isDowngrade: this.isDowngrade});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.ShowNodeInfoDialog = views.Dialog.extend({
|
|
template: _.template(showNodeInfoTemplate),
|
|
templateHelpers: {
|
|
showPropertyName: function(propertyName) {
|
|
return propertyName.replace(/_/g, ' ');
|
|
},
|
|
showPropertyValue: function(group, name, value) {
|
|
try {
|
|
if (group == 'memory' && (name == 'total' || name == 'maximum_capacity' || name == 'size')) {
|
|
value = utils.showMemorySize(value);
|
|
} else if (group == 'disks' && name == 'size') {
|
|
value = utils.showDiskSize(value);
|
|
} else if (name == 'size') {
|
|
value = utils.showSize(value);
|
|
} else if (name == 'frequency') {
|
|
value = utils.showFrequency(value);
|
|
} else if (name == 'max_speed' || name == 'current_speed') {
|
|
value = utils.showBandwidth(value);
|
|
}
|
|
} catch (ignore) {}
|
|
return (_.isEmpty(value) && (value !== 0)) ? '\u00A0' : value;
|
|
},
|
|
showSummary: function(meta, group) {
|
|
var summary = '';
|
|
try {
|
|
if (group == 'system') {
|
|
summary = (meta.system.manufacturer || '') + ' ' + (meta.system.product || '');
|
|
} else if (group == 'memory') {
|
|
if (_.isArray(meta.memory.devices) && meta.memory.devices.length) {
|
|
var sizes = _.groupBy(_.pluck(meta.memory.devices, 'size'), utils.showMemorySize);
|
|
summary = _.map(_.keys(sizes).sort(), function(size) {return sizes[size].length + ' x ' + size;}).join(', ');
|
|
summary += ', ' + utils.showMemorySize(meta.memory.total) + ' ' + $.t('dialog.show_node.total');
|
|
} else {
|
|
summary = utils.showMemorySize(meta.memory.total) + ' ' + $.t('dialog.show_node.total');
|
|
}
|
|
} else if (group == 'disks') {
|
|
summary = meta.disks.length + ' ';
|
|
summary += $.t('dialog.show_node.drive', {count: meta.disks.length});
|
|
summary += ', ' + utils.showDiskSize(_.reduce(_.pluck(meta.disks, 'size'), function(sum, n) {return sum + n;}, 0)) + ' ' + $.t('dialog.show_node.total');
|
|
} else if (group == 'cpu') {
|
|
var frequencies = _.groupBy(_.pluck(meta.cpu.spec, 'frequency'), utils.showFrequency);
|
|
summary = _.map(_.keys(frequencies).sort(), function(frequency) {return frequencies[frequency].length + ' x ' + frequency;}).join(', ');
|
|
} else if (group == 'interfaces') {
|
|
var bandwidths = _.groupBy(_.pluck(meta.interfaces, 'current_speed'), utils.showBandwidth);
|
|
summary = _.map(_.keys(bandwidths).sort(), function(bandwidth) {return bandwidths[bandwidth].length + ' x ' + bandwidth;}).join(', ');
|
|
}
|
|
} catch (ignore) {}
|
|
return summary;
|
|
},
|
|
sortEntryProperties: utils.sortEntryProperties
|
|
},
|
|
events: {
|
|
'click .accordion-heading': 'toggle',
|
|
'click .btn-edit-disks': 'goToDisksConfiguration',
|
|
'click .btn-edit-networks': 'goToInterfacesConfiguration',
|
|
'click .btn-node-console': 'goToSSHConsole'
|
|
},
|
|
toggle: function(e) {
|
|
$(e.currentTarget).siblings('.accordion-body').collapse('toggle');
|
|
},
|
|
goToDisksConfiguration: function() {
|
|
app.navigate('#cluster/' + this.node.get('cluster') + '/nodes/disks/' + utils.serializeTabOptions({nodes: this.node.id}), {trigger: true});
|
|
},
|
|
goToInterfacesConfiguration: function() {
|
|
app.navigate('#cluster/' + this.node.get('cluster') + '/nodes/interfaces/' + utils.serializeTabOptions({nodes: this.node.id}), {trigger: true});
|
|
},
|
|
initialize: function(options) {
|
|
_.defaults(this, options);
|
|
this.node.on('sync', this.render, this);
|
|
},
|
|
goToSSHConsole: function () {
|
|
window.open('http://' + window.location.hostname + ':2443/?' + $.param({
|
|
ssh: 'ssh://root@' + this.node.get('ip'),
|
|
location: this.node.get('ip').replace(/\./g, '')
|
|
}), '_blank');
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, _.extend({node: this.node}, this.templateHelpers));
|
|
this.$('.accordion-body').collapse({
|
|
parent: this.$('.accordion'),
|
|
toggle: false
|
|
}).on('show', function(e) {
|
|
$(e.currentTarget).siblings('.accordion-heading').find('i').removeClass('icon-expand').addClass('icon-collapse');
|
|
}).on('hide', function(e) {
|
|
$(e.currentTarget).siblings('.accordion-heading').find('i').removeClass('icon-collapse').addClass('icon-expand');
|
|
}).on('hidden', function(e) {
|
|
e.stopPropagation();
|
|
});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.DiscardSettingsChangesDialog = views.Dialog.extend({
|
|
template: _.template(discardSettingsChangesTemplate),
|
|
events: {
|
|
'click .proceed-btn': 'proceed'
|
|
},
|
|
proceed: function() {
|
|
this.$el.modal('hide');
|
|
app.page.removeFinishedNetworkTasks().always(_.bind(this.cb, this));
|
|
},
|
|
render: function() {
|
|
if (this.verification) {
|
|
this.message = $.t('dialog.dismiss_settings.verify_message');
|
|
}
|
|
this.constructor.__super__.render.call(this, {
|
|
message: this.message || $.t('dialog.dismiss_settings.default_message'),
|
|
verification: this.verification || false
|
|
});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.DeleteNodesDialog = views.Dialog.extend({
|
|
template: _.template(deleteNodesTemplate),
|
|
events: {
|
|
'click .btn-delete': 'deleteNodes'
|
|
},
|
|
deleteNodes: function() {
|
|
if (this.nodes.cluster) {
|
|
this.$('.btn-delete').prop('disabled', true);
|
|
this.nodes.each(function(node) {
|
|
if (!node.get('pending_deletion')) {
|
|
if (node.get('pending_addition')) {
|
|
node.set({
|
|
cluster_id: null,
|
|
pending_addition: false,
|
|
pending_roles: []
|
|
});
|
|
} else{
|
|
node.set({pending_deletion: true});
|
|
}
|
|
}
|
|
}, this);
|
|
this.nodes.toJSON = function(options) {
|
|
return this.map(function(node) {
|
|
return _.pick(node.attributes, 'id', 'cluster_id', 'pending_roles', 'pending_addition', 'pending_deletion');
|
|
});
|
|
};
|
|
this.nodes.sync('update', this.nodes)
|
|
.done(_.bind(function() {
|
|
this.$el.modal('hide');
|
|
app.page.tab.model.fetch();
|
|
app.page.tab.screen.nodes.fetch();
|
|
_.invoke(app.page.tab.screen.nodes.where({checked: true}), 'set', {checked: false});
|
|
app.page.tab.screen.updateBatchActionsButtons();
|
|
app.navbar.refresh();
|
|
app.page.removeFinishedNetworkTasks();
|
|
}, this))
|
|
.fail(_.bind(function() {
|
|
utils.showErrorDialog({
|
|
title: $.t('cluster_page.nodes_tab.node_deletion_error.title'),
|
|
message: $.t('cluster_page.nodes_tab.node_deletion_error.node_deletion_warning')
|
|
});
|
|
}, this));
|
|
}
|
|
},
|
|
render: function() {
|
|
this.constructor.__super__.render.call(this, {nodes: this.nodes});
|
|
return this;
|
|
}
|
|
});
|
|
|
|
views.ChangePasswordDialog = views.Dialog.extend({
|
|
template: _.template(changePasswordTemplate),
|
|
mixins: [viewMixins.toggleablePassword],
|
|
events: {
|
|
'click .btn-change-password': 'changePassword',
|
|
'keyup input': 'onPasswordChange',
|
|
'keydown': 'onKeydown'
|
|
},
|
|
changePassword: function() {
|
|
var currentPassword = this.$('[name=current_password]').val(),
|
|
newPassword = this.$('[name=new_password]').val(),
|
|
confirmedPassword= this.$('[name=confirm_new_password]').val();
|
|
if (currentPassword && (newPassword == confirmedPassword)) {
|
|
app.keystoneClient.changePassword(currentPassword, newPassword)
|
|
.done(_.bind(function() {
|
|
app.user.set({password: app.keystoneClient.password});
|
|
this.$el.modal('hide');
|
|
}, this))
|
|
.fail(_.bind(function() {
|
|
this.$('[name=current_password]').focus().addClass('error').parent().siblings('.validation-error').show();
|
|
}, this));
|
|
}
|
|
},
|
|
onPasswordChange: function(e) {
|
|
this.$(e.currentTarget).removeClass('error').parent().siblings('.validation-error').hide();
|
|
var newPassword = this.$('[name=new_password]'),
|
|
confirmedPassword = this.$('[name=confirm_new_password]');
|
|
confirmedPassword.removeClass('error').parent().siblings('.validation-error').hide();
|
|
if (newPassword.val() != confirmedPassword.val()) {
|
|
confirmedPassword.addClass('error').parent().siblings('.validation-error').show();
|
|
}
|
|
var disabled = (!_.all(_.invoke(_.map(this.$('input'), $), 'val')) || this.$('.validation-error').is(':visible'));
|
|
_.defer(_.bind(function() {
|
|
this.$('.btn-change-password').attr('disabled', disabled);
|
|
}, this));
|
|
},
|
|
onKeydown: function(e) {
|
|
if (e.which == 13) {
|
|
e.preventDefault();
|
|
this.changePassword();
|
|
}
|
|
}
|
|
});
|
|
|
|
return views;
|
|
});
|