fuel-ui/static/js/views/cluster_page.js

337 lines
15 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(
[
'utils',
'models',
'views/common',
'jsx!views/dialogs',
'views/cluster_page_tabs/nodes_tab',
'views/cluster_page_tabs/network_tab',
'views/cluster_page_tabs/settings_tab',
'views/cluster_page_tabs/logs_tab',
'views/cluster_page_tabs/actions_tab',
'views/cluster_page_tabs/healthcheck_tab',
'text!templates/cluster/page.html',
'text!templates/cluster/customization_message.html',
'text!templates/cluster/deployment_result.html',
'text!templates/cluster/deployment_control.html',
'text!templates/cluster/cluster_release_info.html'
],
function(utils, models, commonViews, dialogViews, NodesTab, NetworkTab, SettingsTab, LogsTab, ActionsTab, HealthCheckTab, clusterPageTemplate, clusterCustomizationMessageTemplate, deploymentResultTemplate, deploymentControlTemplate, clusterInfoTemplate) {
'use strict';
var ClusterPage, ClusterInfo, ClusterCustomizationMessage, DeploymentResult, DeploymentControl;
ClusterPage = commonViews.Page.extend({
navbarActiveElement: 'clusters',
breadcrumbsPath: function() {
return [['home', '#'], ['environments', '#clusters'], [this.model.get('name'), null, true]];
},
title: function() {
return this.model.get('name');
},
tabs: ['nodes', 'network', 'settings', 'logs', 'healthcheck', 'actions'],
updateInterval: 5000,
template: _.template(clusterPageTemplate),
events: {
'click .task-result .close': 'dismissTaskResult'
},
getReleaseSetupTask: function(status) {
return this.tasks.findTask({group: 'release_setup', status: status || 'running', release: this.model.get('release').id});
},
removeFinishedNetworkTasks: function(removeSilently) {
return this.removeFinishedTasks(this.model.tasks({group: 'network'}), removeSilently);
},
removeFinishedDeploymentTasks: function(removeSilently) {
return this.removeFinishedTasks(this.model.tasks({group: 'deployment'}), removeSilently);
},
removeFinishedTasks: function(tasks, removeSilently) {
var requests = [];
_.each(tasks, function(task) {
if (task.get('status') != 'running') {
if (!removeSilently) {
this.model.get('tasks').remove(task);
}
requests.push(task.destroy({silent: true}));
}
}, this);
return $.when.apply($, requests);
},
dismissTaskResult: function() {
var task = this.model.task({group: 'deployment'}) || this.getReleaseSetupTask('error');
if (task) {
task.destroy();
}
},
displayChanges: function() {
this.registerSubView(new dialogViews.DisplayChangesDialog({model: this.model})).render();
},
discardSettingsChanges: function(options) {
this.registerSubView(new dialogViews.DiscardSettingsChangesDialog(options)).render();
},
onNameChange: function() {
this.updateBreadcrumbs();
this.updateTitle();
},
onTabLeave: function(e) {
var href = $(e.currentTarget).attr('href');
if (Backbone.history.getHash() != href.substr(1) && _.result(this.tab, 'hasChanges')) {
e.preventDefault();
this.discardSettingsChanges({
verification: this.model.tasks({group: 'network', status: 'running'}).length,
cb: _.bind(function() {
app.navigate(href, {trigger: true});
}, this)
});
}
},
scheduleUpdate: function() {
if (this.model.task({group: ['deployment', 'network'], status: 'running'}) || this.getReleaseSetupTask()) {
this.registerDeferred($.timeout(this.updateInterval).done(_.bind(this.update, this)));
}
},
update: function() {
var complete = _.after(2, _.bind(this.scheduleUpdate, this));
var task = this.model.task({group: 'deployment', status: 'running'});
if (task) {
this.registerDeferred(task.fetch().done(_.bind(function() {
if (!task.match({status: 'running'})) {
this.deploymentTaskFinished();
}
}, this)).always(complete));
this.registerDeferred(this.model.get('nodes').fetch({data: {cluster_id: this.model.id}}).always(complete));
}
var verificationTask = this.model.task('verify_networks', 'running');
if (verificationTask) {
this.registerDeferred(verificationTask.fetch().always(_.bind(this.scheduleUpdate, this)));
}
var setupTask = this.getReleaseSetupTask();
if (setupTask) {
this.registerDeferred(this.tasks.fetch()
.always(_.bind(this.scheduleUpdate, this))
.done(_.bind(function() {
if (setupTask.get('status') != 'running') {
this.setupFinished(setupTask);
}
}, this))
);
}
},
deploymentTaskStarted: function() {
$.when(this.model.fetch(), this.model.fetchRelated('nodes'), this.model.fetchRelated('tasks')).always(_.bind(function() {
// FIXME: hack to prevent "Deploy" button flashing after deployment is finished
this.model.set({changes: []}, {silent: true});
this.scheduleUpdate();
}, this));
},
deploymentTaskFinished: function() {
$.when(this.model.fetch(), this.model.fetchRelated('nodes'), this.model.fetchRelated('tasks')).always(_.bind(function() {
app.navbar.refresh();
}, this));
},
setupFinished: function(task) {
app.navbar.refresh();
this.model.get('release').fetch();
if (task.match({status: 'ready'})) {
task.destroy();
}
},
beforeTearDown: function() {
$(window).off('beforeunload.' + this.eventNamespace);
$('body').off('click.' + this.eventNamespace);
},
onBeforeunloadEvent: function() {
if (_.result(this.tab, 'hasChanges')) {
return dialogViews.DiscardSettingsChangesDialog.prototype.defaultMessage;
}
},
initialize: function(options) {
_.defaults(this, options);
this.model.on('change:name', this.onNameChange, this);
this.model.on('change:release_id', function() {
var release = new models.Release({id: this.model.get('release_id')});
release.fetch().done(_.bind(function(){
this.model.set({release: release});
}, this));
}, this);
this.scheduleUpdate();
this.eventNamespace = 'unsavedchanges' + this.activeTab;
$(window).on('beforeunload.' + this.eventNamespace, _.bind(this.onBeforeunloadEvent, this));
$('body').on('click.' + this.eventNamespace, 'a[href^=#]:not(.no-leave-check)', _.bind(this.onTabLeave, this));
},
render: function() {
this.tearDownRegisteredSubViews();
this.$el.html(this.template({
cluster: this.model,
tabs: this.tabs,
activeTab: this.activeTab
})).i18n();
var options = {model: this.model, page: this};
this.clusterInfo = utils.universalMount(new ClusterInfo(options), this.$('.cluster-info'), this);
this.clusterCustomizationMessage = utils.universalMount(new ClusterCustomizationMessage(options), this.$('.customization-message'), this);
this.deploymentResult = utils.universalMount(new DeploymentResult(options), this.$('.deployment-result'), this);
this.deploymentControl = utils.universalMount(new DeploymentControl(options), this.$('.deployment-control'), this);
var tabs = {
'nodes': NodesTab,
'network': NetworkTab,
'settings': SettingsTab,
'actions': ActionsTab,
'logs': LogsTab,
'healthcheck': HealthCheckTab
};
if (_.has(tabs, this.activeTab)) {
this.tab = utils.universalMount(
new tabs[this.activeTab]({model: this.model, tabOptions: this.tabOptions, page: this}),
this.$('#tab-' + this.activeTab),
this
);
}
return this;
}
});
ClusterInfo = Backbone.View.extend({
className: 'container',
template: _.template(clusterInfoTemplate),
bindings: {
'.name': 'name',
'.status span': 'status',
'.status': {
attributes:[{
observe: 'status',
name: 'class',
onGet: function(value) {
return _.contains(['error', 'update_error'], value) ? 'status error' : 'status';
}
}]
},
'.mode span': {
observe: 'mode',
onGet: function(value) {
return $.t('cluster.mode.' + value);
}
}
},
initialize: function(options) {
_.defaults(this, options);
this.model.get('nodes').on('resize', this.render, this);
},
render: function() {
this.$el.html(this.template({nodesLength: this.model.get('nodes').length})).i18n();
this.stickit();
this.stickit(this.model.get('release'), {'.release span': {
observe: ['name', 'version'],
onGet: function(values) {
return values[0] + ' (' + values[1] + ')';
}
}});
return this;
}
});
ClusterCustomizationMessage = Backbone.View.extend({
template: _.template(clusterCustomizationMessageTemplate),
initialize: function(options) {
this.model.on('change:is_customized', this.render, this);
},
render: function() {
this.$el.html(this.template({cluster: this.model})).i18n();
return this;
}
});
DeploymentResult = Backbone.View.extend({
template: _.template(deploymentResultTemplate),
templateHelpers: _.pick(utils, 'urlify', 'linebreaks', 'serializeTabOptions'),
initialize: function(options) {
_.defaults(this, options);
_.invoke([this.model.get('tasks'), this.page.tasks], 'bindToView', this, [{group: 'deployment'}, {group: 'release_setup', release: this.model.get('release').id}], function(task) {
task.on('change:status', this.render, this);
});
},
render: function() {
this.$el.html(this.template(_.extend({
cluster: this.model,
task: this.model.task({group: 'deployment'}) || this.page.getReleaseSetupTask('error')
}, this.templateHelpers))).i18n();
return this;
}
});
DeploymentControl = Backbone.View.extend({
template: _.template(deploymentControlTemplate),
events: {
'click .stop-deployment-btn': 'stopDeployment',
'click .deploy-btn:not(.disabled)': 'onDeployRequest',
'click .rollback': 'discardChanges'
},
discardChanges: function() {
this.page.registerSubView(new dialogViews.DiscardChangesDialog({model: this.page.model})).render();
},
onDeployRequest: function() {
if (_.result(this.page.tab, 'hasChanges')) {
this.page.discardSettingsChanges({cb: _.bind(function() {
this.page.tab.revertChanges();
this.page.displayChanges();
}, this)});
} else {
this.page.displayChanges();
}
},
stopDeployment: function() {
this.registerSubView(new dialogViews.StopDeploymentDialog({model: this.model})).render();
},
initialize: function(options) {
_.defaults(this, options);
this.model.on('change:changes', this.render, this);
this.model.get('release').on('change:state', this.render, this);
_.invoke([this.model.get('tasks'), this.page.tasks], 'bindToView', this, [{group: 'deployment'}, {group: 'release_setup', release: this.model.get('release').id}], function(task) {
task.on('change:status', this.render, this);
task.on('change:progress', this.updateProgress, this);
});
this.model.get('nodes').each(this.bindNodeEvents, this);
this.model.get('nodes').on('resize', this.render, this);
this.model.get('nodes').on('add', this.onNewNode, this);
},
bindNodeEvents: function(node) {
return node.on('change:pending_addition change:pending_deletion', this.render, this);
},
onNewNode: function(node) {
return this.bindNodeEvents(node) && this.render();
},
updateProgress: function() {
var task = this.model.task({group: 'deployment', status: 'running'}) || this.page.getReleaseSetupTask();
if (task) {
var progress = task.get('progress') || 0;
this.$('.bar').css('width', (progress > 3 ? progress : 3) + '%');
this.$('.percentage').text(progress + '%');
}
},
render: function() {
this.$el.html(this.template({
cluster: this.model,
task: this.model.task({group: 'deployment', status: 'running'}) || this.page.getReleaseSetupTask()
})).i18n();
this.updateProgress();
return this;
}
});
return ClusterPage;
});