From 3e4547e998f3d1c55579d73b07ccd46957810782 Mon Sep 17 00:00:00 2001 From: bfederle Date: Tue, 20 Dec 2011 13:50:38 -0800 Subject: [PATCH] Merge changes/fixes reviewed-by: alena Update network section -Implement add (isolated) network action -Update list view/detail view to reflect same data as system area -Misc. CSS fixes for list views --- ui/css/cloudstack3.css | 5 +- ui/scripts-test/installWizard.js | 9 +- ui/scripts/installWizard.js | 70 +++-- ui/scripts/network.js | 414 ++++++++++++++++++++++---- ui/scripts/ui-custom/installWizard.js | 105 +++++-- 5 files changed, 481 insertions(+), 122 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 9ca983990a3..a97bb1bd099 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -1054,6 +1054,7 @@ div.list-view td.state span { -webkit-text-shadow: 0px 1px 1px #FFFFFF; -o-text-shadow: 0px 1px 1px #FFFFFF; text-shadow: 0px 1px 1px #FFFFFF; + background: url(../images/sprites.png) 1px -536px; } div.list-view td.state.on span { @@ -1237,7 +1238,7 @@ div.list-view td.state.off span { height: 78%; width: 97%; padding-top: 7px; - overflow: hidden; + overflow: auto; height: 591px; margin: -4px 0 0; } @@ -6371,6 +6372,7 @@ div.panel.ui-dialog div.list-view div.fixed-header { left: 8px; top: 26px; position: absolute; + font-weight: normal; } .system-dashboard .status_box li.capacity span.overview.total { @@ -7710,4 +7712,3 @@ div.panel.ui-dialog div.list-view div.fixed-header { background-color: #FFFFFF; cursor: move !important; } - diff --git a/ui/scripts-test/installWizard.js b/ui/scripts-test/installWizard.js index 3f4c9342560..7a2b6a13e1f 100644 --- a/ui/scripts-test/installWizard.js +++ b/ui/scripts-test/installWizard.js @@ -218,7 +218,9 @@ action: function(args) { var complete = args.response.success; + var error = args.response.error; var message = args.response.message; + var startFn = args.startFn; var createZone = function(args) { message('Creating zone'); @@ -245,6 +247,7 @@ message('Creating cluster'); setTimeout(function() { createHost(); + //error('addCluster', 'Could not create cluster.', createPod); }, 500); }; @@ -274,7 +277,11 @@ setTimeout(complete, 5000); }; - createZone(); + if (startFn) { + startFn(); + } else { + createZone(); + } } }; }(jQuery, cloudStack, testData)); diff --git a/ui/scripts/installWizard.js b/ui/scripts/installWizard.js index f995c258774..d844c72a874 100644 --- a/ui/scripts/installWizard.js +++ b/ui/scripts/installWizard.js @@ -230,8 +230,10 @@ action: function(args) { var complete = args.response.success; + var error = args.response.error; var message = args.response.message; var data = args.data; + var startFn = args.startFn; var createZone = function(args) { message('Creating zone'); @@ -388,10 +390,7 @@ args.complete({ data: { zone: zoneObj } }); } }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - alert("updateNetworkServiceProvider failed. Error: " + errorMsg); - } + error: args.error }); }); } @@ -402,13 +401,12 @@ } } }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - alert("configureVirtualRouterElement failed. Error: " + errorMsg); - } + error: args.error }); }); - } + }, + + error: args.error }); } else if (result.jobstatus == 2) { @@ -416,24 +414,21 @@ } } }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - alert("updatePhysicalNetwork failed. Error: " + errorMsg); - } + error: args.error }); }); } }); //NaaS (end) }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - args.response.error(errorMsg); - } + error: args.error }); }; - addZoneAction({ data: data.zone, complete: createPod }); + addZoneAction({ data: data.zone, complete: createPod, error: function(json) { + debugger; + error('addZone', parseXMLHttpResponse(json), createZone); + } }); }; var createPod = function(args) { @@ -458,7 +453,7 @@ }, error: function(XMLHttpResponse) { var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - args.response.error(errorMsg); + error(errorMsg); } }); }; @@ -571,17 +566,19 @@ success: function(json) { //var item = json.createvlaniprangeresponse.vlan; }, - error: function(XMLHttpResponse) { - //var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - //args.response.error(errorMsg); + error: function(json) { + error('addNetwork', parseXMLHttpResponse(json), function() { + createNetwork(args); + }); } }); } }, - error: function(XMLHttpResponse) { - var errorMsg = parseXMLHttpResponse(XMLHttpResponse); - args.response.error(errorMsg); + error: function(json) { + error('addNetwork', parseXMLHttpResponse(json), function() { + createNetwork(args); + }); } }); }; @@ -602,6 +599,11 @@ response: { success: function(successArgs) { createCluster({ data: $.extend(args.data, { guestNetwork: successArgs.data })}); + }, + error: function(json) { + error('addNetwork', parseXMLHttpResponse(json), function() { + createNetwork(args); + }); } }, data: { @@ -615,7 +617,7 @@ networkOfferingId: networkOfferingID } }); - } + } }); }; @@ -638,6 +640,11 @@ cluster: data.addclusterresponse.cluster[0] }) }); + }, + error: function(json) { + error('addCluster', parseXMLHttpResponse(json), function() { + createCluster(args); + }); } }); }; @@ -664,6 +671,11 @@ host: data.addhostresponse.host[0] }) }); + }, + error: function(json) { + error('addHost', parseXMLHttpResponse(json), function() { + createHost(args); + }); } }); }; @@ -734,7 +746,11 @@ }, 5000); }; - createZone(); + if (startFn) { + startFn(); + } else { + createZone(); + } } }; }(jQuery, cloudStack, testData)); diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 3d6ddb7980b..fda013181df 100644 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -54,15 +54,30 @@ sectionSelect: { preFilter: function(args) { var isSecurityGroupEnabled = false; - + var hasIsolatedNetworks = false; + $.ajax({ url: createURL('listNetworks'), data: { supportedServices: 'SecurityGroup' }, - dataType: 'json', async: false, success: function(data) { + $.ajax({ + url: createURL('listNetworks'), + data: { + type: 'isolated', + supportedServices: 'SourceNat' + }, + async: false, + success: function(data) { + if (data.listnetworksresponse.network && + data.listnetworksresponse.network.length) { + hasIsolatedNetworks = true; + } + } + }); + if (data.listnetworksresponse.network && data.listnetworksresponse.network.length) { isSecurityGroupEnabled = true; @@ -70,7 +85,9 @@ } }); - if (isSecurityGroupEnabled) return ['securityGroups']; + if (isSecurityGroupEnabled && !hasIsolatedNetworks) return ['securityGroups']; + if (isSecurityGroupEnabled && hasIsolatedNetworks) return ['securityGroups', 'networks']; + return ['networks']; }, label: 'Select view' @@ -79,16 +96,95 @@ networks: { id: 'networks', type: 'select', - title: 'Networks', + title: 'Guest Networks', listView: { + actions: { + add: { + label: 'Add guest network', + createForm: { + title: 'Add new guest network', + desc: 'Please specify name and zone for this network; note that network will be isolated and source NAT-enabled.', + fields: { + name: { label: 'Name', validation: { required: true } }, + displayText: { label: 'Display Text', validation: { required: true }}, + zoneId: { + label: 'Zone', + validation: { required: true }, + select: function(args) { + $.ajax({ + url: createURL('listZones'), + data: { + type: 'Advanced' + }, + success: function(json) { + var zones = $.grep(json.listzonesresponse.zone, function(zone) { + return zone.networktype == 'Advanced'; + }); + + args.response.success({ + data: $.map(zones, function(zone) { + return { + id: zone.id, + description: zone.name + }; + }) + }); + } + }); + } + }, + networkOfferingId: { + label: 'Network Offering', + validation: { required: true }, + select: function(args) { + $.ajax({ + url: createURL('listNetworkOfferings'), + data: { + supportedServices: 'SourceNat', + type: 'isolated' + }, + success: function(json) { + var networkOfferings = json.listnetworkofferingsresponse.networkoffering; + args.response.success({ + data: $.map(networkOfferings, function(zone) { + return { + id: zone.id, + description: zone.name + }; + }) + }); + } + }); + } + } + } + }, + action: function(args) { + $.ajax({ + url: createURL('createNetwork'), + data: args.data, + success: function(json) { + args.response.success({ + data: { state: 'Allocated' } + }); + } + }); + }, + messages: { + notification: function() { return 'Added guest network'; } + } + } + }, id: 'networks', fields: { name: { label: 'Name' }, zonename: { label: 'Zone' }, type: { label: 'Type' }, - traffictype: { label: 'Traffic Type' }, - gateway: { label: 'Gateway' }, - state: { label: 'State', indicator: { 'Implemented': 'on', 'Setup': 'on' } } + vlan: { label: 'VLAN' }, + cidr: { label: 'CIDR' }, + state: { label: 'State', indicator: { + 'Implemented': 'on', 'Setup': 'on', 'Allocated': 'on' + } } }, dataProvider: function(args) { $.ajax({ @@ -112,87 +208,275 @@ }, detailView: { - name: 'Network details', - viewAll: { path: 'network.ipAddresses', label: 'Associated IP Addresses' }, + name: 'Guest network details', + viewAll: { + path: '_zone.guestIpRanges', + label: 'IP ranges', + preFilter: function(args) { + if(args.context.networks[0].type == "Isolated") { + var services = args.context.networks[0].service; + if(services != null) { + for(var i=0; i < services.length; i++) { + var service = services[i]; + if(service.name == "SourceNat") + return false; + } + } + } + return true; + } + }, actions: { edit: { - label: 'Edit network', + label: 'Edit', messages: { - notification: function() { return 'Updated network'; } + confirm: function(args) { + return 'Are you sure you want to edit network?'; + }, + success: function(args) { + return 'Network is being edited.'; + }, + notification: function(args) { + return 'Editing network'; + }, + complete: function(args) { + return 'Network has been edited.'; + } }, action: function(args) { + var array1 = []; + array1.push("&name=" + todb(args.data.name)); + array1.push("&displaytext=" + todb(args.data.displaytext)); + + //args.data.networkofferingid is null when networkofferingid field is hidden + if(args.data.networkofferingid != null && args.data.networkofferingid != args.context.networks[0].networkofferingid) + array1.push("&networkofferingid=" + todb(args.data.networkofferingid)); + + //args.data.networkdomain is null when networkdomain field is hidden + if(args.data.networkdomain != null && args.data.networkdomain != args.context.networks[0].networkdomain) + array1.push("&networkdomain=" + todb(args.data.networkdomain)); + $.ajax({ - url: createURL('updateNetwork'), - data: $.extend(args.data, { - id: args.context.networks[0].id - }), + url: createURL("updateNetwork&id=" + args.context.networks[0].id + array1.join("")), + dataType: "json", success: function(json) { - args.response.success({ - _custom: { - jobId: json.updatenetworkresponse.jobid + var jid = json.updatenetworkresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + var item = json.queryasyncjobresultresponse.jobresult.network; + return {data: item}; + } + } } - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); + ); } }); }, - notification: { poll: pollAsyncJobResult } + notification: { + poll: pollAsyncJobResult + } + }, + + 'restart': { + label: 'Restart network', + action: function(args) { + $.ajax({ + url: createURL("restartNetwork&cleanup=true&id=" + args.context.networks[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.restartnetworkresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.network; + } + } + } + ); + } + }); + }, + messages: { + confirm: function(args) { + return 'Please confirm that you want to restart network'; + }, + success: function(args) { + return 'Network is being restarted'; + }, + notification: function(args) { + return 'Restarting network'; + }, + complete: function(args) { + return 'Network has been restarted'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + + 'delete': { + label: 'Delete network', + messages: { + confirm: function(args) { + return 'Are you sure you want to delete network ?'; + }, + success: function(args) { + return 'Network is being deleted.'; + }, + notification: function(args) { + return 'Deleting network'; + }, + complete: function(args) { + return 'Network has been deleted.'; + } + }, + action: function(args) { + $.ajax({ + url: createURL("deleteNetwork&id=" + args.context.networks[0].id), + dataType: "json", + async: true, + success: function(json) { + var jid = json.deletenetworkresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return {}; //nothing in this network needs to be updated, in fact, this whole template has being deleted + } + } + } + ); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } } }, tabs: { details: { title: 'Details', + preFilter: function(args) { + var hiddenFields = []; + var zone; + + $.ajax({ + url: createURL('listZones'), + data: { + id: args.context.networks[0].zoneid + }, + async: false, + success: function(json) { + zone = json.listzonesresponse.zone[0]; + } + }); + + if(zone.networktype == "Basic") { + hiddenFields.push("account"); + hiddenFields.push("gateway"); + //hiddenFields.push("netmask"); + } + + if(args.context.networks[0].type == "Isolated") { + hiddenFields.push("networkofferingdisplaytext"); + hiddenFields.push("networkdomaintext"); + hiddenFields.push("gateway"); + //hiddenFields.push("netmask"); + } + else { //selectedGuestNetworkObj.type == "Shared" + hiddenFields.push("networkofferingid"); + hiddenFields.push("networkdomain"); + } + return hiddenFields; + }, fields: [ { - name: { label: 'Name' } - }, - { - type: { label: 'Type' }, - displaytext: { label: 'Description' }, - traffictype: { label: 'Traffic Type' }, - gateway: { label: 'Gateway' }, - networkofferingid: { - label: 'Network Offering', - isEditable: true, - select: function(args) { - $.ajax({ - url: createURL('listNetworkOfferings'), - data: { - state: 'enabled', - traffictype: args.context.networks[0].traffictype, - guestiptype: args.context.networks[0].type - }, - success: function(json) { - args.response.success({ - data: $.map( - json.listnetworkofferingsresponse.networkoffering, - function(networkOffering) { - return { - id: networkOffering.id, - description: networkOffering.name - }; - } - ) - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); - } + name: { + label: 'Name', + isEditable: true } }, { - startip: { label: 'Start IP' }, - endip: { label: 'End IP' } + id: { label: 'ID' }, + displaytext: { + label: 'Description', + isEditable: true + }, + type: { + label: 'Type' + }, + state: { + label: 'State' + }, + restartrequired: { + label: 'Restart required', + converter: function(booleanValue) { + if(booleanValue == true) + return "Yes"; + else if(booleanValue == false) + return "No"; + } + }, + vlan: { label: 'VLAN ID' }, + scope: { label: 'Scope' }, + networkofferingdisplaytext: { label: 'Network offering' }, + networkofferingid: { + label: 'Network offering', + isEditable: true, + select: function(args){ + var items = []; + $.ajax({ + url: createURL("listNetworkOfferings&networkid=" + args.context.networks[0].id), + dataType: "json", + async: false, + success: function(json) { + var networkOfferingObjs = json.listnetworkofferingsresponse.networkoffering; + $(networkOfferingObjs).each(function() { + items.push({id: this.id, description: this.displaytext}); + }); + } + }); + $.ajax({ + url: createURL("listNetworkOfferings&id=" + args.context.networks[0].networkofferingid), //include currently selected network offeirng to dropdown + dataType: "json", + async: false, + success: function(json) { + var networkOfferingObjs = json.listnetworkofferingsresponse.networkoffering; + $(networkOfferingObjs).each(function() { + items.push({id: this.id, description: this.displaytext}); + }); + } + }); + args.response.success({data: items}); + } + }, + + networkofferingidText: { + label: 'Network offering ID' + }, + + gateway: { label: 'Gateway' }, + //netmask: { label: 'Netmask' }, + cidr: { label: 'CIDR' }, + networkdomaintext: { + label: 'Network domain' + }, + networkdomain: { + label: 'Network domain', + isEditable: true + } } ], - dataProvider: function(args) { - args.response.success({ data: args.context.networks[0] }); + dataProvider: function(args) { + args.response.success({data: args.context.networks[0]}); } - } + }, } } } @@ -1245,7 +1529,7 @@ poll: pollAsyncJobResult } }); - }, + }, error: function(data) { args.response.error(parseXMLHttpResponse(data)); } diff --git a/ui/scripts/ui-custom/installWizard.js b/ui/scripts/ui-custom/installWizard.js index c999b7482e0..360c8abe22d 100644 --- a/ui/scripts/ui-custom/installWizard.js +++ b/ui/scripts/ui-custom/installWizard.js @@ -5,6 +5,9 @@ var $container = args.$container; var state = {}; // Hold wizard form state + var launchStart; // Holds last launch callback, in case of error + var $launchState; + /** * Successful installation action */ @@ -36,8 +39,10 @@ * @param stateStepID ID to group state elements in (i.e., zone, pod, cluster, ...) * @param $elem (optional) Element containing
, to serialize for state */ - var goTo = cloudStack._goto = function(stepID, stateID, $elem) { - var $nextStep = steps[stepID](); + var goTo = cloudStack._goto = function(stepID, stateID, $elem, options) { + if (!options) options = {}; + + var $nextStep = steps[stepID]({ nextStep: options.nextStep }); var $body = $installWizard.find('.body'); if (stateID && $elem) { @@ -83,7 +88,7 @@ $prev.click(function() { goTo(prevStepID); }); - + return function(args) { showDiagram(diagram); @@ -95,7 +100,7 @@ ); }; }, - + /** * A standard form-based wizard step template * -- relies on createForm for form generation @@ -134,7 +139,7 @@ $form.find('.form-item').addClass('field'); $prev.appendTo($form.find('form')); $save.appendTo($form.find('form')); - + // Submit handler $form.find('form').submit(function() { form.completeAction($form); @@ -153,8 +158,19 @@ $container.append($form.prepend($title)); showTooltip($form, tooltipID); - + return function(args) { + var overrideGotoEvent = function(event) { + goTo(args.nextStep); + + return false; + }; + + if (args && args.nextStep) { + $save.unbind('click'); + $save.click(overrideGotoEvent); + } + // Setup diagram, tooltips showDiagram(diagram); setTimeout(function() { @@ -350,7 +366,7 @@ nextStepID: 'addZone', diagram: '.part.zone' }), - + /** * Add zone form */ @@ -622,31 +638,66 @@ launch: function(args) { var $intro = $('
').addClass('intro'); var $title = $('
').addClass('title') - .html('Now building your cloud...'); - var $subtitle = $('
').addClass('subtitle') - .html(''); + .html('Now building your cloud...').appendTo($intro); + var $subtitle = $('
').addClass('subtitle').html('').appendTo($intro); - cloudStack.installWizard.action({ - data: state, - response: { - message: function(msg) { - var $li = $('
  • ').html(msg); - - $subtitle.append($li); - - $li.siblings().addClass('complete'); - }, - success: function() { - goTo('complete'); - } + var doAction = function() { + if (launchStart) { + $('.subtitle').children().remove(); } - }); + + cloudStack.installWizard.action({ + data: state, + startFn: launchStart, + response: { + message: function(msg) { + var $li = $('
  • ').html(msg); + if (launchStart) { + $li.appendTo('.subtitle'); + $li.parent().find('li') + .filter(function() { + return this != $li.get(0); + }).addClass('complete'); + } else { + $subtitle.append($li); + $li.siblings().addClass('complete'); + } + }, + success: function() { + goTo('complete'); + }, + error: function(stepID, message, callback) { + if (launchStart) { + $subtitle = $('.subtitle'); + $subtitle.children().remove(); + $('li').children().remove(); + } + + launchStart = callback; + $subtitle.find('li:last').addClass('error'); + + $subtitle.append( + $('

    ').html( + 'Something went wrong; you may go back and correct any errors.' + ), + $('

    ').addClass('button').append( + $('').html('Go back') + ).click(function() { + goTo(stepID, null, null, { + nextStep: 'launch' + }); + }) + ); + } + } + }); + }; + + doAction(); showDiagram('.part.loading'); - return $intro.append( - $title, $subtitle - ); + return $intro; }, complete: function(args) {