From 29f9137e2d18cfe35656d6b9482248761cd9efa0 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 22 Feb 2013 15:53:48 -0800 Subject: [PATCH 01/41] WIP: Add view all button to NIC --- ui/scripts/instances.js | 4 ++++ ui/scripts/ui/widgets/detailView.js | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 8a7c7ad3af1..5b448e32d4c 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -1242,6 +1242,10 @@ } } ], + viewAll: { + path: 'network.ipAddresses', + attachTo: 'ipaddress' + }, dataProvider: function(args) { $.ajax({ url:createURL("listVirtualMachines&details=nics&id=" + args.context.instances[0].id), diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index f0cc8fd60b2..7d00beaa3bf 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -1034,6 +1034,16 @@ actionFilter: actionFilter } ).appendTo($tabContent); + + if (tabData.viewAll) { + $tabContent.find('tr') + .filter('.' + tabData.viewAll.attachTo).find('td.value') + .append( + $('
').addClass('view-all').append( + $('').html('View all IP Addresses') + ) + ); + } }); return true; From 4c0fd582dc97cc87799699a19cff4cf7f8e2ed64 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Fri, 22 Feb 2013 16:05:21 -0800 Subject: [PATCH 02/41] Style view all button --- ui/css/cloudstack3.css | 23 ++++++++++++++++++++--- ui/scripts/ui/widgets/detailView.js | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index cf18ab215e6..b0a7120ec9f 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -1737,16 +1737,33 @@ div.list-view td.state.off span { text-indent: 0; } -.detail-group .main-groups table td.value span { +.detail-group .main-groups table td.value > span { display: block; height: 30px; overflow: auto; position: relative; top: 9px; + float: left; + width: 291px; } -.detail-group .main-groups table td.value span { - width: 355px; +.detail-group .main-groups table td.value .view-all { + cursor: pointer; + /*[empty]height:;*/ + border-left: 1px solid #9FA2A5; + /*+border-radius:4px 0 0 4px;*/ + -moz-border-radius: 4px 0 0 4px; + -webkit-border-radius: 4px 0 0 4px; + -khtml-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; + background: url(../images/sprites.png) no-repeat 100% -398px; + float: right; + margin: 1px 0 0; + padding: 8px 33px 6px 15px; +} + +.detail-group .main-groups table td.value .view-all:hover { + background-position: 100% -431px; } .panel.always-maximized .detail-group .main-groups table td.value span { diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index 7d00beaa3bf..69743a9fc05 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -1040,7 +1040,7 @@ .filter('.' + tabData.viewAll.attachTo).find('td.value') .append( $('
').addClass('view-all').append( - $('').html('View all IP Addresses') + $('').html(_l('label.view.all')) ) ); } From b652d2e4baacc764a85b88723f191e4c029334f3 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 25 Feb 2013 11:05:42 -0800 Subject: [PATCH 03/41] Implement view all behavior for multi-items -Call viewAll() on click to open new panel with corresponding view -Pass context of selected item (such as nic object) to panel -Allow custom title for panel --- ui/scripts/instances.js | 7 ++++++- ui/scripts/ui/widgets/detailView.js | 23 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 5b448e32d4c..82405817490 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -1244,7 +1244,12 @@ ], viewAll: { path: 'network.ipAddresses', - attachTo: 'ipaddress' + attachTo: 'ipaddress', + title: function(args) { + var title = _l('label.menu.ipaddresses') + ' - ' + args.context.nics[0].name; + + return title; + } }, dataProvider: function(args) { $.ajax({ diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js index 69743a9fc05..9a9ba02269b 100644 --- a/ui/scripts/ui/widgets/detailView.js +++ b/ui/scripts/ui/widgets/detailView.js @@ -587,7 +587,8 @@ var $listView; var isCustom = $.isFunction(viewAllID.custom); var updateContext = options.updateContext; - + var customTitle = options.title; + if (isCustom) { $browser.cloudBrowser('addPanel', { title: _l(viewAllID.label), @@ -644,7 +645,7 @@ // Make panel var $panel = $browser.cloudBrowser('addPanel', { - title: _l(listViewArgs.title), + title: customTitle ? customTitle({ context: context }) : _l(listViewArgs.title), data: '', noSelectPanel: true, maximizeIfSelected: true, @@ -970,7 +971,7 @@ var tabs = args.tabs[targetTabID]; var dataProvider = tabs.dataProvider; var isMultiple = tabs.multiple || tabs.isMultiple; - var viewAll = args.viewAll; + var viewAllArgs = args.viewAll; var $detailView = $tabContent.closest('.detail-view'); var jsonObj = $detailView.data('view-args').jsonObj; @@ -1024,6 +1025,8 @@ if (isMultiple) { $(data).each(function() { + var item = this; + var $fieldContent = makeFieldContent( $.extend(true, {}, tabs, { id: targetTabID @@ -1036,12 +1039,22 @@ ).appendTo($tabContent); if (tabData.viewAll) { - $tabContent.find('tr') + $fieldContent.find('tr') .filter('.' + tabData.viewAll.attachTo).find('td.value') .append( $('
').addClass('view-all').append( $('').html(_l('label.view.all')) - ) + ).click(function() { + viewAll( + tabData.viewAll.path, + { + updateContext: function(args) { + return { nics: [item] }; + }, + title: tabData.viewAll.title + } + ); + }) ); } }); From 6c8182b6f4740a98a613dc440a84c6a080b43d95 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 25 Feb 2013 11:35:41 -0800 Subject: [PATCH 04/41] UI: Update IP address server calls to support per-NIC IPs Update the following actions on IP address section to support per-NIC IP addresses: - List IPs -> listNics - Add IP -> addIpToNic - Remove IP -> removeIpFromNic These API calls will be conditionally invoked in place of the existing calls if a NIC is selected -- i.e., args.context.nics is present --- ui/scripts/network.js | 194 ++++++++++++++++++++++++++---------------- 1 file changed, 120 insertions(+), 74 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 84cc067f82b..cca0e6c3940 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -1585,30 +1585,47 @@ }); } } - $.ajax({ - url: createURL('associateIpAddress'), - data: dataObj, - success: function(data) { - args.response.success({ - _custom: { - jobId: data.associateipaddressresponse.jobid, - getUpdatedItem: function(data) { - var newIP = data.queryasyncjobresultresponse.jobresult.ipaddress; - return $.extend(newIP, { - state: 'Allocated' - }); - }, - getActionFilter: function() { - return actionFilters.ipAddress; - } - } - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); + if (args.context.nics) { + $.ajax({ + url: createURL('addIpToNic'), + data: { + nicId: args.context.nics[0].id + }, + success: function(json) { + args.response.success({ + _custom: { + jobId: json.addiptonicresponse.jobid + } + }); + } + }); + } else { + $.ajax({ + url: createURL('associateIpAddress'), + data: dataObj, + success: function(data) { + args.response.success({ + _custom: { + jobId: data.associateipaddressresponse.jobid, + getUpdatedItem: function(data) { + var newIP = data.queryasyncjobresultresponse.jobresult.ipaddress; + return $.extend(newIP, { + state: 'Allocated' + }); + }, + getActionFilter: function() { + return actionFilters.ipAddress; + } + } + }); + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); + } }, notification: { @@ -1643,27 +1660,42 @@ }); } - $.ajax({ - url: createURL('listPublicIpAddresses'), - data: data, - dataType: "json", - async: true, - success: function(json) { - var items = json.listpublicipaddressesresponse.publicipaddress; - - $(items).each(function() { - getExtaPropertiesForIpObj(this, args); - }); + if (args.context.nics) { + $.ajax({ + url: createURL('listNics'), + data: { + nicId: args.context.nics[0].id, + vmId: args.context.instances[0].id + }, + success: function(json) { + args.response.success({ + data: json.listnicsresponse.nic[0].ipAddresses + }); + } + }); + } else { + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: data, + dataType: "json", + async: true, + success: function(json) { + var items = json.listpublicipaddressesresponse.publicipaddress; + + $(items).each(function() { + getExtaPropertiesForIpObj(this, args); + }); - args.response.success({ - actionFilter: actionFilters.ipAddress, - data: items - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } - }); + args.response.success({ + actionFilter: actionFilters.ipAddress, + data: items + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + } }, // Detail view @@ -2017,39 +2049,53 @@ remove: { label: 'label.action.release.ip', action: function(args) { - $.ajax({ - url: createURL('disassociateIpAddress'), - data: { - id: args.context.ipAddresses[0].id - }, - dataType: 'json', - async: true, - success: function(data) { - args.response.success({ - _custom: { - jobId: data.disassociateipaddressresponse.jobid, - getActionFilter: function() { - return function(args) { - var allowedActions = ['enableStaticNAT']; + if (args.context.nics) { + $.ajax({ + url: createURL('removeIpFromNic'), + data: { + id: args.context.ipAddresses[0].id + }, + success: function(json) { + args.response.success({ + _custom: { jobId: json.removeipfromnicresponse.jobid } + }); + } + }); + } else { + $.ajax({ + url: createURL('disassociateIpAddress'), + data: { + id: args.context.ipAddresses[0].id + }, + dataType: 'json', + async: true, + success: function(data) { + args.response.success({ + _custom: { + jobId: data.disassociateipaddressresponse.jobid, + getActionFilter: function() { + return function(args) { + var allowedActions = ['enableStaticNAT']; - return allowedActions; - }; - }, - getUpdatedItem: function(args) { - return { - state: 'Released' - }; - }, - onComplete: function() { - $(window).trigger('cloudStack.fullRefresh'); + return allowedActions; + }; + }, + getUpdatedItem: function(args) { + return { + state: 'Released' + }; + }, + onComplete: function() { + $(window).trigger('cloudStack.fullRefresh'); + } } - } - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } - }); + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); + } }, messages: { confirm: function(args) { From 243b20f0fd4061c12441a90b178c8be06dee28b6 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Mon, 25 Feb 2013 11:44:59 -0800 Subject: [PATCH 05/41] Prevent wrapping of 'view all' button --- ui/css/cloudstack3.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index b0a7120ec9f..c3313d6eebd 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -1744,7 +1744,7 @@ div.list-view td.state.off span { position: relative; top: 9px; float: left; - width: 291px; + width: 245px; } .detail-group .main-groups table td.value .view-all { From 4469e6b859f1f6ed690151b185f7aaf29884d806 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 6 Mar 2013 11:26:38 -0800 Subject: [PATCH 06/41] Multiple NICs UI: Fix list view data --- ui/scripts/network.js | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 5d9966f2c61..f685c9cea4d 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -620,7 +620,7 @@ var data = { id: args.context.networks[0].id, name: args.data.name, - displaytext: args.data.displaytext, + displaytext: args.data.displaytext }; //args.data.networkdomain is null when networkdomain field is hidden @@ -628,13 +628,13 @@ $.extend(data, { networkdomain: args.data.networkdomain }); - } + } - if(args.data.cidr !="" ){ - $.extend(data, { - guestvmcidr: args.data.cidr - }); - } + if(args.data.cidr !="" ){ + $.extend(data, { + guestvmcidr: args.data.cidr + }); + } //args.data.networkofferingid is null when networkofferingid field is hidden if(args.data.networkofferingid != null && args.data.networkofferingid != args.context.networks[0].networkofferingid) { @@ -1606,7 +1606,10 @@ success: function(json) { args.response.success({ _custom: { - jobId: json.addiptonicresponse.jobid + getUpdatedItem: function(data) { + + }, + jobId: json.addiptovmnicresponse.jobid } }); } @@ -1676,11 +1679,18 @@ url: createURL('listNics'), data: { nicId: args.context.nics[0].id, - vmId: args.context.instances[0].id + virtualmachineid: args.context.instances[0].id }, success: function(json) { + var ips = json.listnics.nic[0].secondaryip + args.response.success({ - data: json.listnicsresponse.nic[0].ipAddresses + data: $(ips).map(function(index, ip) { + return $.extend(ip, { + zonename: args.context.instances[0].zonename, + virtualmachinedisplayname: args.context.instances[0].displayname + }); + }) }); } }); From f24ee207c46b2a5fcf7c96bb3f9590ad43a06f33 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 6 Mar 2013 12:13:00 -0800 Subject: [PATCH 07/41] Multiple NICs UI: Refactor list view/detail view Puts secondary IP list view and detail view in dedicated section, instead of as part of the public IP address section. Removes conditional API calls from public IP address list/detail view for secondary NICS. --- ui/scripts/instances.js | 2 +- ui/scripts/network.js | 735 ++++++++++++---------------------------- 2 files changed, 224 insertions(+), 513 deletions(-) diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 82405817490..787239d9528 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -1243,7 +1243,7 @@ } ], viewAll: { - path: 'network.ipAddresses', + path: 'network.secondaryNicIps', attachTo: 'ipaddress', title: function(args) { var title = _l('label.menu.ipaddresses') + ' - ' + args.context.nics[0].name; diff --git a/ui/scripts/network.js b/ui/scripts/network.js index f685c9cea4d..4d8798b82b3 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -1058,400 +1058,167 @@ } }); } + } + } + } + } + }, + secondaryNicIps: { + title: 'label.menu.ipaddresses', + listView: { + id: 'secondaryNicIps', + label: 'label.ips', + fields: { + ipaddress: { + label: 'label.ips', + converter: function(text, item) { + if (item.issourcenat) { + return text + ' [' + _l('label.source.nat') + ']'; + } + + return text; + } + }, + zonename: { label: 'label.zone' }, + virtualmachinedisplayname: { label: 'label.vm.name' }, + state: { + converter: function(str) { + // For localization + return str; + }, + label: 'label.state', indicator: { 'Allocated': 'on', 'Released': 'off' } + } + }, + actions: { + add: { + label: 'label.acquire.new.ip', + addRow: 'true', + messages: { + confirm: function(args) { + 'message.acquire.new.ip'; + }, + notification: function(args) { + return 'label.acquire.new.ip'; + } + }, + action: function(args) { + var dataObj = {}; + + $.ajax({ + url: createURL('addIpToNic'), + data: { + nicId: args.context.nics[0].id + }, + success: function(json) { + args.response.success({ + _custom: { + getUpdatedItem: function(data) { + + }, + jobId: json.addiptovmnicresponse.jobid + } + }); + } + }); }, - egressRules: { - title: 'label.egress.rules', - custom: function(args) { - var context = args.context; + notification: { + poll: pollAsyncJobResult + } + } + }, - return $('
').multiEdit({ - context: context, - noSelect: true, - noHeaderActionsColumn: true, - fields: { - 'cidrlist': { edit: true, label: 'label.cidr.list', isOptional: true }, - 'protocol': { - label: 'label.protocol', - select: function(args) { - args.$select.change(function() { - var $inputs = args.$form.find('th, td'); - var $icmpFields = $inputs.filter(function() { - var name = $(this).attr('rel'); + dataProvider: function(args) { + var data = {}; - return $.inArray(name, [ - 'icmptype', - 'icmpcode' - ]) > -1; - }); - var $otherFields = $inputs.filter(function() { - var name = $(this).attr('rel'); + $.ajax({ + url: createURL('listNics'), + data: { + nicId: args.context.nics[0].id, + virtualmachineid: args.context.instances[0].id + }, + success: function(json) { + var ips = json.listnics.nic[0].secondaryip - return name != 'cidrlist' && - name != 'icmptype' && - name != 'icmpcode' && - name != 'protocol' && - name != 'add-rule'; - }); + args.response.success({ + data: $(ips).map(function(index, ip) { + return $.extend(ip, { + zoneid: args.context.instances[0].zoneid, + zonename: args.context.instances[0].zonename, + virtualmachinedisplayname: args.context.instances[0].displayname + }); + }) + }); + } + }); + }, - if ($(this).val() == 'icmp') { - $icmpFields.show(); - $otherFields.hide(); - } else if ($(this).val() == 'all') { - $icmpFields.hide(); - $otherFields.hide(); - } else { - $icmpFields.hide(); - $otherFields.show(); - } - }); - - args.response.success({ - data: [ - { name: 'tcp', description: 'TCP' }, - { name: 'udp', description: 'UDP' }, - { name: 'icmp', description: 'ICMP' }, - { name: 'all', description: 'All' } - ] - }); - } - }, - 'startport': { edit: true, label: 'label.start.port', isOptional: true }, - 'endport': { edit: true, label: 'label.end.port', isOptional: true }, - 'icmptype': { edit: true, label: 'ICMP.type', isHidden: true, isOptional: true }, - 'icmpcode': { edit: true, label: 'ICMP.code', isHidden: true, isOptional: true }, - 'add-rule': { - label: 'label.add', - addButton: true - } + // Detail view + detailView: { + name: 'Secondary IP address detail', + actions: { + remove: { + label: 'label.action.release.ip', + action: function(args) { + $.ajax({ + url: createURL('removeIpFromNic'), + data: { + id: args.context.secondaryNicIps[0].ipaddress }, - add: { - label: 'label.add', - action: function(args) { - var data = { - protocol: args.data.protocol, - cidrlist: args.data.cidrlist, - networkid: args.context.networks[0].id - }; - - if (args.data.icmptype && args.data.icmpcode) { // ICMP - $.extend(data, { - icmptype: args.data.icmptype, - icmpcode: args.data.icmpcode - }); - } else { // TCP/UDP - $.extend(data, { - startport: args.data.startport, - endport: args.data.endport - }); - } - - $.ajax({ - url: createURL('createEgressFirewallRule'), - data: data, - dataType: 'json', - async: true, - success: function(json) { - var jobId = json.createegressfirewallruleresponse.jobid; - - args.response.success({ - _custom: { - jobId: jobId - }, - notification: { - label: 'label.add.egress.rule', - poll: pollAsyncJobResult - } - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); - } - }, - actions: { - destroy: { - label: 'label.remove.rule', - action: function(args) { - $.ajax({ - url: createURL('deleteEgressFirewallRule'), - data: { - id: args.context.multiRule[0].id - }, - dataType: 'json', - async: true, - success: function(data) { - var jobID = data.deleteegressfirewallruleresponse.jobid; - - args.response.success({ - _custom: { - jobId: jobID - }, - notification: { - label: 'label.remove.egress.rule', - poll: pollAsyncJobResult - } - }); - }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); - } - } - }, - ignoreEmptyFields: true, - dataProvider: function(args) { - $.ajax({ - url: createURL('listEgressFirewallRules'), - data: { - listAll: true, - networkid: args.context.networks[0].id - }, - dataType: 'json', - async: true, - success: function(json) { - var response = json.listegressfirewallrulesresponse.firewallrule ? - json.listegressfirewallrulesresponse.firewallrule : []; - - args.response.success({ - data: $.map(response, function(rule) { - if (rule.protocol == 'all') { - $.extend(rule, { - startport: 'All', - endport: 'All' - }); - } else if (rule.protocol == 'tcp' || rule.protocol == 'udp') { - if (!rule.startport) { - rule.startport = ' '; - } - - if (!rule.endport) { - rule.endport = ' '; - } - } - - return rule; - }) - }); - } + success: function(json) { + args.response.success({ + _custom: { jobId: json.removeipfromnicresponse.jobid } }); } }); - } - }, + }, + messages: { + confirm: function(args) { + return 'message.action.release.ip'; + }, + notification: function(args) { + return 'label.action.release.ip'; + } + }, + notification: { poll: pollAsyncJobResult } + } + }, + tabs: { + details: { + title: 'label.details', + fields: [ + { + ipaddress: { label: 'label.ip' } + }, + { + id: { label: 'label.id' }, + virtualmachinedisplayname: { label: 'label.vm.name' }, + zonename: { label: 'label.zone.name' } + } + ], - addloadBalancer: { // EIP/ELB Basic zone: Add Load Balancer tab in network detailView - title: 'label.add.load.balancer', - custom: function(args) { - var context = args.context; + dataProvider: function(args) { + $.ajax({ + url: createURL('listNics'), + data: { + nicId: args.context.nics[0].id, + virtualmachineid: args.context.instances[0].id + }, + success: function(json) { + var ips = json.listnics.nic[0].secondaryip - return $('
').addClass('loadBalancer').multiEdit( - { - context: context, - listView: $.extend(true, {}, cloudStack.sections.instances, { - listView: { - filters: false, - - dataProvider: function(args) { - var data = { - page: args.page, - pageSize: pageSize, - domainid: g_domainid, - account: g_account, - networkid: args.context.networks[0].id, - listAll: true - }; - - $.ajax({ - url: createURL('listVirtualMachines'), - data: data, - dataType: 'json', - async: true, - success: function(data) { - args.response.success({ - data: $.grep( - data.listvirtualmachinesresponse.virtualmachine ? - data.listvirtualmachinesresponse.virtualmachine : [], - function(instance) { - var nonAutoScale=0; - if(instance.displayname == null) - nonAutoScale = 1; - else{ - if( instance.displayname.match(/AutoScale-LB-/)==null) - nonAutoScale =1; - else { - if(instance.displayname.match(/AutoScale-LB-/).length) - nonAutoScale =0; - } - } - var isActiveState= $.inArray(instance.state, ['Destroyed','Expunging']) == -1; - return nonAutoScale && isActiveState; - } - ) - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } - }); - } - } - }), - multipleAdd: true, - fields: { - 'name': { edit: true, label: 'label.name' }, - 'publicport': { edit: true, label: 'label.public.port' }, - 'privateport': { edit: true, label: 'label.private.port' }, - 'algorithm': { - label: 'label.algorithm', - select: function(args) { - args.response.success({ - data: [ - { name: 'roundrobin', description: _l('label.round.robin') }, - { name: 'leastconn', description: _l('label.least.connections') }, - { name: 'source', description: _l('label.source') } - ] - }); - } - }, - 'sticky': { - label: 'label.stickiness', - custom: { - buttonLabel: 'label.configure', - action: cloudStack.lbStickyPolicy.dialog() - } - }, - 'autoScale': { - label: 'AutoScale', - custom: { - requireValidation: true, - buttonLabel: 'label.configure', - action: cloudStack.uiCustom.autoscaler(cloudStack.autoscaler) - } - }, - 'add-vm': { - label: 'label.add.vms', - addButton: true - } - }, - - add: { //basic zone - elastic IP - Add Load Balancer tab - Add VMs button - label: 'label.add.vms', - action: function(args) { - var data = { - algorithm: args.data.algorithm, - name: args.data.name, - privateport: args.data.privateport, - publicport: args.data.publicport, - openfirewall: false, - domainid: g_domainid, - account: g_account - }; - - if('vpc' in args.context) { //from VPC section - if(args.data.tier == null) { - args.response.error('Tier is required'); - return; - } - $.extend(data, { - networkid: args.data.tier - }); - } - else { //from Guest Network section - $.extend(data, { - networkid: args.context.networks[0].id - }); - } - - var stickyData = $.extend(true, {}, args.data.sticky); - - $.ajax({ - url: createURL('createLoadBalancerRule'), - data: data, - dataType: 'json', - async: true, - success: function(data) { - var itemData = args.itemData; - //var jobID = data.createloadbalancerruleresponse.jobid; //CS-16964: use jobid from assignToLoadBalancerRule instead of createLoadBalancerRule - - $.ajax({ - url: createURL('assignToLoadBalancerRule'), - data: { - id: data.createloadbalancerruleresponse.id, - virtualmachineids: $.map(itemData, function(elem) { - return elem.id; - }).join(',') - }, - dataType: 'json', - async: true, - success: function(data) { - var jobID = data.assigntoloadbalancerruleresponse.jobid; //CS-16964: use jobid from assignToLoadBalancerRule instead of createLoadBalancerRule - var lbCreationComplete = false; - - args.response.success({ - _custom: { - jobId: jobID - }, - notification: { - label: 'label.add.load.balancer', - poll: function(args) { - var complete = args.complete; - var error = args.error; - - pollAsyncJobResult({ - _custom: args._custom, - complete: function(args) { - if (lbCreationComplete) { - return; - } - - lbCreationComplete = true; - cloudStack.dialog.notice({ - message: _l('message.add.load.balancer.under.ip') + - args.data.loadbalancer.publicip - }); - - if (stickyData && - stickyData.methodname && - stickyData.methodname != 'None') { - cloudStack.lbStickyPolicy.actions.add( - args.data.loadbalancer.id, - stickyData, - complete, // Complete - complete // Error - ); - } else { - complete(); - } - }, - error: error - }); - } - } - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } + args.response.success({ + data: $.grep($(ips).map(function(index, ip) { + return $.extend(ip, { + zonename: args.context.instances[0].zonename, + virtualmachinedisplayname: args.context.instances[0].displayname }); - } - }, - - - dataProvider: function(args) { - args.response.success({ //no LB listing in AddLoadBalancer tab - data: [] - }); - } + }), function(ip) { + return ip.ipaddress == args.context.secondaryNicIps[0].ipaddress; + })[0] + }); } - ); + }); } } } @@ -1595,51 +1362,32 @@ account: g_account }); } - } + } - if (args.context.nics) { - $.ajax({ - url: createURL('addIpToNic'), - data: { - nicId: args.context.nics[0].id - }, - success: function(json) { - args.response.success({ - _custom: { - getUpdatedItem: function(data) { - - }, - jobId: json.addiptovmnicresponse.jobid + $.ajax({ + url: createURL('associateIpAddress'), + data: dataObj, + success: function(data) { + args.response.success({ + _custom: { + jobId: data.associateipaddressresponse.jobid, + getUpdatedItem: function(data) { + var newIP = data.queryasyncjobresultresponse.jobresult.ipaddress; + return $.extend(newIP, { + state: 'Allocated' + }); + }, + getActionFilter: function() { + return actionFilters.ipAddress; } - }); - } - }); - } else { - $.ajax({ - url: createURL('associateIpAddress'), - data: dataObj, - success: function(data) { - args.response.success({ - _custom: { - jobId: data.associateipaddressresponse.jobid, - getUpdatedItem: function(data) { - var newIP = data.queryasyncjobresultresponse.jobresult.ipaddress; - return $.extend(newIP, { - state: 'Allocated' - }); - }, - getActionFilter: function() { - return actionFilters.ipAddress; - } - } - }); - }, + } + }); + }, - error: function(json) { - args.response.error(parseXMLHttpResponse(json)); - } - }); - } + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + }); }, notification: { @@ -1674,49 +1422,27 @@ }); } - if (args.context.nics) { - $.ajax({ - url: createURL('listNics'), - data: { - nicId: args.context.nics[0].id, - virtualmachineid: args.context.instances[0].id - }, - success: function(json) { - var ips = json.listnics.nic[0].secondaryip + $.ajax({ + url: createURL('listPublicIpAddresses'), + data: data, + dataType: "json", + async: true, + success: function(json) { + var items = json.listpublicipaddressesresponse.publicipaddress; - args.response.success({ - data: $(ips).map(function(index, ip) { - return $.extend(ip, { - zonename: args.context.instances[0].zonename, - virtualmachinedisplayname: args.context.instances[0].displayname - }); - }) - }); - } - }); - } else { - $.ajax({ - url: createURL('listPublicIpAddresses'), - data: data, - dataType: "json", - async: true, - success: function(json) { - var items = json.listpublicipaddressesresponse.publicipaddress; - - $(items).each(function() { - getExtaPropertiesForIpObj(this, args); - }); + $(items).each(function() { + getExtaPropertiesForIpObj(this, args); + }); - args.response.success({ - actionFilter: actionFilters.ipAddress, - data: items - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } - }); - } + args.response.success({ + actionFilter: actionFilters.ipAddress, + data: items + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); }, // Detail view @@ -2070,53 +1796,39 @@ remove: { label: 'label.action.release.ip', action: function(args) { - if (args.context.nics) { - $.ajax({ - url: createURL('removeIpFromNic'), - data: { - id: args.context.ipAddresses[0].id - }, - success: function(json) { - args.response.success({ - _custom: { jobId: json.removeipfromnicresponse.jobid } - }); - } - }); - } else { - $.ajax({ - url: createURL('disassociateIpAddress'), - data: { - id: args.context.ipAddresses[0].id - }, - dataType: 'json', - async: true, - success: function(data) { - args.response.success({ - _custom: { - jobId: data.disassociateipaddressresponse.jobid, - getActionFilter: function() { - return function(args) { - var allowedActions = ['enableStaticNAT']; + $.ajax({ + url: createURL('disassociateIpAddress'), + data: { + id: args.context.ipAddresses[0].id + }, + dataType: 'json', + async: true, + success: function(data) { + args.response.success({ + _custom: { + jobId: data.disassociateipaddressresponse.jobid, + getActionFilter: function() { + return function(args) { + var allowedActions = ['enableStaticNAT']; - return allowedActions; - }; - }, - getUpdatedItem: function(args) { - return { - state: 'Released' - }; - }, - onComplete: function() { - $(window).trigger('cloudStack.fullRefresh'); - } + return allowedActions; + }; + }, + getUpdatedItem: function(args) { + return { + state: 'Released' + }; + }, + onComplete: function() { + $(window).trigger('cloudStack.fullRefresh'); } - }); - }, - error: function(data) { - args.response.error(parseXMLHttpResponse(data)); - } - }); - } + } + }); + }, + error: function(data) { + args.response.error(parseXMLHttpResponse(data)); + } + }); }, messages: { confirm: function(args) { @@ -2180,19 +1892,18 @@ dataProvider: function(args) { var items = args.context.ipAddresses; - // Get network data $.ajax({ url: createURL('listPublicIpAddresses'), - data: { + data: { id: args.context.ipAddresses[0].id, - listAll: true + listAll: true }, dataType: "json", async: true, success: function(json) { var ipObj = json.listpublicipaddressesresponse.publicipaddress[0]; getExtaPropertiesForIpObj(ipObj, args); - + var network = $.grep( args.context.vpc ? args.context.vpc[0].network : args.context.networks, From ed7ebf57cc1eca8d0259816a1e4950e0f399930e Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 6 Mar 2013 13:48:49 -0800 Subject: [PATCH 08/41] List view: Add event hook on add row Adds new event 'cloudStack.listView.addRow' which is triggered after new data row is appended. It passes the table row ($tr) in the data. --- ui/scripts/ui/widgets/listView.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/scripts/ui/widgets/listView.js b/ui/scripts/ui/widgets/listView.js index 9e16ec2ae54..a1e9a95f365 100644 --- a/ui/scripts/ui/widgets/listView.js +++ b/ui/scripts/ui/widgets/listView.js @@ -1037,6 +1037,8 @@ allowedActions: allowedActions } ); + + $listView.trigger('cloudStack.listView.addRow', { $tr: $tr }); } // Add quick view From e046364ccece52b0a6c705c2ad92dee427ef3c61 Mon Sep 17 00:00:00 2001 From: Brian Federle Date: Wed, 6 Mar 2013 14:37:26 -0800 Subject: [PATCH 09/41] LB/PF/NAT UI: Support secondary IP selection During selection of a VM for LB/PF/Static NAT, show dropdown to select which secondary IP will apply for the rule being created. --- ui/scripts/network.js | 51 ++++++++++++++++++++++++++++-- ui/scripts/ui/widgets/multiEdit.js | 48 ++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 4d8798b82b3..6cb7cf0511b 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -34,6 +34,36 @@ return elemData; }; + + var instanceSecondaryIPSubselect = function(args) { + var instance = args.context.instances[0]; + var network = args.context.networks[0]; + var nic = $.grep(instance.nic, function(nic) { + return nic.networkid = network.id; + })[0] + + // Get NIC IPs + $.ajax({ + url: createURL('listNics'), + data: { + virtualmachineid: instance.id, + nicId: nic.id + }, + success: function(json) { + var nic = json.listnics.nic[0]; + var ips = nic.secondaryip ? nic.secondaryip : []; + + args.response.success({ + data: $(ips).map(function(index, ip) { + return { + id: ip.ipaddress, + description: ip.ipaddress + } ; + }) + }); + } + }) + }; var ipChangeNotice = function() { cloudStack.dialog.confirm({ @@ -1095,7 +1125,7 @@ addRow: 'true', messages: { confirm: function(args) { - 'message.acquire.new.ip'; + return 'message.acquire.new.ip'; }, notification: function(args) { return 'label.acquire.new.ip'; @@ -1138,7 +1168,7 @@ virtualmachineid: args.context.instances[0].id }, success: function(json) { - var ips = json.listnics.nic[0].secondaryip + var ips = json.listnics.nic ? json.listnics.nic[0].secondaryip : []; args.response.success({ data: $(ips).map(function(index, ip) { @@ -1632,6 +1662,7 @@ listView: $.extend(true, {}, cloudStack.sections.instances, { listView: { filters: false, + subselect: instanceSecondaryIPSubselect, dataProvider: function(args) { var data = { page: args.page, @@ -2374,7 +2405,22 @@ loadBalancing: { listView: $.extend(true, {}, cloudStack.sections.instances, { listView: { + fields: { + name: { label: 'label.name' }, + displayname: { label: 'label.display.name' }, + zonename: { label: 'label.zone.name' }, + state: { + label: 'label.state', + indicator: { + 'Running': 'on', + 'Stopped': 'off', + 'Destroyed': 'off', + 'Error': 'off' + } + } + }, filters: false, + subselect: instanceSecondaryIPSubselect, dataProvider: function(args) { var itemData = $.isArray(args.context.multiRule) && args.context.multiRule[0]['_itemData'] ? args.context.multiRule[0]['_itemData'] : []; @@ -2957,6 +3003,7 @@ listView: $.extend(true, {}, cloudStack.sections.instances, { listView: { filters: false, + subselect: instanceSecondaryIPSubselect, dataProvider: function(args) { var networkid; if('vpc' in args.context) diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 2480f56f872..7bce1289720 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -468,6 +468,54 @@ // Change action label $listView.find('th.actions').html(_l('Select')); + // Add sub-select + if (instances.listView.subselect) { + $listView.bind('cloudStack.listView.addRow', function(event, data) { + var $tr = data.$tr; + var $td = $tr.find('td.first'); + var $select = $('
').addClass('subselect').append( + $('') + ).hide(); + var $selectionArea = $tr.find('td:last').find('input'); + + $td.append($select); + + // Show and populate selection + $selectionArea.change(function() { + if ($(this).is(':checked')) { + // Populate data + instances.listView.subselect({ + context: $.extend(true, {}, context, { + instances: [$tr.data('json-obj')] + }), + response: { + success: function(args) { + var data = args.data; + + if (data.length) { + $(data).map(function(index, item) { + var $option = $('