Skip to content

Commit aef6197

Browse files
Pearl1594nvazquez
andauthored
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 <[email protected]>
1 parent ce9cbb2 commit aef6197

File tree

9 files changed

+128
-9
lines changed

9 files changed

+128
-9
lines changed

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ public class ApiConstants {
210210
public static final String FORMAT = "format";
211211
public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
212212
public static final String FOR_SYSTEM_VMS = "forsystemvms";
213+
public static final String FOR_PROVIDER = "forprovider";
213214
public static final String FULL_PATH = "fullpath";
214215
public static final String GATEWAY = "gateway";
215216
public static final String IP6_GATEWAY = "ip6gateway";

api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC
108108
@Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0")
109109
private Boolean forSystemVMs;
110110

111+
@Parameter(name = ApiConstants.FOR_PROVIDER, type = CommandType.BOOLEAN, description = "true if range is dedicated for external network provider", since = "4.20.0")
112+
private Boolean forProvider;
113+
111114
/////////////////////////////////////////////////////
112115
/////////////////// Accessors ///////////////////////
113116
/////////////////////////////////////////////////////
@@ -183,6 +186,10 @@ public boolean getForSystemVMs() {
183186
return BooleanUtils.isTrue(forSystemVMs);
184187
}
185188

189+
public boolean isForProvider() {
190+
return BooleanUtils.isTrue(forProvider);
191+
}
192+
186193
/////////////////////////////////////////////////////
187194
/////////////// API Implementation///////////////////
188195
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
175175
@Param(description="true if range is dedicated for System VMs")
176176
private boolean forSystemVms;
177177

178+
@SerializedName(ApiConstants.FOR_PROVIDER)
179+
@Param(description="true if range is dedicated for external network providers")
180+
private boolean forProvider;
181+
178182
public void setIpAddress(String ipAddress) {
179183
this.ipAddress = ipAddress;
180184
}
@@ -332,4 +336,6 @@ public void setHasRules(final boolean hasRules) {
332336
public void setForSystemVms(boolean forSystemVms) {
333337
this.forSystemVms = forSystemVms;
334338
}
339+
340+
public void setForProvider(boolean forProvider) { this.forProvider = forProvider; }
335341
}

server/src/main/java/com/cloud/api/ApiResponseHelper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,9 @@ public IPAddressResponse createIPAddressResponse(ResponseView view, IpAddress ip
11681168

11691169
ipResponse.setPortable(ipAddr.isPortable());
11701170
ipResponse.setForSystemVms(ipAddr.isForSystemVms());
1171+
if (Objects.nonNull(getProviderFromVlanDetailKey(vlan))) {
1172+
ipResponse.setForProvider(true);
1173+
}
11711174

11721175
//set tag information
11731176
List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(ResourceObjectType.PublicIpAddress, ipAddr.getId());

server/src/main/java/com/cloud/network/IpAddressManagerImpl.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@
3333

3434
import javax.inject.Inject;
3535

36+
import com.cloud.dc.VlanDetailsVO;
37+
import com.cloud.dc.dao.VlanDetailsDao;
38+
import com.cloud.network.dao.NetrisProviderDao;
39+
import com.cloud.network.dao.NsxProviderDao;
3640
import com.cloud.network.dao.PublicIpQuarantineDao;
41+
import com.cloud.network.element.NetrisProviderVO;
42+
import com.cloud.network.element.NsxProviderVO;
3743
import com.cloud.network.vo.PublicIpQuarantineVO;
3844
import com.cloud.resourcelimit.CheckedReservation;
3945
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
@@ -189,6 +195,7 @@
189195
import com.cloud.vm.dao.NicSecondaryIpDao;
190196
import com.cloud.vm.dao.UserVmDao;
191197
import com.cloud.vm.dao.VMInstanceDao;
198+
import org.apache.commons.lang3.ObjectUtils;
192199

193200
public class IpAddressManagerImpl extends ManagerBase implements IpAddressManager, Configurable {
194201

@@ -313,6 +320,12 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
313320
private AnnotationDao annotationDao;
314321
@Inject
315322
MessageBus messageBus;
323+
@Inject
324+
NsxProviderDao nsxProviderDao;
325+
@Inject
326+
NetrisProviderDao netrisProviderDao;
327+
@Inject
328+
VlanDetailsDao vlanDetailsDao;
316329

317330
@Inject
318331
PublicIpQuarantineDao publicIpQuarantineDao;
@@ -1301,6 +1314,30 @@ public void releasePodIp(Long id) throws CloudRuntimeException {
13011314
}
13021315
}
13031316

1317+
/**
1318+
* When the zone is linked to external provider NSX or Netris: check if the IP to be associated is from the suitable pool
1319+
* Otherwise, no checks are performed
1320+
*/
1321+
private void checkPublicIpOnExternalProviderZone(DataCenter zone, String ip) {
1322+
long zoneId = zone.getId();
1323+
NetrisProviderVO netrisProvider = netrisProviderDao.findByZoneId(zoneId);
1324+
NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId);
1325+
if (ObjectUtils.allNull(netrisProvider, nsxProvider)) {
1326+
return;
1327+
}
1328+
IPAddressVO ipAddress = _ipAddressDao.findByIpAndDcId(zoneId, ip);
1329+
if (ipAddress != null) {
1330+
String detailKey = nsxProvider != null ? ApiConstants.NSX_DETAIL_KEY : ApiConstants.NETRIS_DETAIL_KEY;
1331+
VlanDetailsVO vlanDetailVO = vlanDetailsDao.findDetail(ipAddress.getVlanId(), detailKey);
1332+
if (vlanDetailVO == null || vlanDetailVO.getValue().equalsIgnoreCase("false")) {
1333+
String msg = String.format("Cannot acquire IP %s on the zone %s as the IP is not from the reserved pool " +
1334+
"for the external provider", ip, zone.getName());
1335+
logger.error(msg);
1336+
throw new CloudRuntimeException(msg);
1337+
}
1338+
}
1339+
}
1340+
13041341
@DB
13051342
@Override
13061343
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 IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Accou
13091346
final VlanType vlanType = VlanType.VirtualNetwork;
13101347
final boolean assign = false;
13111348

1349+
checkPublicIpOnExternalProviderZone(zone, ipaddress);
1350+
13121351
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
13131352
// zone is of type DataCenter. See DataCenterVO.java.
13141353
PermissionDeniedException ex = new PermissionDeniedException(generateErrorMessageForOperationOnDisabledZone("allocate IP addresses", zone));

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
import javax.inject.Inject;
4545
import javax.naming.ConfigurationException;
4646

47+
import com.cloud.dc.VlanDetailsVO;
48+
import com.cloud.dc.dao.VlanDetailsDao;
49+
import com.cloud.network.dao.NetrisProviderDao;
50+
import com.cloud.network.dao.NsxProviderDao;
51+
4752
import com.cloud.utils.security.CertificateHelper;
4853
import org.apache.cloudstack.acl.ControlledEntity;
4954
import org.apache.cloudstack.acl.SecurityChecker;
@@ -885,6 +890,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
885890
@Inject
886891
private VlanDao _vlanDao;
887892
@Inject
893+
private VlanDetailsDao vlanDetailsDao;
894+
@Inject
888895
private AccountVlanMapDao _accountVlanMapDao;
889896
@Inject
890897
private PodVlanMapDao _podVlanMapDao;
@@ -927,7 +934,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
927934
@Inject
928935
private StoragePoolJoinDao _poolJoinDao;
929936
@Inject
930-
private NetworkDao networkDao;
937+
protected NetworkDao networkDao;
931938
@Inject
932939
private StorageManager _storageMgr;
933940
@Inject
@@ -997,7 +1004,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
9971004
@Inject
9981005
private NetworkModel _networkMgr;
9991006
@Inject
1000-
private VpcDao _vpcDao;
1007+
protected VpcDao _vpcDao;
10011008
@Inject
10021009
private DomainVlanMapDao _domainVlanMapDao;
10031010
@Inject
@@ -1027,6 +1034,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
10271034
protected AffinityGroupVMMapDao _affinityGroupVMMapDao;
10281035
@Inject
10291036
ResourceLimitService resourceLimitService;
1037+
@Inject
1038+
NsxProviderDao nsxProviderDao;
1039+
@Inject
1040+
NetrisProviderDao netrisProviderDao;
10301041

10311042
private LockControllerListener _lockControllerListener;
10321043
private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
@@ -2571,6 +2582,7 @@ private void buildParameters(final SearchBuilder<IPAddressVO> sb, final ListPubl
25712582
final String address = cmd.getIpAddress();
25722583
final Boolean forLoadBalancing = cmd.isForLoadBalancing();
25732584
final Map<String, String> tags = cmd.getTags();
2585+
boolean forProvider = cmd.isForProvider();
25742586

25752587
sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
25762588
sb.and("address", sb.entity().getAddress(), SearchCriteria.Op.EQ);
@@ -2616,13 +2628,21 @@ private void buildParameters(final SearchBuilder<IPAddressVO> sb, final ListPubl
26162628
if (isAllocated != null && isAllocated) {
26172629
sb.and("allocated", sb.entity().getAllocatedTime(), SearchCriteria.Op.NNULL);
26182630
}
2631+
2632+
if (forProvider) {
2633+
SearchBuilder<VlanDetailsVO> vlanDetailsSearch = vlanDetailsDao.createSearchBuilder();
2634+
vlanDetailsSearch.and("name", vlanDetailsSearch.entity().getName(), SearchCriteria.Op.IN);
2635+
vlanDetailsSearch.and("value", vlanDetailsSearch.entity().getValue(), SearchCriteria.Op.EQ);
2636+
sb.join("vlanDetailSearch", vlanDetailsSearch, sb.entity().getVlanId(), vlanDetailsSearch.entity().getResourceId(), JoinType.LEFT);
2637+
}
26192638
}
26202639

26212640
protected void setParameters(SearchCriteria<IPAddressVO> sc, final ListPublicIpAddressesCmd cmd, VlanType vlanType, Boolean isAllocated) {
26222641
final Object keyword = cmd.getKeyword();
26232642
final Long physicalNetworkId = cmd.getPhysicalNetworkId();
26242643
final Long sourceNetworkId = cmd.getNetworkId();
2625-
final Long zone = cmd.getZoneId();
2644+
final Long vpcId = cmd.getVpcId();
2645+
Long zone = cmd.getZoneId();
26262646
final String address = cmd.getIpAddress();
26272647
final Long vlan = cmd.getVlanId();
26282648
final Long ipId = cmd.getId();
@@ -2631,6 +2651,7 @@ protected void setParameters(SearchCriteria<IPAddressVO> sc, final ListPublicIpA
26312651
final Boolean forDisplay = cmd.getDisplay();
26322652
final String state = cmd.getState();
26332653
final Boolean forSystemVms = cmd.getForSystemVMs();
2654+
final boolean forProvider = cmd.isForProvider();
26342655
final Map<String, String> tags = cmd.getTags();
26352656

26362657
sc.setJoinParameters("vlanSearch", "vlanType", vlanType);
@@ -2696,6 +2717,11 @@ protected void setParameters(SearchCriteria<IPAddressVO> sc, final ListPublicIpA
26962717
} else {
26972718
sc.setParameters(FOR_SYSTEMVMS, forSystemVms);
26982719
}
2720+
2721+
if (forProvider) {
2722+
sc.setJoinParameters("vlanDetailSearch", "name", ApiConstants.NETRIS_DETAIL_KEY, ApiConstants.NSX_DETAIL_KEY);
2723+
sc.setJoinParameters("vlanDetailSearch", "value", "true");
2724+
}
26992725
}
27002726

27012727
@Override

server/src/test/java/com/cloud/server/ManagementServerImplTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import com.cloud.network.IpAddress;
2626
import com.cloud.network.IpAddressManagerImpl;
2727
import com.cloud.network.dao.IPAddressVO;
28+
import com.cloud.network.dao.NetworkDao;
29+
import com.cloud.network.vpc.dao.VpcDao;
2830
import com.cloud.storage.VMTemplateVO;
2931
import com.cloud.storage.dao.VMTemplateDao;
3032
import com.cloud.user.Account;
@@ -123,6 +125,12 @@ public class ManagementServerImplTest {
123125
@Mock
124126
UserDataManager userDataManager;
125127

128+
@Mock
129+
VpcDao vpcDao;
130+
131+
@Mock
132+
NetworkDao networkDao;
133+
126134
@Spy
127135
ManagementServerImpl spy = new ManagementServerImpl();
128136

@@ -145,6 +153,8 @@ public void setup() throws IllegalAccessException, NoSuchFieldException {
145153
spy._UserVmDetailsDao = userVmDetailsDao;
146154
spy._detailsDao = hostDetailsDao;
147155
spy.userDataManager = userDataManager;
156+
spy._vpcDao = vpcDao;
157+
spy.networkDao = networkDao;
148158
}
149159

150160
@After

ui/src/views/AutogenView.vue

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,19 @@ export default {
10011001
}
10021002
10031003
this.items = json[responseName][objectName]
1004+
var filteredItems = []
1005+
if (this.apiName === 'listPublicIpAddresses') {
1006+
for (var zone of this.$store.getters.zones) {
1007+
const zoneIps = this.items.filter(item => item.zoneid === zone.id)
1008+
const providerIps = zoneIps.filter(item => item.forprovider === true)
1009+
if (providerIps.length === 0) {
1010+
filteredItems.push(...zoneIps)
1011+
} else {
1012+
filteredItems.push(...providerIps)
1013+
}
1014+
}
1015+
this.items = filteredItems
1016+
}
10041017
if (!this.items || this.items.length === 0) {
10051018
this.items = []
10061019
}
@@ -1931,7 +1944,6 @@ export default {
19311944
this.rules[field.name].push(rule)
19321945
break
19331946
case (this.currentAction.mapping && field.name in this.currentAction.mapping && 'options' in this.currentAction.mapping[field.name]):
1934-
console.log('op: ' + field)
19351947
rule.required = field.required
19361948
rule.message = this.$t('message.error.select')
19371949
this.rules[field.name].push(rule)
@@ -1942,20 +1954,17 @@ export default {
19421954
this.rules[field.name].push(rule)
19431955
break
19441956
case (field.type === 'uuid'):
1945-
console.log('uuid: ' + field)
19461957
rule.required = field.required
19471958
rule.message = this.$t('message.error.select')
19481959
this.rules[field.name].push(rule)
19491960
break
19501961
case (field.type === 'list'):
1951-
console.log('list: ' + field)
19521962
rule.type = 'array'
19531963
rule.required = field.required
19541964
rule.message = this.$t('message.error.select')
19551965
this.rules[field.name].push(rule)
19561966
break
19571967
case (field.type === 'long'):
1958-
console.log(field)
19591968
rule.type = 'number'
19601969
rule.required = field.required
19611970
rule.message = this.$t('message.validate.number')

ui/src/views/network/IpAddressesTab.vue

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,12 @@ export default {
282282
acquireLoading: false,
283283
acquireIp: null,
284284
listPublicIpAddress: [],
285-
changeSourceNat: false
285+
changeSourceNat: false,
286+
zoneExtNetProvider: ''
286287
}
287288
},
288-
created () {
289+
async created () {
290+
await this.fetchZones()
289291
this.fetchData()
290292
},
291293
watch: {
@@ -321,6 +323,9 @@ export default {
321323
} else {
322324
params.associatednetworkid = this.resource.id
323325
}
326+
if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) {
327+
params.forprovider = true
328+
}
324329
this.fetchLoading = true
325330
api('listPublicIpAddresses', params).then(json => {
326331
this.totalIps = json.listpublicipaddressesresponse.count || 0
@@ -329,6 +334,16 @@ export default {
329334
this.fetchLoading = false
330335
})
331336
},
337+
fetchZones () {
338+
return new Promise((resolve, reject) => {
339+
api('listZones', {
340+
id: this.resource.zoneid
341+
}).then(json => {
342+
this.zoneExtNetProvider = json?.listzonesresponse?.zone?.[0]?.provider || null
343+
resolve(this.zoneExtNetProvider)
344+
}).catch(reject)
345+
})
346+
},
332347
fetchListPublicIpAddress () {
333348
return new Promise((resolve, reject) => {
334349
const params = {
@@ -338,6 +353,9 @@ export default {
338353
forvirtualnetwork: true,
339354
allocatedonly: false
340355
}
356+
if (['nsx', 'netris'].includes(this.zoneExtNetProvider?.toLowerCase())) {
357+
params.forprovider = true
358+
}
341359
api('listPublicIpAddresses', params).then(json => {
342360
const listPublicIps = json.listpublicipaddressesresponse.publicipaddress || []
343361
resolve(listPublicIps)

0 commit comments

Comments
 (0)