bug 11798 (WIP)

-Add UI for sticky policy

-Create sticky policy functionality
This commit is contained in:
bfederle 2012-01-20 14:30:24 -08:00
parent 79e9e292dd
commit 3e365ad8c2
6 changed files with 444 additions and 39 deletions

View File

@ -5646,6 +5646,8 @@ div.panel.ui-dialog div.list-view div.fixed-header {
-webkit-border-radius: 5px;
-khtml-border-radius: 5px;
border-radius: 5px 5px 5px 5px;
width: 74px;
text-align: center;
padding: 6px 9px 4px 0px;
background: url(../images/bg-gradients.png) repeat-x 0px -220px;
/*+placement:shift 4px 0px;*/
@ -5663,6 +5665,28 @@ div.panel.ui-dialog div.list-view div.fixed-header {
box-shadow: inset 0px 1px 1px #000000;
}
.multi-edit .button.custom-action {
background: url(../images/bg-gradients.png) 0px -271px;
border: 1px solid #B7B7B7;
color: #485867;
font-size: 11px;
/*+text-shadow:0px 1px #FFFFFF;*/
-moz-text-shadow: 0px 1px #FFFFFF;
-webkit-text-shadow: 0px 1px #FFFFFF;
-o-text-shadow: 0px 1px #FFFFFF;
text-shadow: 0px 1px #FFFFFF;
}
.multi-edit .button.custom-action:hover {
background: #808080 url(../images/bg-gradients.png);
color: #FFFFFF;
/*+text-shadow:0px 1px 1px #000000;*/
-moz-text-shadow: 0px 1px 1px #000000;
-webkit-text-shadow: 0px 1px 1px #000000;
-o-text-shadow: 0px 1px 1px #000000;
text-shadow: 0px 1px 1px #000000;
}
.multi-edit-add-list .ui-button.ok,
.multi-edit-add-list .ui-button.cancel {
float: right;
@ -5791,6 +5815,10 @@ div.panel.ui-dialog div.list-view div.fixed-header {
cursor: pointer;
}
.multi-edit .data .data-body .data-item tr td .custom-action {
margin: -6px 0 0 -1px;
}
.multi-edit .data .data-body .data-item tr td.add-vm:hover {
color: #0060FF;
font-weight: bold;

View File

@ -21,7 +21,7 @@
vlan: { label: 'VLAN' }
},
dataProvider: testData.dataProvider.listView('networks'),
detailView: {
name: 'Network details',
viewAll: { path: 'network.ipAddresses', label: 'IP Addresses' },
@ -513,6 +513,124 @@
});
}
},
'sticky': {
label: 'Sticky Policy',
custom: {
buttonLabel: 'Configure',
action: function(args) {
var success = args.response.success;
var fields = {
method: {
label: 'Stickiness method',
select: function(args) {
var $select = args.$select;
var $form = $select.closest('form');
args.response.success({
data: [
{
id: 'none',
description: 'None'
},
{
id: 'lb',
description: 'LB-based'
},
{
id: 'cookie',
description: 'Cookie-based'
},
{
id: 'source',
description: 'Source-based'
}
]
}, 500);
$select.change(function() {
var value = $select.val();
var showFields = [];
switch (value) {
case 'none':
showFields = [];
break;
case 'lb':
showFields = ['name', 'mode', 'nocache', 'indirect', 'postonly', 'domain'];
break;
case 'cookie':
showFields = ['name', 'length', 'holdtime', 'request-learn', 'prefix', 'mode'];
break;
case 'source':
showFields = ['tablesize', 'expire'];
break;
}
$select.closest('.form-item').siblings('.form-item').each(function() {
var $field = $(this);
var id = $field.attr('rel');
if ($.inArray(id, showFields) > -1) {
$field.css('display', 'inline-block');
} else {
$field.hide();
}
});
$select.closest(':ui-dialog').dialog('option', 'position', 'center');
});
}
},
name: { label: 'Name', validation: { required: true }, isHidden: true },
mode: { label: 'Mode', isHidden: true },
length: { label: 'Length', validation: { required: true }, isHidden: true },
holdtime: { label: 'Hold Time', validation: { required: true }, isHidden: true },
tablesize: { label: 'Table size', isHidden: true },
expire: { label: 'Expire', isHidden: true },
requestlearn: { label: 'Request-Learn', isBoolean: true, isHidden: true },
prefix: { label: 'Prefix', isBoolean: true, isHidden: true },
nocache: { label: 'No cache', isBoolean: true, isHidden: true },
indirect: { label: 'Indirect', isBoolean: true, isHidden: true },
postonly: { label: 'Is post-only', isBoolean: true, isHidden: true },
domain: { label: 'Domain', isBoolean: true, isHidden: true }
};
if (args.data) {
var populatedFields = $.map(fields, function(field, id) {
return id;
});
$(populatedFields).each(function() {
var id = this;
var field = fields[id];
var dataItem = args.data[id];
if (field.isBoolean) {
field.isChecked = dataItem ? true : false;
} else {
field.defaultValue = dataItem;
}
});
}
cloudStack.dialog.createForm({
form: {
title: 'Configure Sticky Policy',
desc: 'Please complete the following fields',
fields: fields
},
after: function(args) {
var data = cloudStack.serializeForm(args.$form);
success({
data: $.extend(data, {
_buttonLabel: data.method.toUpperCase()
})
});
}
});
}
}
},
'add-vm': {
label: 'Add VMs',
addButton: true
@ -569,7 +687,17 @@
testData.data.instances[1],
testData.data.instances[2],
testData.data.instances[3]
]
],
sticky: {
_buttonLabel: 'lb'.toUpperCase(),
method: 'lb',
name: 'StickyTest',
mode: '123',
nocache: true,
indirect: false,
postonly: true,
domain: false
}
}
]
});
@ -864,9 +992,9 @@
'icmptype': { edit: true, label: 'ICMP Type', isHidden: true },
'icmpcode': { edit: true, label: 'ICMP Code', isHidden: true },
'cidr': { edit: true, label: 'CIDR', isHidden: true },
'accountname': {
edit: true,
label: 'Account, Security Group',
'accountname': {
edit: true,
label: 'Account, Security Group',
isHidden: true,
range: ['accountname', 'securitygroupname']
},

View File

@ -1507,6 +1507,124 @@
});
}
},
'sticky': {
label: 'Sticky Policy',
custom: {
buttonLabel: 'Configure',
action: function(args) {
var success = args.response.success;
var fields = {
methodname: {
label: 'Stickiness method',
select: function(args) {
var $select = args.$select;
var $form = $select.closest('form');
args.response.success({
data: [
{
id: 'none',
description: 'None'
},
{
id: 'LbCookie',
description: 'LB-based'
},
{
id: 'AppCookie',
description: 'Cookie-based'
},
{
id: 'SourceBased',
description: 'Source-based'
}
]
}, 500);
$select.change(function() {
var value = $select.val();
var showFields = [];
switch (value) {
case 'none':
showFields = [];
break;
case 'LbCookie':
showFields = ['name', 'mode', 'nocache', 'indirect', 'postonly', 'domain'];
break;
case 'AppCookie':
showFields = ['name', 'length', 'holdtime', 'request-learn', 'prefix', 'mode'];
break;
case 'SourceBased':
showFields = ['tablesize', 'expire'];
break;
}
$select.closest('.form-item').siblings('.form-item').each(function() {
var $field = $(this);
var id = $field.attr('rel');
if ($.inArray(id, showFields) > -1) {
$field.css('display', 'inline-block');
} else {
$field.hide();
}
});
$select.closest(':ui-dialog').dialog('option', 'position', 'center');
});
}
},
name: { label: 'Name', validation: { required: true }, isHidden: true },
mode: { label: 'Mode', isHidden: true },
length: { label: 'Length', validation: { required: true }, isHidden: true },
holdtime: { label: 'Hold Time', validation: { required: true }, isHidden: true },
tablesize: { label: 'Table size', isHidden: true },
expire: { label: 'Expire', isHidden: true },
requestlearn: { label: 'Request-Learn', isBoolean: true, isHidden: true },
prefix: { label: 'Prefix', isBoolean: true, isHidden: true },
nocache: { label: 'No cache', isBoolean: true, isHidden: true },
indirect: { label: 'Indirect', isBoolean: true, isHidden: true },
postonly: { label: 'Is post-only', isBoolean: true, isHidden: true },
domain: { label: 'Domain', isBoolean: true, isHidden: true }
};
if (args.data) {
var populatedFields = $.map(fields, function(field, id) {
return id;
});
$(populatedFields).each(function() {
var id = this;
var field = fields[id];
var dataItem = args.data[id];
if (field.isBoolean) {
field.isChecked = dataItem ? true : false;
} else {
field.defaultValue = dataItem;
}
});
}
cloudStack.dialog.createForm({
form: {
title: 'Configure Sticky Policy',
desc: 'Please complete the following fields',
fields: fields
},
after: function(args) {
var data = cloudStack.serializeForm(args.$form);
success({
data: $.extend(data, {
_buttonLabel: data.methodname.toUpperCase()
})
});
}
});
}
}
},
'add-vm': {
label: 'Add VMs',
addButton: true
@ -1516,13 +1634,20 @@
label: 'Add VMs',
action: function(args) {
var openFirewall = g_firewallRuleUiEnabled == "true" ? false : true;
var data = {
algorithm: args.data.algorithm,
name: args.data.name,
privateport: args.data.privateport,
publicport: args.data.publicport
};
var stickyData = $.extend(true, {}, args.data.sticky);
$.ajax({
url: createURL('createLoadBalancerRule'),
data: $.extend(args.data, {
data: $.extend(data, {
openfirewall: openFirewall,
publicipid: args.context.ipAddresses[0].id
publicipid: args.context.ipAddresses[0].id,
networkid: args.context.networks[0].id
}),
dataType: 'json',
async: true,
@ -1547,7 +1672,73 @@
},
notification: {
label: 'Add load balancer rule',
poll: pollAsyncJobResult
poll: function(args) {
var complete = args.complete;
var error = args.error;
pollAsyncJobResult({
_custom: args._custom,
complete: function(args) {
// Create stickiness policy
if (stickyData && stickyData.methodname != 'none') {
var stickyURLData = '';
var stickyParams;
switch (stickyData.methodname) {
case 'LbCookie':
stickyParams = ['name', 'mode', 'nocache', 'indirect', 'postonly', 'domain'];
break;
case 'AppCookie':
stickyParams = ['name', 'length', 'holdtime', 'request-learn', 'prefix', 'mode'];
break;
case 'SourceBased':
stickyParams = ['tablesize', 'expire'];
break;
}
$(stickyParams).each(function(index) {
var param = '&param[' + index + ']';
var name = this;
var value = stickyData[name];
if (!value) return true;
if (value == 'on') value = true;
stickyURLData += param + '.name=' + name + param + '.value=' + value;
});
$.ajax({
url: createURL('createLBStickinessPolicy' + stickyURLData),
data: {
lbruleid: args.data.loadbalancer.id,
name: stickyData.name,
methodname: stickyData.methodname
},
success: function(json) {
var addStickyCheck = setInterval(function() {
pollAsyncJobResult({
_custom: {
jobId: json.createLBStickinessPolicy.jobid,
},
complete: function(args) {
clearInterval(addStickyCheck);
complete();
},
error: function(args) {
clearInterval(addStickyCheck);
}
});
}, 1000);
},
error: error
});
} else {
complete();
}
},
error: error
});
}
}
});
},

View File

@ -56,7 +56,7 @@ var pollAsyncJobResult = function(args) {
});
}
else {
args.complete();
args.complete({ data: json.queryasyncjobresultresponse.jobresult });
}
}
else if (result.jobstatus == 2) { // Failed

View File

@ -36,30 +36,36 @@
.appendTo($form);
// Render fields and events
$.each(args.form.fields, function(key, field) {
var fields = $.map(args.form.fields, function(value, key) {
return key;
})
$(fields).each(function() {
var key = this;
var field = args.form.fields[key];
var $formItem = $('<div>')
.addClass('form-item')
.attr({ rel: key });
.addClass('form-item')
.attr({ rel: key });
if (this.hidden || this.isHidden) $formItem.hide();
if (field.hidden || field.isHidden) $formItem.hide();
$formItem.appendTo($form);
// Label field
var $name = $('<div>').addClass('name')
.appendTo($formItem)
.append(
$('<label>').html(this.label + ':')
);
.appendTo($formItem)
.append(
$('<label>').html(field.label + ':')
);
// Input area
var $value = $('<div>').addClass('value')
.appendTo($formItem);
.appendTo($formItem);
var $input, $dependsOn, selectFn, selectArgs;
var dependsOn = this.dependsOn;
var dependsOn = field.dependsOn;
// Depends on fields
if (this.dependsOn) {
if (field.dependsOn) {
$formItem.attr('depends-on', dependsOn);
$dependsOn = $form.find('input, select').filter(function() {
return $(this).attr('name') === dependsOn;
@ -105,7 +111,7 @@
}
// Determine field type of input
if (this.select) {
if (field.select) {
selectArgs = {
context: args.context,
response: {
@ -120,17 +126,21 @@
description = this.description;
var $option = $('<option>')
.appendTo($input)
.val(id)
.html(description);
.appendTo($input)
.val(id)
.html(description);
});
if (field.defaultValue) {
$input.val(field.defaultValue);
}
$input.trigger('change');
}
}
};
selectFn = this.select;
selectFn = field.select;
$input = $('<select>')
.attr({ name: key })
.data('dialog-select-fn', function() {
@ -170,12 +180,12 @@
} else {
selectFn(selectArgs);
}
} else if (this.isBoolean) {
if (this.multiArray) {
} else if (field.isBoolean) {
if (field.multiArray) {
$input = $('<div>')
.addClass('multi-array').addClass(key).appendTo($value);
$.each(this.multiArray, function(itemKey, itemValue) {
$.each(field.multiArray, function(itemKey, itemValue) {
$input.append(
$('<div>').addClass('item')
.append(
@ -191,16 +201,16 @@
} else {
$input = $('<input>').attr({ name: key, type: 'checkbox' }).appendTo($value);
if (this.isChecked) {
if (field.isChecked) {
$input.attr('checked', 'checked');
}
}
} else if (this.dynamic) {
} else if (field.dynamic) {
// Generate a 'sub-create-form' -- append resulting fields
$input = $('<div>').addClass('dynamic-input').appendTo($value);
$form.hide();
this.dynamic({
field.dynamic({
response: {
success: function(args) {
var form = cloudStack.dialog.createForm({
@ -221,18 +231,18 @@
});
} else {
// Text field
if (this.range) {
if (field.range) {
$input = $.merge(
// Range start
$('<input>').attr({
type: 'text',
name: this.range[0]
name: field.range[0]
}),
// Range end
$('<input>').attr({
type: 'text',
name: this.range[1]
name: field.range[1]
})
).appendTo(
$('<div>').addClass('range-edit').appendTo($value)
@ -242,16 +252,16 @@
} else {
$input = $('<input>').attr({
name: key,
type: this.password || this.isPassword ? 'password' : 'text'
type: field.password || field.isPassword ? 'password' : 'text'
}).appendTo($value);
if (this.defaultValue) {
$input.val(this.defaultValue);
if (field.defaultValue) {
$input.val(field.defaultValue);
}
}
}
$input.data('validation-rules', this.validation);
$input.data('validation-rules', field.validation);
$('<label>').addClass('error').appendTo($value).html('*required');
});

View File

@ -70,6 +70,27 @@
_medit.details(itemData[0], $browser, { context: options.context });
}
});
} else if (field.custom) {
$td.data('multi-custom-data', data[fieldName]);
$('<div>').addClass('button add-vm custom-action')
.html(data && data[fieldName] && data[fieldName]['_buttonLabel'] ?
data[fieldName]['_buttonLabel'] : field.custom.buttonLabel)
.click(function() {
var $button = $(this);
field.custom.action({
context: {},
data: $td.data('multi-custom-data'),
response: {
success: function(args) {
if (args.data['_buttonLabel']) {
$button.html(args.data['_buttonLabel']);
}
$td.data('multi-custom-data', args.data)
}
}
})
})
.appendTo($td);
}
};
@ -413,6 +434,20 @@
.attr('disabled', field.isDisabled ? 'disabled' : false)
.appendTo($td);
}
} else if (field.custom) {
$('<div>').addClass('button add-vm custom-action')
.html(field.custom.buttonLabel)
.click(function() {
field.custom.action({
context: context,
data: $td.data('multi-custom-data'),
response: {
success: function(args) {
$td.data('multi-custom-data', args.data);
}
}
})
}).appendTo($td);
} else if (field.addButton) {
$addVM = $('<div>').addClass('button add-vm').html(
args.add.label
@ -483,6 +518,19 @@
}
});
// Append custom data
var $customFields = $multi.find('tbody td').filter(function() {
return $(this).data('multi-custom-data');
});
$customFields.each(function() {
var $field = $(this);
var fieldID = $field.attr('rel');
var fieldData = $field.data('multi-custom-data');
data[fieldID] = fieldData;
});
// Loading appearance
var $loading = _medit.loadingItem($multi, 'Adding...');
$dataBody.prepend($loading);