337 lines
15 KiB
JavaScript
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;
|
|
});
|