Edit loadbalancer config

This commit is contained in:
Vincent Fiduccia 2015-06-09 20:02:29 -07:00
parent cb404a4dfc
commit 2dfe358803
16 changed files with 245 additions and 128 deletions

View File

@ -1,7 +1,8 @@
import Ember from 'ember'; import Ember from 'ember';
import { addAction } from 'ui/utils/add-view-action'; import { addAction } from 'ui/utils/add-view-action';
import SelectTab from 'ui/mixins/select-tab';
export default Ember.View.extend({ export default Ember.View.extend(SelectTab, {
actions: { actions: {
addEnvironment: addAction('addEnvironment', '.environment-name'), addEnvironment: addAction('addEnvironment', '.environment-name'),
addPort: addAction('addPort', '.port-public'), addPort: addAction('addPort', '.port-public'),
@ -13,14 +14,6 @@ export default Ember.View.extend({
addDevice: addAction('addDevice', '.device-host'), addDevice: addAction('addDevice', '.device-host'),
addLabel: addAction('addLabel', '.label-key'), addLabel: addAction('addLabel', '.label-key'),
addSchedulingRule: addAction('addSchedulingRule', '.schedule-rule'), addSchedulingRule: addAction('addSchedulingRule', '.schedule-rule'),
selectTab: function(name) {
this.set('context.tab',name);
this.$('.tab').removeClass('active');
this.$('.tab[data-section="'+name+'"]').addClass('active');
this.$('.section').addClass('hide');
this.$('.section[data-section="'+name+'"]').removeClass('hide');
}
}, },
didInsertElement: function() { didInsertElement: function() {
@ -109,7 +102,7 @@ export default Ember.View.extend({
this.$('.select-cap-drop').multiselect(opts); this.$('.select-cap-drop').multiselect(opts);
}, },
priviligedDidChange: function() { privilegedDidChange: function() {
var add = this.$('.select-cap-add'); var add = this.$('.select-cap-add');
var drop = this.$('.select-cap-drop'); var drop = this.$('.select-cap-drop');
if ( add && drop ) if ( add && drop )

View File

@ -1,20 +1,13 @@
import Ember from 'ember'; import Ember from 'ember';
import { addAction } from 'ui/utils/add-view-action'; import { addAction } from 'ui/utils/add-view-action';
import SelectTab from 'ui/mixins/select-tab';
export default Ember.View.extend({ export default Ember.View.extend(SelectTab, {
actions: { actions: {
addHost: addAction('addHost', '.lb-host'), addHost: addAction('addHost', '.lb-host'),
addTargetContainer: addAction('addTargetContainer', '.lb-target'), addTargetContainer: addAction('addTargetContainer', '.lb-target'),
addTargetIp: addAction('addTargetIp', '.lb-target'), addTargetIp: addAction('addTargetIp', '.lb-target'),
addListener: addAction('addListener', '.lb-listener-source-port'), addListener: addAction('addListener', '.lb-listener-source-port'),
selectTab: function(name) {
this.set('context.tab',name);
this.$('.tab').removeClass('active');
this.$('.tab[data-section="'+name+'"]').addClass('active');
this.$('.section').addClass('hide');
this.$('.section[data-section="'+name+'"]').removeClass('hide');
}
}, },
didInsertElement: function() { didInsertElement: function() {

View File

@ -29,6 +29,12 @@ var LoadBalancerConfigController = Cattle.TransitioningResourceController.extend
sourceContent: this.get('loadBalancers'), sourceContent: this.get('loadBalancers'),
}); });
}.property('loadBalancers'), }.property('loadBalancers'),
unremovedListeners: function() {
return UnremovedArrayProxy.create({
sourceContent: this.get('listeners'),
});
}.property('listeners'),
}); });
LoadBalancerConfigController.reopenClass({ LoadBalancerConfigController.reopenClass({

View File

@ -1,8 +1,50 @@
import Ember from 'ember'; import Ember from 'ember';
import Cattle from 'ui/utils/cattle'; import Cattle from 'ui/utils/cattle';
import EditLoadBalancerConfig from 'ui/mixins/edit-loadbalancerconfig';
export default Ember.ObjectController.extend(Cattle.NewOrEditMixin, { export default Ember.ObjectController.extend(Cattle.NewOrEditMixin, EditLoadBalancerConfig, {
editing: true, editing: true,
primaryResource: Ember.computed.alias('model.config'),
queryParams: ['tab'],
tab: 'listeners',
initFields: function() {
this._super();
this.initListeners();
this.initStickiness();
this.initHealthCheck();
},
didSave: function() {
var orig = this.get('listeners')||[];
var neu = this.get('listenersArray');
var promises = [];
orig.forEach((listener) => {
// Delete removed
if( neu.indexOf(listener) === -1 )
{
promises.push(listener.delete());
}
});
neu.forEach((listener) => {
if ( orig.indexOf(listener) === -1 )
{
promises.push(listener.save());
}
});
return Ember.RSVP.all(promises).then(() => {
var ids = neu.filter((listener) => {
return neu.indexOf(listener) >= 0;
}).map((listener) => {
return listener.get('id');
});
return this.get('config').doAction('setlisteners',{loadBalancerListenerIds: ids});
});
},
doneSaving: function() { doneSaving: function() {
this.send('goToPrevious'); this.send('goToPrevious');

View File

@ -2,12 +2,57 @@ import Ember from 'ember';
export default Ember.Route.extend({ export default Ember.Route.extend({
model: function() { model: function() {
return this.modelFor('loadbalancerconfig'); var store = this.get('store');
var orig = this.modelFor('loadbalancerconfig');
var config = orig.clone();
var listeners = (orig.get('listeners')||[]).filter((listener) => {
return ['removed','purging','purged'].indexOf(listener.get('state')) === -1;
});
var healthCheck = config.get('healthCheck');
if ( !healthCheck )
{
healthCheck = store.createRecord({
type: 'loadBalancerHealthCheck',
interval: 2000,
responseTimeout: 2000,
healthyThreshold: 2,
unhealthyThreshold: 3,
});
config.set('healthCheck', healthCheck);
}
var appCookie = config.get('appCookieStickinessPolicy');
if ( !appCookie )
{
appCookie = store.createRecord({
type: 'loadBalancerAppCookieStickinessPolicy',
mode: 'path_parameters',
requestLearn: true,
timeout: 3600000,
});
}
var lbCookie = config.get('lbCookieStickinessPolicy');
if ( !appCookie )
{
lbCookie = store.createRecord({
type: 'loadBalancerCookieStickinessPolicy'
});
}
return {
listeners: listeners,
config: config,
healthCheck: healthCheck,
appCookie: appCookie,
lbCookie: lbCookie,
};
}, },
setupController: function(controller, model) { setupController: function(controller, model) {
controller.set('originalModel',model); controller.set('model', model);
controller.set('model', model.clone());
controller.initFields(); controller.initFields();
}, },
@ -15,6 +60,13 @@ export default Ember.Route.extend({
this.render('loadbalancerconfig/edit', {into: 'application', outlet: 'overlay'}); this.render('loadbalancerconfig/edit', {into: 'application', outlet: 'overlay'});
}, },
resetController: function (controller, isExiting/*, transition*/) {
if (isExiting)
{
controller.set('tab', 'listeners');
}
},
actions: { actions: {
cancel: function() { cancel: function() {
this.goToPrevious(); this.goToPrevious();

View File

@ -7,7 +7,7 @@
<label for="name">Name</label> <label for="name">Name</label>
</div> </div>
<div class="col-sm-12 col-md-8"> <div class="col-sm-12 col-md-8">
{{input id="name" type="text" value=name classNames="form-control" placeholder="e.g. Website"}} {{input id="name" type="text" value=primaryResource.name classNames="form-control" placeholder="e.g. Website"}}
</div> </div>
</div> </div>
@ -16,9 +16,11 @@
<label for="description">Description</label> <label for="description">Description</label>
</div> </div>
<div class="col-sm-12 col-md-8"> <div class="col-sm-12 col-md-8">
{{textarea id="description" value=description classNames="form-control no-resize" rows="5" placeholder="e.g. Balancer for mycompany.com"}} {{textarea id="description" value=primaryResource.description classNames="form-control no-resize" rows="5" placeholder="e.g. Balancer for mycompany.com"}}
</div> </div>
</div> </div>
</section> </section>
{{partial "loadbalancer/edit-config"}}
{{partial "save-cancel"}} {{partial "save-cancel"}}

View File

@ -1,7 +1,11 @@
import Overlay from "ui/overlay/view"; import Overlay from "ui/overlay/view";
import SelectTab from 'ui/mixins/select-tab';
import { addAction } from 'ui/utils/add-view-action';
export default Overlay.extend({ export default Overlay.extend(SelectTab, {
actions: { actions: {
addListener: addAction('addListener', '.lb-listener-source-port'),
overlayClose: function() { overlayClose: function() {
this.get('controller').send('cancel'); this.get('controller').send('cancel');
}, },
@ -9,5 +13,10 @@ export default Overlay.extend({
overlayEnter: function() { overlayEnter: function() {
this.get('controller').send('save'); this.get('controller').send('save');
}, },
} },
didInsertElement: function() {
this.send('selectTab',this.get('context.tab'));
},
}); });

View File

@ -1,20 +1,13 @@
import Ember from 'ember'; import Ember from 'ember';
import { addAction } from 'ui/utils/add-view-action'; import { addAction } from 'ui/utils/add-view-action';
import SelectTab from 'ui/mixins/select-tab';
export default Ember.View.extend({ export default Ember.View.extend(SelectTab, {
actions: { actions: {
addHost: addAction('addHost', '.lb-host'), addHost: addAction('addHost', '.lb-host'),
addTargetContainer: addAction('addTargetContainer', '.lb-target'), addTargetContainer: addAction('addTargetContainer', '.lb-target'),
addTargetIp: addAction('addTargetIp', '.lb-target'), addTargetIp: addAction('addTargetIp', '.lb-target'),
addListener: addAction('addListener', '.lb-listener-source-port'), addListener: addAction('addListener', '.lb-listener-source-port'),
selectTab: function(name) {
this.set('context.tab',name);
this.$('.tab').removeClass('active');
this.$('.tab[data-section="'+name+'"]').addClass('active');
this.$('.section').addClass('hide');
this.$('.section[data-section="'+name+'"]').removeClass('hide');
}
}, },
didInsertElement: function() { didInsertElement: function() {

View File

@ -1,20 +1,13 @@
import Ember from 'ember'; import Ember from 'ember';
import { addAction } from 'ui/utils/add-view-action'; import { addAction } from 'ui/utils/add-view-action';
import SelectTab from 'ui/mixins/select-tab';
export default Ember.View.extend({ export default Ember.View.extend(SelectTab, {
actions: { actions: {
addHost: addAction('addHost', '.lb-host'), addHost: addAction('addHost', '.lb-host'),
addTargetContainer: addAction('addTargetContainer', '.lb-target'), addTargetContainer: addAction('addTargetContainer', '.lb-target'),
addTargetIp: addAction('addTargetIp', '.lb-target'), addTargetIp: addAction('addTargetIp', '.lb-target'),
addListener: addAction('addListener', '.lb-listener-source-port'), addListener: addAction('addListener', '.lb-listener-source-port'),
selectTab: function(name) {
this.set('context.tab',name);
this.$('.tab').removeClass('active');
this.$('.tab[data-section="'+name+'"]').addClass('active');
this.$('.section').addClass('hide');
this.$('.section[data-section="'+name+'"]').removeClass('hide');
}
}, },
didInsertElement: function() { didInsertElement: function() {

View File

@ -472,12 +472,12 @@ export default Ember.Mixin.create(Cattle.NewOrEditMixin, EditHealthCheck, EditLa
portsArray: null, portsArray: null,
initPorts: function() { initPorts: function() {
var out = []; var out = [];
var ports = this.get('instance.ports')||[];
ports.forEach(function(value) { var ports = this.get('ports');
// Objects, from edit if ( ports )
if ( typeof value === 'object' )
{ {
// Objects, from edit
ports.forEach(function(value) {
out.push({ out.push({
existing: (value.id ? true : false), existing: (value.id ? true : false),
obj: value, obj: value,
@ -485,6 +485,21 @@ export default Ember.Mixin.create(Cattle.NewOrEditMixin, EditHealthCheck, EditLa
private: value.privatePort, private: value.privatePort,
protocol: value.protocol, protocol: value.protocol,
}); });
});
}
else
{
ports = this.get('instance.ports')||[];
ports.forEach(function(value) {
if ( typeof value === 'object' )
{
// Objects, from clone
out.push({
existing: false,
public: value.publicPort,
private: value.privatePort,
protocol: value.protocol,
});
} }
else else
{ {
@ -501,6 +516,7 @@ export default Ember.Mixin.create(Cattle.NewOrEditMixin, EditHealthCheck, EditLa
} }
} }
}); });
}
this.set('portsArray', out); this.set('portsArray', out);
}, },

View File

@ -27,11 +27,12 @@ export default Ember.Mixin.create(EditHealthCheck,{
listenersArray: null, listenersArray: null,
initListeners: function() { initListeners: function() {
var store = this.get('store'); var store = this.get('store');
var existing = this.get('balancer.loadBalancerListeners');
var out = []; var out = [];
if ( existing ) var existingService = this.get('balancer.loadBalancerListeners');
var existingRegular = this.get('listeners');
if ( existingService )
{ {
existing.forEach((listener) => { existingService.forEach((listener) => {
var neu = listener.cloneForNew(); var neu = listener.cloneForNew();
neu.setProperties({ neu.setProperties({
serviceId: null, serviceId: null,
@ -40,6 +41,10 @@ export default Ember.Mixin.create(EditHealthCheck,{
out.push(neu); out.push(neu);
}); });
} }
else if ( existingRegular )
{
out.pushObjects(existingRegular);
}
else else
{ {
out.push(store.createRecord({ out.push(store.createRecord({

13
app/mixins/select-tab.js Normal file
View File

@ -0,0 +1,13 @@
import Ember from 'ember';
export default Ember.Mixin.create({
actions: {
selectTab: function(name) {
this.set('context.tab',name);
this.$('.tab').removeClass('active');
this.$('.tab[data-section="'+name+'"]').addClass('active');
this.$('.section').addClass('hide');
this.$('.section[data-section="'+name+'"]').removeClass('hide');
}
}
});

View File

@ -1,18 +1,11 @@
import Ember from 'ember'; import Ember from 'ember';
import { addAction } from 'ui/utils/add-view-action'; import { addAction } from 'ui/utils/add-view-action';
import SelectTab from 'ui/mixins/select-tab';
export default Ember.View.extend({ export default Ember.View.extend(SelectTab, {
actions: { actions: {
addTargetService: addAction('addTargetService', '.lb-target'), addTargetService: addAction('addTargetService', '.lb-target'),
addListener: addAction('addListener', '.lb-listener-source-port'), addListener: addAction('addListener', '.lb-listener-source-port'),
selectTab: function(name) {
this.set('context.tab',name);
this.$('.tab').removeClass('active');
this.$('.tab[data-section="'+name+'"]').addClass('active');
this.$('.section').addClass('hide');
this.$('.section[data-section="'+name+'"]').removeClass('hide');
}
}, },
didInsertElement: function() { didInsertElement: function() {

View File

@ -25,9 +25,7 @@
{{input class="form-control input-sm port-public" type="number" min="1" max="65535" value=port.public placeholder="e.g. 80"}} {{input class="form-control input-sm port-public" type="number" min="1" max="65535" value=port.public placeholder="e.g. 80"}}
</td> </td>
<td class="text-center"> <td class="text-center">
<div class="form-group">
<p class="form-control-static"><i class="ss-right"></i></p> <p class="form-control-static"><i class="ss-right"></i></p>
</div>
</td> </td>
<td> <td>
<div class="form-control-static text-muted">{{port.private}}</div> <div class="form-control-static text-muted">{{port.private}}</div>

View File

@ -1,7 +1,7 @@
<div class="well section"> <div class="well section">
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-6">
<h4>Listeners ({{listeners.length}})</h4> <h4>Listeners ({{unremovedListeners.length}})</h4>
</div> </div>
</div> </div>
@ -16,7 +16,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{#each listener in listeners itemController="loadbalancerlistener"}} {{#each listener in unremovedListeners itemController="loadbalancerlistener"}}
<tr> <tr>
<td> <td>
<span {{bind-attr class=":badge :state listener.stateBackground"}}> <span {{bind-attr class=":badge :state listener.stateBackground"}}>
@ -82,13 +82,13 @@
<label>Healthy Threshold</label> <label>Healthy Threshold</label>
</div> </div>
<div class="col-sm-12 col-md-3"> <div class="col-sm-12 col-md-3">
<span class="form-control-static">{{config.healthCheck.healthyThreshold}}ms</span> <span class="form-control-static">{{config.healthCheck.healthyThreshold}}</span>
</div> </div>
<div class="col-sm-12 col-md-3"> <div class="col-sm-12 col-md-3">
<label>Unhealthy Threshold</label> <label>Unhealthy Threshold</label>
</div> </div>
<div class="col-sm-12 col-md-3"> <div class="col-sm-12 col-md-3">
<span class="form-control-static">{{config.healthCheck.unhealthyThreshold}}ms</span> <span class="form-control-static">{{config.healthCheck.unhealthyThreshold}}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -14,6 +14,16 @@
<tbody> <tbody>
{{#each listener in listenersArray}} {{#each listener in listenersArray}}
<tr> <tr>
{{#if listener.id}}
<td>{{listener.sourcePort}}/{{listener.sourceProtocol}}</td>
<td class="text-center"><i class="ss-right"></i></td>
<td>{{listener.targetPort}}/{{listener.targetProtocol}}</td>
<td>&nbsp;</td>
<td>{{listener.algorithm}}</td>
<td class="text-right">
<button {{action "removeListener" listener}} class="btn-circle-x" type="button" tabindex="-1"></button>
</td>
{{else}}
<td> <td>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
{{input type="text" classNames="form-control lb-listener-source-port" min="1" max="65535" step="1" value=listener.sourcePort placeholder="e.g. 80"}} {{input type="text" classNames="form-control lb-listener-source-port" min="1" max="65535" step="1" value=listener.sourcePort placeholder="e.g. 80"}}
@ -32,9 +42,7 @@
</div> </div>
</div> </div>
</td> </td>
<td class="text-center"><div class="form-control-static"><i class="ss-right"></i></div></td>
<td class="text-center"><div class="form-control-static input-sm"><i class="ss-right"></i></div></td>
<td> <td>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
{{input type="text" classNames="form-control lb-listener-target-port" min="1" max="65535" step="1" value=listener.targetPort placeholder="e.g. 8080"}} {{input type="text" classNames="form-control lb-listener-target-port" min="1" max="65535" step="1" value=listener.targetPort placeholder="e.g. 8080"}}
@ -62,7 +70,7 @@
<td> <td>
{{#if model.isService}} {{#if model.isService}}
<div class="form-control-static input-sm">Round Robin</div> <div class="form-control-static">Round Robin</div>
{{else}} {{else}}
{{view "select" {{view "select"
class="form-control input-sm" class="form-control input-sm"
@ -74,6 +82,7 @@
<td class="text-right"> <td class="text-right">
<button {{action "removeListener" listener}} class="btn-circle-x" type="button" tabindex="-1"></button> <button {{action "removeListener" listener}} class="btn-circle-x" type="button" tabindex="-1"></button>
</td> </td>
{{/if}}
</tr> </tr>
{{/each}} {{/each}}
</tbody> </tbody>