fix: scope trunk nic's associatednetworks to the requested network in listNics
diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index 6e880c8..9f9f1a2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java
@@ -529,6 +529,8 @@ public NicResponse createNicResponse(Nic result); + public NicResponse createNicResponse(Nic result, Long networkId); + ApplicationLoadBalancerResponse createLoadBalancerContainerReponse(ApplicationLoadBalancerRule lb, Map<Ip, UserVm> lbInstances);
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java index 7d2b27e..3ea93e0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java
@@ -143,7 +143,7 @@ if (results != null) { resList = new ArrayList<NicResponse>(results.size()); for (Nic r : results) { - NicResponse resp = _responseGenerator.createNicResponse(r); + NicResponse resp = _responseGenerator.createNicResponse(r, this.getNetworkId()); resp.setObjectName("nic"); resList.add(resp); }
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index bd7e6b8..60c827d 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -2262,9 +2262,21 @@ * identical shape, so it's built once here rather than duplicated per builder. */ public static List<NicNetworkMapResponse> findAssociatedNetworkResponses(long nicId) { + return findAssociatedNetworkResponses(nicId, null); + } + + /** + * Same as {@link #findAssociatedNetworkResponses(long)}, narrowed to the given network when one is passed - + * for callers like {@code listNics?networkid=X} that asked about one specific network and should not get + * back every other network this nic happens to trunk too. + */ + public static List<NicNetworkMapResponse> findAssociatedNetworkResponses(long nicId, Long networkId) { List<NicNetworkMapVO> associations = s_nicNetworkMapDao.listByNicId(nicId); List<NicNetworkMapResponse> associatedNetworkResponses = new ArrayList<>(); for (NicNetworkMapVO association : associations) { + if (networkId != null && association.getNetworkId() != networkId) { + continue; + } NetworkVO associatedNetwork = findNetworkById(association.getNetworkId()); if (associatedNetwork == null) { continue;
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 561ce3d..ba86367 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -4819,6 +4819,16 @@ */ @Override public NicResponse createNicResponse(Nic result) { + return createNicResponse(result, null); + } + + /** + * Same as {@link #createNicResponse(Nic)}, but when {@code networkId} is given (a {@code listNics?networkid=} + * query), the trunk nic's {@code associatednetworks} is narrowed to that one network instead of every + * network the nic trunks - callers that asked about one specific network shouldn't get every other one back. + */ + @Override + public NicResponse createNicResponse(Nic result, Long networkId) { NicResponse response = new NicResponse(); NetworkVO network = _entityMgr.findById(NetworkVO.class, result.getNetworkId()); VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, result.getInstanceId()); @@ -4894,7 +4904,7 @@ /*trunked / associatednetworks - multi-VLAN trunk nic, if any*/ response.setTrunked(result.getMultiNetwork()); if (result.getMultiNetwork()) { - response.setAssociatedNetworks(ApiDBUtils.findAssociatedNetworkResponses(result.getId())); + response.setAssociatedNetworks(ApiDBUtils.findAssociatedNetworkResponses(result.getId(), networkId)); } /*18: extra dhcp options */ List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = nicExtraDhcpOptionVOs
diff --git a/ui/src/utils/multiNetworkNic.js b/ui/src/utils/multiNetworkNic.js index 7997ac3..6f17a08 100644 --- a/ui/src/utils/multiNetworkNic.js +++ b/ui/src/utils/multiNetworkNic.js
@@ -52,15 +52,26 @@ // Each option carries the networkid a rule-creation call (enableStaticNat/createPortForwardingRule/ // assignToLoadBalancerRule) must submit alongside its ip - an associated option's networkid differs from // the nic's own, which is the one real behavioral difference from a secondary IP. -export function buildNicIpOptions (nic) { +// +// targetNetworkId, when given (the rule's own network - e.g. the public IP's network), excludes the +// primary/secondary options when the nic's primary network isn't that one: a trunk nic listed as a +// candidate only because one of its *associated* networks matches must not also offer its unrelated +// primary IP, which is never valid for this rule. +export function buildNicIpOptions (nic, targetNetworkId) { const options = [] - if (nic.ipaddress) { + const primaryMatches = !targetNetworkId || nic.networkid === targetNetworkId + if (nic.ipaddress && primaryMatches) { options.push({ kind: 'primary', ip: nic.ipaddress, networkid: nic.networkid, networkname: nic.networkname, nicid: nic.id }) } - for (const secondary of (nic.secondaryip || [])) { - options.push({ kind: 'secondary', ip: secondary.ipaddress, networkid: nic.networkid, networkname: nic.networkname, nicid: nic.id }) + if (primaryMatches) { + for (const secondary of (nic.secondaryip || [])) { + options.push({ kind: 'secondary', ip: secondary.ipaddress, networkid: nic.networkid, networkname: nic.networkname, nicid: nic.id }) + } } for (const association of (nic.associatednetworks || [])) { + if (targetNetworkId && association.networkid !== targetNetworkId) { + continue + } options.push({ kind: 'associated', ip: association.ipaddress, networkid: association.networkid, networkname: association.networkname, nicid: nic.id }) } return options
diff --git a/ui/src/views/network/EnableStaticNat.vue b/ui/src/views/network/EnableStaticNat.vue index bc955cf..2e94946 100644 --- a/ui/src/views/network/EnableStaticNat.vue +++ b/ui/src/views/network/EnableStaticNat.vue
@@ -230,12 +230,13 @@ this.selectedVm = e.target.value this.nicIpOptions = [] this.loading = true + const networkId = this.resource.associatednetworkid || this.selectedVpcTier getAPI('listNics', { virtualmachineid: this.selectedVm, - networkid: this.resource.associatednetworkid || this.selectedVpcTier + networkid: networkId }).then(response => { const nics = response.listnicsresponse.nic || [] - this.nicIpOptions = nics.flatMap(nic => buildNicIpOptions(nic)) + this.nicIpOptions = nics.flatMap(nic => buildNicIpOptions(nic, networkId)) this.selectedNic = this.nicIpOptions[0] }).catch(error => { this.$notifyError(error)
diff --git a/ui/src/views/network/InternalLBAssignVmForm.vue b/ui/src/views/network/InternalLBAssignVmForm.vue index 73f61f3..2dd3308 100644 --- a/ui/src/views/network/InternalLBAssignVmForm.vue +++ b/ui/src/views/network/InternalLBAssignVmForm.vue
@@ -188,13 +188,14 @@ } this.iLb.virtualmachineid[index] = e.target.value this.addVmModalNicLoading = true + const networkId = this.resource.networkid getAPI('listNics', { virtualmachineid: e.target.value, - networkid: this.resource.networkid + networkid: networkId }).then(response => { if (!response.listnicsresponse.nic[0]) return const nicsReturned = response.listnicsresponse.nic || [] - this.nics[index] = nicsReturned.flatMap(nic => buildNicIpOptions(nic)) + this.nics[index] = nicsReturned.flatMap(nic => buildNicIpOptions(nic, networkId)) this.iLb.vmguestip[index] = this.nics[index][0]?.ip this.addVmModalNicLoading = false }).catch(error => {
diff --git a/ui/src/views/network/LoadBalancing.vue b/ui/src/views/network/LoadBalancing.vue index 58fd14e..fc93e5a 100644 --- a/ui/src/views/network/LoadBalancing.vue +++ b/ui/src/views/network/LoadBalancing.vue
@@ -1854,13 +1854,14 @@ this.newRule.virtualmachineid[index] = e.target.value this.addVmModalNicLoading = true + const networkId = ('vpcid' in this.resource && (!('associatednetworkid' in this.resource) || this.vpcConserveMode)) ? this.selectedTier : this.resource.associatednetworkid getAPI('listNics', { virtualmachineid: e.target.value, - networkid: ('vpcid' in this.resource && (!('associatednetworkid' in this.resource) || this.vpcConserveMode)) ? this.selectedTier : this.resource.associatednetworkid + networkid: networkId }).then(response => { if (!response || !response.listnicsresponse || !response.listnicsresponse.nic[0]) return const nicsReturned = response.listnicsresponse.nic || [] - this.nics[index] = nicsReturned.flatMap(nic => buildNicIpOptions(nic)) + this.nics[index] = nicsReturned.flatMap(nic => buildNicIpOptions(nic, networkId)) this.newRule.vmguestip[index] = [this.nics[index][0]?.ip] this.addVmModalNicLoading = false }).catch(error => {
diff --git a/ui/src/views/network/PortForwarding.vue b/ui/src/views/network/PortForwarding.vue index 4026202..4a40304 100644 --- a/ui/src/views/network/PortForwarding.vue +++ b/ui/src/views/network/PortForwarding.vue
@@ -816,12 +816,13 @@ this.nicIpOptions = [] this.addVmModalNicLoading = true this.newRule.virtualmachineid = e.target.value + const networkId = ('vpcid' in this.resource && (!('associatednetworkid' in this.resource) || this.vpcConserveMode)) ? this.selectedTier : this.resource.associatednetworkid getAPI('listNics', { virtualmachineid: e.target.value, - networkid: ('vpcid' in this.resource && (!('associatednetworkid' in this.resource) || this.vpcConserveMode)) ? this.selectedTier : this.resource.associatednetworkid + networkid: networkId }).then(response => { const nics = response.listnicsresponse.nic || [] - this.nicIpOptions = nics.flatMap(nic => buildNicIpOptions(nic)) + this.nicIpOptions = nics.flatMap(nic => buildNicIpOptions(nic, networkId)) this.newRule.vmguestip = this.nicIpOptions[0]?.ip this.addVmModalNicLoading = false }).catch(error => {