This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch netris-integration-upstream in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 7bbd2495033505685dffeb5c727be13edf45e5ee Author: Pearl Dsilva <pearl1...@gmail.com> AuthorDate: Thu Dec 5 10:33:10 2024 -0500 List only Netris Public IPs for NAT operations (#26) * List only Netris Public IPs for NAT operations * rename getter and change type * fix failing unit tests * list all IPs if forProvider is not passed * fix list public IPs for external providers with additional IP range * filter provider Ips in a zone with external provider setup * Prevent acquiring IP that is not from the external provider range * formating --------- Co-authored-by: nvazquez <nicovazque...@gmail.com> --- .../org/apache/cloudstack/api/ApiConstants.java | 1 + .../user/address/ListPublicIpAddressesCmd.java | 7 ++++ .../cloudstack/api/response/IPAddressResponse.java | 6 ++++ .../main/java/com/cloud/api/ApiResponseHelper.java | 3 ++ .../com/cloud/network/IpAddressManagerImpl.java | 39 ++++++++++++++++++++++ .../com/cloud/server/ManagementServerImpl.java | 33 ++++++++++++++++-- .../com/cloud/server/ManagementServerImplTest.java | 10 ++++++ ui/src/views/AutogenView.vue | 17 +++++++--- ui/src/views/network/IpAddressesTab.vue | 22 ++++++++++-- 9 files changed, 129 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 6182cdec824..0849e747774 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -212,6 +212,7 @@ public class ApiConstants { public static final String FORMAT = "format"; public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork"; public static final String FOR_SYSTEM_VMS = "forsystemvms"; + public static final String FOR_PROVIDER = "forprovider"; public static final String FULL_PATH = "fullpath"; public static final String GATEWAY = "gateway"; public static final String IP6_GATEWAY = "ip6gateway"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java index 5760ca3ba1c..a9d651ae5fe 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java @@ -108,6 +108,9 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0") private Boolean forSystemVMs; + @Parameter(name = ApiConstants.FOR_PROVIDER, type = CommandType.BOOLEAN, description = "true if range is dedicated for external network provider", since = "4.20.0") + private Boolean forProvider; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -183,6 +186,10 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC return BooleanUtils.isTrue(forSystemVMs); } + public boolean isForProvider() { + return BooleanUtils.isTrue(forProvider); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java index 0018edc8638..0f339d5d5b1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -175,6 +175,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co @Param(description="true if range is dedicated for System VMs") private boolean forSystemVms; + @SerializedName(ApiConstants.FOR_PROVIDER) + @Param(description="true if range is dedicated for external network providers") + private boolean forProvider; + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } @@ -332,4 +336,6 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co public void setForSystemVms(boolean forSystemVms) { this.forSystemVms = forSystemVms; } + + public void setForProvider(boolean forProvider) { this.forProvider = forProvider; } } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index c54302e6ae7..24dd07d1097 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -1168,6 +1168,9 @@ public class ApiResponseHelper implements ResponseGenerator { ipResponse.setPortable(ipAddr.isPortable()); ipResponse.setForSystemVms(ipAddr.isForSystemVms()); + if (Objects.nonNull(getProviderFromVlanDetailKey(vlan))) { + ipResponse.setForProvider(true); + } //set tag information List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(ResourceObjectType.PublicIpAddress, ipAddr.getId()); diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java index 1fa416b643b..32a37aa7277 100644 --- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java @@ -33,7 +33,13 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.network.dao.NetrisProviderDao; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.PublicIpQuarantineDao; +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.network.element.NsxProviderVO; import com.cloud.network.vo.PublicIpQuarantineVO; import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity.ACLType; @@ -189,6 +195,7 @@ import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import org.apache.commons.lang3.ObjectUtils; public class IpAddressManagerImpl extends ManagerBase implements IpAddressManager, Configurable { @@ -313,6 +320,12 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage private AnnotationDao annotationDao; @Inject MessageBus messageBus; + @Inject + NsxProviderDao nsxProviderDao; + @Inject + NetrisProviderDao netrisProviderDao; + @Inject + VlanDetailsDao vlanDetailsDao; @Inject PublicIpQuarantineDao publicIpQuarantineDao; @@ -1303,6 +1316,30 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage } } + /** + * When the zone is linked to external provider NSX or Netris: check if the IP to be associated is from the suitable pool + * Otherwise, no checks are performed + */ + private void checkPublicIpOnExternalProviderZone(DataCenter zone, String ip) { + long zoneId = zone.getId(); + NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId); + NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId); + if (ObjectUtils.allNull(netrisProvider, nsxProvider)) { + return; + } + IPAddressVO ipAddress = _ipAddressDao.findByIpAndDcId(zoneId, ip); + if (ipAddress != null) { + String detailKey = nsxProvider != null ? ApiConstants.NSX_DETAIL_KEY : ApiConstants.NETRIS_DETAIL_KEY; + VlanDetailsVO vlanDetailVO = vlanDetailsDao.findDetail(ipAddress.getVlanId(), detailKey); + if (vlanDetailVO == null || vlanDetailVO.getValue().equalsIgnoreCase("false")) { + String msg = String.format("Cannot acquire IP %s on the zone %s as the IP is not from the reserved pool " + + "for the external provider", ip, zone.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + } + @DB @Override public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, User callerUser, final DataCenter zone, final Boolean displayIp, final String ipaddress) @@ -1311,6 +1348,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage final VlanType vlanType = VlanType.VirtualNetwork; final boolean assign = false; + checkPublicIpOnExternalProviderZone(zone, ipaddress); + if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) { // zone is of type DataCenter. See DataCenterVO.java. PermissionDeniedException ex = new PermissionDeniedException(generateErrorMessageForOperationOnDisabledZone("allocate IP addresses", zone)); diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 8575a26a5cb..f5fcfe92ca2 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -45,6 +45,12 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.cpu.CPU; +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.network.dao.NetrisProviderDao; +import com.cloud.network.dao.NsxProviderDao; + +import com.cloud.utils.security.CertificateHelper; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroupProcessor; @@ -881,6 +887,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private VlanDao _vlanDao; @Inject + private VlanDetailsDao vlanDetailsDao; + @Inject private AccountVlanMapDao _accountVlanMapDao; @Inject private PodVlanMapDao _podVlanMapDao; @@ -923,7 +931,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private StoragePoolJoinDao _poolJoinDao; @Inject - private NetworkDao networkDao; + protected NetworkDao networkDao; @Inject private StorageManager _storageMgr; @Inject @@ -993,7 +1001,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private NetworkModel _networkMgr; @Inject - private VpcDao _vpcDao; + protected VpcDao _vpcDao; @Inject private DomainVlanMapDao _domainVlanMapDao; @Inject @@ -1023,6 +1031,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe protected AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject ResourceLimitService resourceLimitService; + @Inject + NsxProviderDao nsxProviderDao; + @Inject + NetrisProviderDao netrisProviderDao; private LockControllerListener _lockControllerListener; private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker")); @@ -2575,6 +2587,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final String address = cmd.getIpAddress(); final Boolean forLoadBalancing = cmd.isForLoadBalancing(); final Map<String, String> tags = cmd.getTags(); + boolean forProvider = cmd.isForProvider(); sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("address", sb.entity().getAddress(), SearchCriteria.Op.EQ); @@ -2620,13 +2633,21 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (isAllocated != null && isAllocated) { sb.and("allocated", sb.entity().getAllocatedTime(), SearchCriteria.Op.NNULL); } + + if (forProvider) { + SearchBuilder<VlanDetailsVO> vlanDetailsSearch = vlanDetailsDao.createSearchBuilder(); + vlanDetailsSearch.and("name", vlanDetailsSearch.entity().getName(), SearchCriteria.Op.IN); + vlanDetailsSearch.and("value", vlanDetailsSearch.entity().getValue(), SearchCriteria.Op.EQ); + sb.join("vlanDetailSearch", vlanDetailsSearch, sb.entity().getVlanId(), vlanDetailsSearch.entity().getResourceId(), JoinType.LEFT); + } } protected void setParameters(SearchCriteria<IPAddressVO> sc, final ListPublicIpAddressesCmd cmd, VlanType vlanType, Boolean isAllocated) { final Object keyword = cmd.getKeyword(); final Long physicalNetworkId = cmd.getPhysicalNetworkId(); final Long sourceNetworkId = cmd.getNetworkId(); - final Long zone = cmd.getZoneId(); + final Long vpcId = cmd.getVpcId(); + Long zone = cmd.getZoneId(); final String address = cmd.getIpAddress(); final Long vlan = cmd.getVlanId(); final Long ipId = cmd.getId(); @@ -2635,6 +2656,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final Boolean forDisplay = cmd.getDisplay(); final String state = cmd.getState(); final Boolean forSystemVms = cmd.getForSystemVMs(); + final boolean forProvider = cmd.isForProvider(); final Map<String, String> tags = cmd.getTags(); sc.setJoinParameters("vlanSearch", "vlanType", vlanType); @@ -2700,6 +2722,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } else { sc.setParameters(FOR_SYSTEMVMS, forSystemVms); } + + if (forProvider) { + sc.setJoinParameters("vlanDetailSearch", "name", ApiConstants.NETRIS_DETAIL_KEY, ApiConstants.NSX_DETAIL_KEY); + sc.setJoinParameters("vlanDetailSearch", "value", "true"); + } } @Override diff --git a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java index b26cd455cfb..7d828236ab3 100644 --- a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java +++ b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java @@ -25,6 +25,8 @@ import com.cloud.host.dao.HostDetailsDao; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManagerImpl; import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; @@ -123,6 +125,12 @@ public class ManagementServerImplTest { @Mock UserDataManager userDataManager; + @Mock + VpcDao vpcDao; + + @Mock + NetworkDao networkDao; + @Spy ManagementServerImpl spy = new ManagementServerImpl(); @@ -145,6 +153,8 @@ public class ManagementServerImplTest { spy._UserVmDetailsDao = userVmDetailsDao; spy._detailsDao = hostDetailsDao; spy.userDataManager = userDataManager; + spy._vpcDao = vpcDao; + spy.networkDao = networkDao; } @After diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index 27445ddeb29..972f0b2cb7a 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -1004,6 +1004,19 @@ export default { } this.items = json[responseName][objectName] + var filteredItems = [] + if (this.apiName === 'listPublicIpAddresses') { + for (var zone of this.$store.getters.zones) { + const zoneIps = this.items.filter(item => item.zoneid === zone.id) + const providerIps = zoneIps.filter(item => item.forprovider === true) + if (providerIps.length === 0) { + filteredItems.push(...zoneIps) + } else { + filteredItems.push(...providerIps) + } + } + this.items = filteredItems + } if (!this.items || this.items.length === 0) { this.items = [] } @@ -1934,7 +1947,6 @@ export default { this.rules[field.name].push(rule) break case (this.currentAction.mapping && field.name in this.currentAction.mapping && 'options' in this.currentAction.mapping[field.name]): - console.log('op: ' + field) rule.required = field.required rule.message = this.$t('message.error.select') this.rules[field.name].push(rule) @@ -1945,20 +1957,17 @@ export default { this.rules[field.name].push(rule) break case (field.type === 'uuid'): - console.log('uuid: ' + field) rule.required = field.required rule.message = this.$t('message.error.select') this.rules[field.name].push(rule) break case (field.type === 'list'): - console.log('list: ' + field) rule.type = 'array' rule.required = field.required rule.message = this.$t('message.error.select') this.rules[field.name].push(rule) break case (field.type === 'long'): - console.log(field) rule.type = 'number' rule.required = field.required rule.message = this.$t('message.validate.number') diff --git a/ui/src/views/network/IpAddressesTab.vue b/ui/src/views/network/IpAddressesTab.vue index 37b5b210a96..8f232129381 100644 --- a/ui/src/views/network/IpAddressesTab.vue +++ b/ui/src/views/network/IpAddressesTab.vue @@ -282,10 +282,12 @@ export default { acquireLoading: false, acquireIp: null, listPublicIpAddress: [], - changeSourceNat: false + changeSourceNat: false, + zoneExtNetProvider: '' } }, - created () { + async created () { + await this.fetchZones() this.fetchData() }, watch: { @@ -321,6 +323,9 @@ export default { } else { params.associatednetworkid = this.resource.id } + if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) { + params.forprovider = true + } this.fetchLoading = true api('listPublicIpAddresses', params).then(json => { this.totalIps = json.listpublicipaddressesresponse.count || 0 @@ -329,6 +334,16 @@ export default { this.fetchLoading = false }) }, + fetchZones () { + return new Promise((resolve, reject) => { + api('listZones', { + id: this.resource.zoneid + }).then(json => { + this.zoneExtNetProvider = json?.listzonesresponse?.zone?.[0]?.provider || null + resolve(this.zoneExtNetProvider) + }).catch(reject) + }) + }, fetchListPublicIpAddress () { return new Promise((resolve, reject) => { const params = { @@ -338,6 +353,9 @@ export default { forvirtualnetwork: true, allocatedonly: false } + if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) { + params.forprovider = true + } api('listPublicIpAddresses', params).then(json => { const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || [] resolve(listPublicIps)