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)

Reply via email to