You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2025/03/05 22:51:49 UTC
(cloudstack) 19/44: List only Netris Public IPs for NAT operations (#26)
This is an automated email from the ASF dual-hosted git repository.
pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit aef61973f3ee281cb599c58db9847d270204fd15
Author: Pearl Dsilva <pe...@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 <ni...@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 | 32 ++++++++++++++++--
.../com/cloud/server/ManagementServerImplTest.java | 10 ++++++
ui/src/views/AutogenView.vue | 17 +++++++---
ui/src/views/network/IpAddressesTab.vue | 22 ++++++++++--
9 files changed, 128 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 b0d01e89c7d..c0b478977c8 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -210,6 +210,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 2f5c6d2832a..43b3932b00c 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 e6be174abcd..e440d39462e 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;
@@ -1301,6 +1314,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, long callerUserId, final DataCenter zone, final Boolean displayIp, final String ipaddress)
@@ -1309,6 +1346,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 87b3dd3cbd9..c071d3626ed 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -44,6 +44,11 @@ import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+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;
@@ -885,6 +890,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject
private VlanDao _vlanDao;
@Inject
+ private VlanDetailsDao vlanDetailsDao;
+ @Inject
private AccountVlanMapDao _accountVlanMapDao;
@Inject
private PodVlanMapDao _podVlanMapDao;
@@ -927,7 +934,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject
private StoragePoolJoinDao _poolJoinDao;
@Inject
- private NetworkDao networkDao;
+ protected NetworkDao networkDao;
@Inject
private StorageManager _storageMgr;
@Inject
@@ -997,7 +1004,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
@Inject
private NetworkModel _networkMgr;
@Inject
- private VpcDao _vpcDao;
+ protected VpcDao _vpcDao;
@Inject
private DomainVlanMapDao _domainVlanMapDao;
@Inject
@@ -1027,6 +1034,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"));
@@ -2571,6 +2582,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);
@@ -2616,13 +2628,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();
@@ -2631,6 +2651,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);
@@ -2696,6 +2717,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 bfdde77191e..54524ccc6b8 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -1001,6 +1001,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 = []
}
@@ -1931,7 +1944,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)
@@ -1942,20 +1954,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)