mirror of https://github.com/rancher/ui.git
Merge pull request #145 from vincent99/service-balancer
Load Balancer Service
This commit is contained in:
commit
b7e4a28e64
|
|
@ -7,6 +7,6 @@ export default Ember.Component.extend({
|
|||
tagName: 'nav',
|
||||
hasServices: function() {
|
||||
var store = this.get('store');
|
||||
return store && store.hasRecordFor('schema','service') && this.get('session.showServices');
|
||||
return store && store.hasRecordFor('schema','service');
|
||||
}.property(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,14 @@ var EnvironmentController = Cattle.TransitioningResourceController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
addBalancer: function() {
|
||||
this.transitionToRoute('service.new-balancer', {
|
||||
queryParams: {
|
||||
environmentId: this.get('id'),
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
edit: function() {
|
||||
this.transitionToRoute('environment.edit', this.get('id'));
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,5 +6,8 @@ export default Ember.ObjectController.extend({
|
|||
addService: function() {
|
||||
this.get('controllers.environment').send('addService');
|
||||
},
|
||||
addBalancer: function() {
|
||||
this.get('controllers.environment').send('addBalancer');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@
|
|||
<div class="pod-column">
|
||||
{{#each item in col}}
|
||||
{{#if item.isNewPlaceHolder}}
|
||||
{{add-pod action="addService" label="Add Service"}}
|
||||
{{#if item.isService}}
|
||||
{{add-pod action="addService" label="Add Service"}}
|
||||
{{/if}}
|
||||
{{#if item.isBalancer}}
|
||||
{{add-pod action="addBalancer" label="Add Load Balancer"}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#with item as service controller="service"}}
|
||||
{{service-pod model=service}}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ export default ColumnView.extend({
|
|||
columns[nextIndex()].push(services.objectAt(i));
|
||||
}
|
||||
|
||||
// Add a placeholder for where to put the 'Add Service' button
|
||||
columns[nextIndex()].push(Ember.Object.create({isNewPlaceHolder: true}));
|
||||
// Add a placeholder for where to put the 'Add Service' and 'Add Balancer' buttons
|
||||
columns[nextIndex()].push(Ember.Object.create({isNewPlaceHolder: true, isService: true}));
|
||||
columns[nextIndex()].push(Ember.Object.create({isNewPlaceHolder: true, isBalancer: true}));
|
||||
|
||||
this.set('podCount', podCount);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import Cattle from 'ui/utils/cattle';
|
||||
|
||||
var LoadBalancerServiceController = Cattle.TransitioningResourceController.extend({
|
||||
actions: {
|
||||
activate: function() {
|
||||
return this.doAction('activate');
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
return this.doAction('deactivate');
|
||||
},
|
||||
|
||||
edit: function() {
|
||||
this.transitionToRoute('service.edit', this.get('id'));
|
||||
},
|
||||
|
||||
scaleUp: function() {
|
||||
this.incrementProperty('scale');
|
||||
return this.save();
|
||||
}
|
||||
},
|
||||
|
||||
availableActions: function() {
|
||||
|
||||
var a = this.get('actions');
|
||||
|
||||
var choices = [
|
||||
{ label: 'Start', icon: 'ss-play', action: 'activate', enabled: !!a.activate, color: 'text-success'},
|
||||
{ label: 'Stop', icon: 'ss-pause', action: 'deactivate', enabled: !!a.deactivate, color: 'text-danger'},
|
||||
{ label: 'Delete', icon: 'ss-trash', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete', color: 'text-warning' },
|
||||
{ label: 'Purge', icon: 'ss-tornado', action: 'purge', enabled: !!a.purge },
|
||||
{ divider: true },
|
||||
{ label: 'View in API', icon: '', action: 'goToApi', enabled: true},
|
||||
{ divider: true },
|
||||
{ label: 'Edit', icon: 'ss-write', action: 'edit', enabled: !!a.update },
|
||||
];
|
||||
|
||||
return choices;
|
||||
}.property('actions.{activate,deactivate,update,remove,purge}'),
|
||||
|
||||
getEnvironment: function() {
|
||||
return this.get('store').find('environment', this.get('environmentId'));
|
||||
},
|
||||
});
|
||||
|
||||
LoadBalancerServiceController.reopenClass({
|
||||
stateMap: {
|
||||
'requested': {icon: 'ss-tag', color: 'text-danger'},
|
||||
'registering': {icon: 'ss-tag', color: 'text-danger'},
|
||||
'activating': {icon: 'ss-tag', color: 'text-danger'},
|
||||
'active': {icon: 'ss-layergroup', color: 'text-success'},
|
||||
'deactivating': {icon: 'ss-down', color: 'text-danger'},
|
||||
'inactive': {icon: 'fa fa-circle', color: 'text-danger'},
|
||||
'removing': {icon: 'ss-trash', color: 'text-danger'},
|
||||
'removed': {icon: 'ss-trash', color: 'text-danger'},
|
||||
}
|
||||
});
|
||||
|
||||
export default LoadBalancerServiceController;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import Cattle from 'ui/utils/cattle';
|
||||
|
||||
var LoadBalancerService = Cattle.TransitioningResource.extend({
|
||||
type: 'service',
|
||||
|
||||
consumedServicesUpdated: 0,
|
||||
onConsumedServicesChanged: function() {
|
||||
this.incrementProperty('consumedServicesUpdated');
|
||||
}.observes('consumedservices.@each.{id,name,state}'),
|
||||
});
|
||||
|
||||
LoadBalancerService.reopenClass({
|
||||
});
|
||||
|
||||
export default LoadBalancerService;
|
||||
|
|
@ -112,6 +112,7 @@ Router.map(function() {
|
|||
|
||||
this.resource('environments.new', {path: '/environments/add'});
|
||||
this.resource('service.new', {path: '/environments/add-service'});
|
||||
this.resource('service.new-balancer', {path: '/environments/add-balancer'});
|
||||
this.resource('environments', {path: '/environments'}, function() {
|
||||
this.route('index', {path: '/'});
|
||||
this.resource('environment', {path: '/:environment_id'}, function() {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ ServiceController.reopenClass({
|
|||
'registering': {icon: 'ss-tag', color: 'text-danger'},
|
||||
'activating': {icon: 'ss-tag', color: 'text-danger'},
|
||||
'active': {icon: 'ss-layergroup', color: 'text-success'},
|
||||
'updating-active': {icon: 'ss-tag', color: 'text-success'},
|
||||
'updating-inactive':{icon: 'ss-tag', color: 'text-danger'},
|
||||
'deactivating': {icon: 'ss-down', color: 'text-danger'},
|
||||
'inactive': {icon: 'fa fa-circle', color: 'text-danger'},
|
||||
'removing': {icon: 'ss-trash', color: 'text-danger'},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
import Ember from 'ember';
|
||||
import Cattle from 'ui/utils/cattle';
|
||||
import EditLoadBalancerConfig from 'ui/mixins/edit-loadbalancerconfig';
|
||||
|
||||
export default Ember.ObjectController.extend(Cattle.NewOrEditMixin, EditLoadBalancerConfig, {
|
||||
queryParams: ['environmentId','tab'],
|
||||
environmentId: null,
|
||||
tab: 'listeners',
|
||||
error: null,
|
||||
editing: false,
|
||||
primaryResource: Ember.computed.alias('model.balancer'),
|
||||
|
||||
actions: {
|
||||
addTargetService: function() {
|
||||
this.get('targetsArray').pushObject({isService: true, value: null});
|
||||
},
|
||||
removeTarget: function(obj) {
|
||||
this.get('targetsArray').removeObject(obj);
|
||||
},
|
||||
},
|
||||
|
||||
initFields: function() {
|
||||
this._super();
|
||||
this.set('targetsArray', [{isService: true, value: null}]);
|
||||
this.set('listenersArray', [
|
||||
this.get('store').createRecord({
|
||||
type: 'loadBalancerListener',
|
||||
name: 'uilistener',
|
||||
sourcePort: '',
|
||||
sourceProtocol: 'http',
|
||||
targetPort: '',
|
||||
targetProtocol: 'http',
|
||||
algorithm: 'roundrobin',
|
||||
})
|
||||
]);
|
||||
this.initUri();
|
||||
},
|
||||
|
||||
useExisting: 'no',
|
||||
isUseExisting: Ember.computed.equal('useExisting','yes'),
|
||||
hasNoExisting: Ember.computed.equal('activeConfigs.length',0),
|
||||
existingConfigId: null,
|
||||
|
||||
initHosts: function() {
|
||||
},
|
||||
hostDisabled: Ember.computed.equal('hostChoices.length',0),
|
||||
hostChoices: function() {
|
||||
return this.get('allHosts').filter((host) => {
|
||||
return host.get('state') === 'active';
|
||||
}).sortBy('name','id');
|
||||
}.property('allHosts.@each.{id,name,state}'),
|
||||
|
||||
targetsArray: null,
|
||||
targetServiceIds: function() {
|
||||
return this.get('targetsArray').filterProperty('isService',true).filterProperty('value').map((choice) => {
|
||||
return Ember.get(choice,'value');
|
||||
}).uniq();
|
||||
}.property('targetsArray.@each.{isService,value}'),
|
||||
|
||||
targetChoices: function() {
|
||||
var list = [];
|
||||
var env = this.get('environment');
|
||||
var envName = env.get('name') || ('(Environment '+env.get('id')+')');
|
||||
|
||||
env.get('services').map((service) => {
|
||||
list.pushObject({
|
||||
group: 'Environment: ' + envName,
|
||||
id: service.get('id'),
|
||||
name: service.get('name') || ('(' + service.get('id') + ')')
|
||||
});
|
||||
});
|
||||
|
||||
return list.sortBy('group','name','id');
|
||||
}.property('environment.services.@each.{name,id},environment.{name,id}').volatile(),
|
||||
|
||||
activeConfigs: function() {
|
||||
return this.get('allConfigs').filter((config) => {
|
||||
return config.get('state') === 'active';
|
||||
});
|
||||
}.property('allConfigs.@each.state'),
|
||||
|
||||
validate: function() {
|
||||
this._super();
|
||||
var errors = this.get('errors')||[];
|
||||
|
||||
if ( !this.get('targetServiceIds.length') )
|
||||
{
|
||||
errors.push('Choose one or more targets to send traffic to');
|
||||
}
|
||||
|
||||
if (!this.get('listenersArray.length') )
|
||||
{
|
||||
errors.push('One or more listening ports are required');
|
||||
}
|
||||
|
||||
errors.pushObjects(this.get('config').validationErrors());
|
||||
this.get('listenersArray').forEach((listener) => {
|
||||
errors.pushObjects(listener.validationErrors());
|
||||
});
|
||||
|
||||
if ( (this.get('listenersArray')||[]).filterProperty('sourcePort',8080).get('length') > 0 )
|
||||
{
|
||||
errors.push('Port 8080 cannot currently be used as a source port');
|
||||
}
|
||||
|
||||
errors.pushObjects(this.get('balancer').validationErrors());
|
||||
|
||||
if ( errors.length )
|
||||
{
|
||||
this.set('errors',errors.uniq());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
has8080: function() {
|
||||
// The port might be an int or a string due to validation..
|
||||
return ( (this.get('listenersArray')||[]).filterProperty('sourcePort','8080').get('length') > 0 ) ||
|
||||
( (this.get('listenersArray')||[]).filterProperty('sourcePort',8080).get('length') > 0 );
|
||||
}.property('listenersArray.@each.sourcePort'),
|
||||
|
||||
listenersChanged: function() {
|
||||
var list = [];
|
||||
this.get('listenersArray').forEach(function(listener) {
|
||||
var src = listener.get('sourcePort');
|
||||
var proto = listener.get('sourceProtocol');
|
||||
var tgt = listener.get('targetPort');
|
||||
|
||||
if ( src && proto )
|
||||
{
|
||||
list.pushObject(src + ':' + (tgt ? tgt : src) + (proto === 'http' ? '': '/' + proto ) );
|
||||
}
|
||||
});
|
||||
|
||||
this.set('model.launchConfig.ports', list.sort().uniq());
|
||||
}.observes('listenersArray.@each.{sourcePort,sourceProtocol,targetPort,targetProtocol}'),
|
||||
|
||||
nameChanged: function() {
|
||||
this.set('config.name', this.get('balancer.name') + ' config');
|
||||
}.observes('balancer.name'),
|
||||
|
||||
descriptionChanged: function() {
|
||||
this.set('config.description', this.get('balancer.description'));
|
||||
}.observes('balancer.description'),
|
||||
|
||||
didSave: function() {
|
||||
var balancer = this.get('model.balancer');
|
||||
// Set balancer targets
|
||||
return balancer.waitForNotTransitioning().then(() => {
|
||||
return balancer.doAction('setservicelinks', {
|
||||
serviceIds: this.get('targetServiceIds'),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
doneSaving: function() {
|
||||
this.transitionToRoute('environment', this.get('environment.id'));
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import AuthenticatedRouteMixin from 'ui/mixins/authenticated-route';
|
||||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||
model: function(params/*, transition*/) {
|
||||
var store = this.get('store');
|
||||
|
||||
var dependencies = [
|
||||
store.findAll('host'),
|
||||
store.find('environment', params.environmentId).then(function(env) {
|
||||
return env.importLink('services');
|
||||
})
|
||||
];
|
||||
|
||||
return Ember.RSVP.all(dependencies, 'Load dependencies').then(function(results) {
|
||||
var allHosts = results[0];
|
||||
var environment = results[1];
|
||||
|
||||
var launchConfig = store.createRecord({
|
||||
type: 'container',
|
||||
});
|
||||
|
||||
var lbConfig = store.createRecord({
|
||||
type: 'loadBalancerConfig',
|
||||
healthCheck: store.createRecord({
|
||||
type: 'loadBalancerHealthCheck',
|
||||
interval: 2000,
|
||||
responseTimeout: 2000,
|
||||
healthyThreshold: 2,
|
||||
unhealthyThreshold: 3,
|
||||
requestLine: null,
|
||||
}),
|
||||
appCookieStickinessPolicy: null,
|
||||
lbCookieStickinessPolicy: null,
|
||||
});
|
||||
|
||||
return {
|
||||
isService: true,
|
||||
allHosts: allHosts,
|
||||
environment: environment,
|
||||
balancer: store.createRecord({
|
||||
type: 'loadBalancerService',
|
||||
name: '',
|
||||
description: '',
|
||||
scale: 1,
|
||||
environmentId: environment.get('id'),
|
||||
launchConfig: launchConfig,
|
||||
loadBalancerConfig: lbConfig,
|
||||
}),
|
||||
config: lbConfig,
|
||||
launchConfig: launchConfig,
|
||||
appCookie: store.createRecord({
|
||||
type: 'loadBalancerAppCookieStickinessPolicy',
|
||||
mode: 'path_parameters',
|
||||
requestLearn: true,
|
||||
prefix: false,
|
||||
timeout: 3600000,
|
||||
maxLength: 1024,
|
||||
}),
|
||||
lbCookie: store.createRecord({
|
||||
type: 'loadBalancerCookieStickinessPolicy'
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('model',model);
|
||||
controller.initFields();
|
||||
},
|
||||
|
||||
resetController: function (controller, isExisting/*, transition*/) {
|
||||
if (isExisting)
|
||||
{
|
||||
controller.set('tab', 'listeners');
|
||||
controller.set('stickiness', 'none');
|
||||
}
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.send('setPageLayout', {label: 'Back', backPrevious: true});
|
||||
},
|
||||
|
||||
actions: {
|
||||
cancel: function() {
|
||||
this.transitionTo('loadbalancers');
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<section class="horizontal-form container-fluid">
|
||||
<h2>Add Load Balancer</h2>
|
||||
{{top-errors errors=errors}}
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-sm-12 col-md-2 form-label">
|
||||
<label for="name">Name</label>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-8">
|
||||
{{input id="name" type="text" value=balancer.name classNames="form-control" placeholder="e.g. Website"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-sm-12 col-md-2 form-label">
|
||||
<label for="description">Description</label>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-8">
|
||||
{{textarea id="description" value=balancer.description classNames="form-control no-resize" rows="5" placeholder="e.g. Balancer for mycompany.com"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row form-group">
|
||||
<div class="col-sm-12 col-md-2 form-label">
|
||||
<label>Scale</label>
|
||||
</div>
|
||||
<div class="col-sm-2 col-md-1">
|
||||
{{balancer.scale}}
|
||||
</div>
|
||||
<div class="col-sm-10 col-md-7">
|
||||
{{input-slider value=balancer.scale valueMin=1 valueMax=10 scaleMin=0}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{partial "form-divider"}}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-md-2 form-label">
|
||||
<label>Targets</label>
|
||||
</div>
|
||||
<div class="col-xs-6 col-md-8">
|
||||
<button class="btn-circle-plus btn-circle-text" style="margin-right: 20px;" {{action "addTargetService" target="view"}}>Add Service</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-8 col-md-offset-2">
|
||||
{{partial "loadbalancer/edit-targets"}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{partial "form-divider"}}
|
||||
|
||||
{{partial "loadbalancer/edit-config"}}
|
||||
|
||||
{{partial "save-cancel"}}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
function addAction(action, selector) {
|
||||
return function() {
|
||||
this.get('controller').send(action);
|
||||
Ember.run.next(this, function() {
|
||||
this.$(selector).last().focus();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default Ember.View.extend({
|
||||
actions: {
|
||||
addTargetService: addAction('addTargetService', '.lb-target'),
|
||||
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() {
|
||||
$('BODY').addClass('white');
|
||||
this._super();
|
||||
this.send('selectTab',this.get('context.tab'));
|
||||
|
||||
this.$('INPUT')[0].focus();
|
||||
},
|
||||
|
||||
willDestroyElement: function() {
|
||||
$('BODY').removeClass('white');
|
||||
},
|
||||
});
|
||||
|
|
@ -6,15 +6,27 @@
|
|||
{{#if tgt.isIp}}
|
||||
{{input type="text" class="form-control input-sm lb-target" value=tgt.value placeholder="e.g. 192.0.2.24"}}
|
||||
{{else}}
|
||||
{{display-name-select
|
||||
classNames="form-control input-sm lb-target"
|
||||
prompt="Select a container..."
|
||||
value=tgt.value
|
||||
content=targetChoices
|
||||
optionValuePath="content.id"
|
||||
optionLabelPath="content.name"
|
||||
optionGroupPath="group"
|
||||
}}
|
||||
{{#if model.isService}}
|
||||
{{display-name-select
|
||||
classNames="form-control input-sm lb-target"
|
||||
prompt="Select a service..."
|
||||
value=tgt.value
|
||||
content=targetChoices
|
||||
optionValuePath="content.id"
|
||||
optionLabelPath="content.name"
|
||||
optionGroupPath="group"
|
||||
}}
|
||||
{{else}}
|
||||
{{display-name-select
|
||||
classNames="form-control input-sm lb-target"
|
||||
prompt="Select a container..."
|
||||
value=tgt.value
|
||||
content=targetChoices
|
||||
optionValuePath="content.id"
|
||||
optionLabelPath="content.name"
|
||||
optionGroupPath="group"
|
||||
}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td width="30" class="text-right">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<th width="30"></th>
|
||||
<th>Target</th>
|
||||
<th width="30"></th>
|
||||
<th>Algorithm</th>
|
||||
<th width="100">Algorithm</th>
|
||||
<th width="40"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -33,35 +33,43 @@
|
|||
</div>
|
||||
</td>
|
||||
|
||||
<td class="text-center"><i class="ss-right"></i></td>
|
||||
<td class="text-center"><div class="form-control-static input-sm"><i class="ss-right"></i></div></td>
|
||||
|
||||
<td>
|
||||
<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"}}
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="border-left: 0;">/{{listener.targetProtocol}} <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li role="presentation" class="dropdown-header">
|
||||
Select a protocol:
|
||||
</li>
|
||||
{{#each choice in targetProtocolOptions}}
|
||||
<li {{action "chooseProtocol" listener "targetProtocol" choice}}>
|
||||
<a>{{choice}}</a>
|
||||
{{#if model.isService}}
|
||||
<span class="input-group-addon">/{{listener.sourceProtocol}}</span>
|
||||
{{else}}
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="border-left: 0;">/{{listener.targetProtocol}} <span class="caret"></span></button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li role="presentation" class="dropdown-header">
|
||||
Select a protocol:
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{#each choice in targetProtocolOptions}}
|
||||
<li {{action "chooseProtocol" listener "targetProtocol" choice}}>
|
||||
<a>{{choice}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td> </td>
|
||||
|
||||
<td>
|
||||
{{view "select"
|
||||
class="form-control input-sm"
|
||||
value=listener.algorithm
|
||||
content=algorithmOptions
|
||||
}}
|
||||
{{#if model.isService}}
|
||||
<div class="form-control-static input-sm">Round Robin</div>
|
||||
{{else}}
|
||||
{{view "select"
|
||||
class="form-control input-sm"
|
||||
value=listener.algorithm
|
||||
content=algorithmOptions
|
||||
}}
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<button {{action "removeListener" listener}} class="btn-circle-x" type="button" tabindex="-1"></button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "ui",
|
||||
"version": "0.18.2",
|
||||
"version": "0.19.0",
|
||||
"private": true,
|
||||
"directories": {
|
||||
"doc": "doc",
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
"devDependencies": {
|
||||
"broccoli-asset-rev": "^2.0.0",
|
||||
"broccoli-sass": "0.6.2",
|
||||
"ember-api-store": "1.0.17",
|
||||
"ember-api-store": "^1.0.17",
|
||||
"ember-browserify": "^0.6.4",
|
||||
"ember-cli": "0.2.0",
|
||||
"ember-cli-app-version": "0.3.3",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="103.7 26 100 83.2" enable-background="new 103.7 26 100 83.2" xml:space="preserve">
|
||||
<path fill="#3D8ECB" d="M148.5,26.9c4.3-2.3,9.5,0.5,11.7,4.4c1.5,2.4,2,5.3,2.5,8c-1.3,0-2.5,0.1-3.8,0.1c-0.6-2.9-1.3-6.2-3.8-8
|
||||
c-2-1.7-5.3-1.5-7.1,0.5c-2.2,1.9-2.7,4.9-3.2,7.6c-1.3,0-2.6-0.1-3.8-0.1C141.7,34.4,143.6,29,148.5,26.9z"/>
|
||||
<path fill="#3D8ECB" d="M135.4,41.6c1.1-0.6,2.7-0.3,3.9-0.3c0.1,9.2,0.1,18.4,0,27.6c-1.3,0-2.6,0-3.9-0.2
|
||||
c-0.4-5.5-0.1-11-0.1-16.4C135.3,48.7,135,45.1,135.4,41.6z"/>
|
||||
<path fill="#3D8ECB" d="M141.3,41.3c1.3,0,2.6,0,3.9,0c0.3,9.2,0.3,18.5-0.1,27.7c-1.3-0.1-2.6-0.1-3.9-0.2
|
||||
C141.3,59.5,141.2,50.4,141.3,41.3z"/>
|
||||
<path fill="#3D8ECB" d="M147.1,41.3c1.3-0.1,2.5-0.1,3.7-0.2c0.1,2.7-0.1,8.3-0.1,8.3l-3.7-0.1C147,49.2,146.7,44,147.1,41.3z"/>
|
||||
<path fill="#3D8ECB" d="M152.9,41.3c1.3-0.1,2.7-0.1,4.1-0.1c0.1,2.8-0.1,8.5-0.1,8.5l-4-0.4C152.9,49.2,152.9,44,152.9,41.3z"/>
|
||||
<path fill="#3D8ECB" d="M158.7,41.3c1.3-0.1,2.5-0.1,3.8-0.1c0,9.2-0.1,27.6-0.1,27.6l-4.1-0.1c0,0,0-15.2-0.1-22.8
|
||||
C158.4,44.4,158.3,42.8,158.7,41.3z"/>
|
||||
<path fill="#3D8ECB" d="M164.7,41.3l3.9-0.1v27.6l-3.8-0.1l-0.2-17.1L164.7,41.3z"/>
|
||||
<path fill="#3D8ECB" d="M124.8,84.2c-0.1-3.8-0.8-8.7-4.6-10.6c-3.6-1.3-7.6-1.2-11.2-0.2c-3,1-4.5,4.3-4.8,7.2
|
||||
c-0.6,4.4-0.7,9,0.5,13.3c0.7,2.9,3.3,5,6.2,5.2c3.6,0.3,7.9,0.6,10.9-1.8C125.2,93.9,124.8,88.6,124.8,84.2z M119.8,89
|
||||
c-0.1,2.2-0.8,5.1-3.4,5.6c-2.4,0.3-6.1,0.6-7-2.3c-1-3.7-0.8-7.6-0.2-11.3c0.3-1.5,1.1-3,2.6-3.4c2.3-0.3,5.5-0.8,7,1.5
|
||||
C120.1,82.2,119.8,85.7,119.8,89z"/>
|
||||
<path fill="#3D8ECB" d="M127.5,80.2c1.7,0,3.4,0,5,0c0.2,4.8-0.5,9.7,0.3,14.4c1.7,1.9,4.5,0.2,6.4-0.6c0-4.6,0-9.2,0-13.8
|
||||
c1.7,0,3.4,0,5,0c0,6.3,0,12.7,0,19c-1.3,0-2.7,0-4.1,0c-0.3-0.6,0.3-2.5-0.8-2c-1.2,0.6-5,2.9-7.8,2.1c-2.4-0.5-3.8-2.9-3.9-5.2
|
||||
C127.3,89.5,127.5,84.8,127.5,80.2z"/>
|
||||
<path fill="#3D8ECB" d="M164.7,80.2c1.7,0,3.6,0,5.3,0c1,3.9,2,7.9,2.9,11.8c0.2,1.3,1.1,2.4,2,3.4c1.5-5,2.7-10.1,4.1-15.2
|
||||
c1.7,0,3.5,0,5.2,0c-2,7.7-4.1,15.5-6.1,23.1c-0.6,2.2-2,4-3.2,5.9c-1.2,0-2.4-0.1-3.6-0.1c0.9-3.2,1.7-6.4,2.7-9.6
|
||||
c-2.2-0.7-4.6-1.7-5.2-4.1C167.3,90.4,166.2,85.3,164.7,80.2z"/>
|
||||
<path fill="#3D8ECB" d="M150.6,51.1l3.3,0l0.6,8.1l-5.1,0.1L150.6,51.1z"/>
|
||||
<path fill="#C0C2C4" d="M189.7,85.3c1,0,2,0.1,2.9,0.1c0,0.6,0,2,0,2.6c-1,0-2,0-2.9,0C189.7,87.4,189.7,86,189.7,85.3z"/>
|
||||
<path fill="#BDBFC1" d="M189.8,89.1c0.9,0,1.8,0,2.7,0c0,3.4,0,6.7,0,10.1c-0.9,0-1.8,0-2.7,0C189.8,95.9,189.8,92.5,189.8,89.1z"/>
|
||||
<path fill="#3D8ECB" d="M163.1,85c0-2.1-1.3-4.2-3.4-4.8c-4-1.1-8.2-0.3-12.2,0.6c0,1-0.1,2.2-0.1,3.2c3.1-0.1,6.2-0.6,9.3-0.2
|
||||
c1.5,0.2,1,2.3,1.5,3.4c-3.4,0.3-7.8-1-10.4,1.8c-1.9,3.1-1.7,8.4,2.1,9.9c2.9,1.3,7.1-1,8.3-1.6c1.2-0.6,0.6,1.2,0.8,1.8
|
||||
c1.4,0,2.7,0,4.1,0C163.1,94.5,163.4,89.7,163.1,85z M158.1,94.1c-2,0.8-4.1,1.3-6.2,1c0-0.6,0-1.3,0.1-2.1c0.2-0.8,0.4-1.3,0.6-1.7
|
||||
c0.7-0.3,1.6-0.6,2.7-0.8s2-0.3,2.8-0.2C158.1,91.6,158.1,92.9,158.1,94.1z"/>
|
||||
<path fill="#C0C2C4" d="M203.4,91.9c-0.9-3.1-5-3.6-7.6-2.3c-2.3,1.3-1.9,4.4-1.6,6.6c0.3,3.2,4.3,3.7,6.9,2.9
|
||||
C204,98.1,204,94.3,203.4,91.9z M200.5,96.7c-0.1,0.1-0.8,0.7-1.7,0.6c-1.2-0.1-1.7-1-1.8-1.1c-0.1-0.6-0.3-1.5-0.2-2.7
|
||||
c0.1-0.9,0.3-1.6,0.6-2.1c0.2-0.1,2-0.8,2.8,0c0.1,0.1,0.3,0.3,0.4,0.6c0.2,0.5,0.5,1.3,0.5,2.4S200.7,96.3,200.5,96.7z"/>
|
||||
<path fill="#3D8ECB" d="M146.9,60.5c1.3,0,2.6,0,3.9,0c0.1,2.8,0.1,5.6,0,8.4c-1.3,0-2.5,0-3.8-0.2C146.6,66,146.8,63.2,146.9,60.5z
|
||||
"/>
|
||||
<path fill="#3D8ECB" d="M152.9,60.5l4-0.1v8.4l-4-0.1V60.5z"/>
|
||||
<path fill="#C2C4C6" d="M185.1,96.7c0.9,0,1.7,0,2.7,0c0,0.7,0.1,2,0.1,2.7c-0.9-0.1-1.8-0.1-2.7-0.1
|
||||
C185.1,98.5,185.1,97.3,185.1,96.7z"/>
|
||||
<path fill="#3D8ECB" d="M112.4,100.5c0.9,0,1.9,0,2.8,0c1.8,1.3,4.1,1.3,6.2,1.1c0,1.3,0,2.7,0,4.1c-2.2-0.1-4.5,0.1-6.4-1.1
|
||||
C113.7,103.6,113.1,101.9,112.4,100.5z"/>
|
||||
<path fill="#FFFFFF" d="M159.3,97.4"/>
|
||||
<path fill="#FFFFFF" d="M158.8,97.3"/>
|
||||
<path fill="#FFFFFF" d="M151.3,92.9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
Loading…
Reference in New Issue