Skip to content

Commit e333f27

Browse files
user-shared networks: fix few issues (#6887)
This PR fixes few issues: - check ip range of new network instead of network cidr, so that the two networks can use same cidr but no IP conflicts. - Private gateways: return vlan number only for root admins - when update isolated network, check new guest vm cidr and IPs of neworks/vpc gateways associated to it
1 parent 72cf974 commit e333f27

File tree

8 files changed

+93
-28
lines changed

8 files changed

+93
-28
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachine
438438
* @param result
439439
* @return
440440
*/
441-
PrivateGatewayResponse createPrivateGatewayResponse(PrivateGateway result);
441+
PrivateGatewayResponse createPrivateGatewayResponse(ResponseView view, PrivateGateway result);
442442

443443
/**
444444
* @param result
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package org.apache.cloudstack.api.command.admin.vpc;
18+
19+
import org.apache.log4j.Logger;
20+
21+
import org.apache.cloudstack.api.APICommand;
22+
import org.apache.cloudstack.api.ResponseObject;
23+
import org.apache.cloudstack.api.command.admin.AdminCmd;
24+
import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd;
25+
import org.apache.cloudstack.api.response.PrivateGatewayResponse;
26+
27+
import com.cloud.network.vpc.VpcGateway;
28+
29+
@APICommand(name = "listPrivateGateways", description = "List private gateways", responseObject = PrivateGatewayResponse.class, entityType = {VpcGateway.class},
30+
responseView = ResponseObject.ResponseView.Full,
31+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
32+
public class ListPrivateGatewaysCmdByAdminCmd extends ListPrivateGatewaysCmd implements AdminCmd {
33+
public static final Logger s_logger = Logger.getLogger(ListPrivateGatewaysCmdByAdminCmd.class.getName());
34+
35+
}

api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreatePrivateGatewayCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public void create() throws ResourceAllocationException {
169169
public void execute() throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException, ResourceUnavailableException {
170170
PrivateGateway result = _vpcService.applyVpcPrivateGateway(getEntityId(), true);
171171
if (result != null) {
172-
PrivateGatewayResponse response = _responseGenerator.createPrivateGatewayResponse(result);
172+
PrivateGatewayResponse response = _responseGenerator.createPrivateGatewayResponse(getResponseView(), result);
173173
response.setResponseName(getCommandName());
174174
setResponseObject(response);
175175
} else {

api/src/main/java/org/apache/cloudstack/api/command/user/vpc/ListPrivateGatewaysCmd.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.apache.cloudstack.api.ApiConstants;
2626
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
2727
import org.apache.cloudstack.api.Parameter;
28+
import org.apache.cloudstack.api.ResponseObject;
29+
import org.apache.cloudstack.api.command.user.UserCmd;
2830
import org.apache.cloudstack.api.response.ListResponse;
2931
import org.apache.cloudstack.api.response.PrivateGatewayResponse;
3032
import org.apache.cloudstack.api.response.VpcResponse;
@@ -34,8 +36,9 @@
3436
import com.cloud.utils.Pair;
3537

3638
@APICommand(name = "listPrivateGateways", description = "List private gateways", responseObject = PrivateGatewayResponse.class, entityType = {VpcGateway.class},
39+
responseView = ResponseObject.ResponseView.Restricted,
3740
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
38-
public class ListPrivateGatewaysCmd extends BaseListProjectAndAccountResourcesCmd {
41+
public class ListPrivateGatewaysCmd extends BaseListProjectAndAccountResourcesCmd implements UserCmd {
3942
public static final Logger s_logger = Logger.getLogger(ListPrivateGatewaysCmd.class.getName());
4043

4144

@@ -90,7 +93,7 @@ public void execute() {
9093
ListResponse<PrivateGatewayResponse> response = new ListResponse<PrivateGatewayResponse>();
9194
List<PrivateGatewayResponse> projectResponses = new ArrayList<PrivateGatewayResponse>();
9295
for (PrivateGateway gateway : gateways.first()) {
93-
PrivateGatewayResponse gatewayResponse = _responseGenerator.createPrivateGatewayResponse(gateway);
96+
PrivateGatewayResponse gatewayResponse = _responseGenerator.createPrivateGatewayResponse(getResponseView(), gateway);
9497
projectResponses.add(gatewayResponse);
9598
}
9699
response.setResponses(projectResponses, gateways.second());

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,10 +3357,12 @@ public VpcResponse createVpcResponse(ResponseView view, Vpc vpc) {
33573357
}
33583358

33593359
@Override
3360-
public PrivateGatewayResponse createPrivateGatewayResponse(PrivateGateway result) {
3360+
public PrivateGatewayResponse createPrivateGatewayResponse(ResponseView view, PrivateGateway result) {
33613361
PrivateGatewayResponse response = new PrivateGatewayResponse();
33623362
response.setId(result.getUuid());
3363-
response.setBroadcastUri(result.getBroadcastUri());
3363+
if (view == ResponseView.Full) {
3364+
response.setBroadcastUri(result.getBroadcastUri());
3365+
}
33643366
response.setGateway(result.getGateway());
33653367
response.setNetmask(result.getNetmask());
33663368
if (result.getVpcId() != null) {

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

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,13 @@
178178
import com.cloud.network.vpc.NetworkACL;
179179
import com.cloud.network.vpc.PrivateIpVO;
180180
import com.cloud.network.vpc.Vpc;
181+
import com.cloud.network.vpc.VpcGatewayVO;
181182
import com.cloud.network.vpc.VpcManager;
182183
import com.cloud.network.vpc.VpcVO;
183184
import com.cloud.network.vpc.dao.NetworkACLDao;
184185
import com.cloud.network.vpc.dao.PrivateIpDao;
185186
import com.cloud.network.vpc.dao.VpcDao;
187+
import com.cloud.network.vpc.dao.VpcGatewayDao;
186188
import com.cloud.network.vpc.dao.VpcOfferingDao;
187189
import com.cloud.offering.NetworkOffering;
188190
import com.cloud.offerings.NetworkOfferingVO;
@@ -388,6 +390,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
388390
@Inject
389391
Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao;
390392
@Inject
393+
VpcGatewayDao vpcGatewayDao;
394+
@Inject
391395
AlertManager alertManager;
392396
@Inject
393397
DomainRouterDao routerDao;
@@ -1939,24 +1943,17 @@ private Network implementAssociatedNetwork(Long associatedNetworkId, Account cal
19391943
if (domainId != null && associatedNetwork.getDomainId() != domainId) {
19401944
throw new InvalidParameterValueException("The new network and associated network MUST be in same domain");
19411945
}
1942-
if (cidr != null && associatedNetwork.getCidr() != null && NetUtils.isNetworksOverlap(cidr, associatedNetwork.getCidr())) {
1943-
throw new InvalidParameterValueException("The cidr overlaps with associated network: " + associatedNetwork.getName());
1944-
}
1945-
List<NetworkDetailVO> associatedNetworks = _networkDetailsDao.findDetails(Network.AssociatedNetworkId, String.valueOf(associatedNetworkId), null);
1946-
for (NetworkDetailVO networkDetailVO : associatedNetworks) {
1947-
NetworkVO associatedNetwork2 = _networksDao.findById(networkDetailVO.getResourceId());
1948-
if (associatedNetwork2 != null) {
1949-
List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(associatedNetwork2.getId());
1950-
if (vlans.isEmpty()) {
1951-
continue;
1952-
}
1953-
String startIP2 = vlans.get(0).getIpRange().split("-")[0];
1954-
String endIP2 = vlans.get(0).getIpRange().split("-")[1];
1955-
if (StringUtils.isNoneBlank(startIp, startIP2) && NetUtils.ipRangesOverlap(startIp, endIp, startIP2, endIP2)) {
1956-
throw new InvalidParameterValueException("The startIp/endIp overlaps with network: " + associatedNetwork2.getName());
1957-
}
1946+
if (cidr != null && associatedNetwork.getCidr() != null) {
1947+
String[] guestVmCidrPair = associatedNetwork.getCidr().split("\\/");
1948+
String[] cidrIpRange = NetUtils.getIpRangeFromCidr(guestVmCidrPair[0], Long.valueOf(guestVmCidrPair[1]));
1949+
if (StringUtils.isNoneBlank(startIp, endIp) && NetUtils.ipRangesOverlap(startIp, endIp, cidrIpRange[0], cidrIpRange[1])) {
1950+
throw new InvalidParameterValueException(String.format("The IP range (%s-%s) overlaps with cidr of associated network: %s (%s)",
1951+
startIp, endIp, associatedNetwork.getName(), associatedNetwork.getCidr()));
19581952
}
19591953
}
1954+
// Check IP range overlap on shared networks and vpc private gateways associated to the same network
1955+
checkIpRangeOverlapWithAssociatedNetworks(associatedNetworkId, startIp, endIp);
1956+
19601957
associatedNetwork = implementedNetworkInCreation(caller, zone, associatedNetwork);
19611958
if (associatedNetwork == null || (associatedNetwork.getState() != Network.State.Implemented && associatedNetwork.getState() != Network.State.Setup)) {
19621959
throw new InvalidParameterValueException("Unable to implement associated network " + associatedNetwork);
@@ -3065,8 +3062,9 @@ public Network updateGuestNetwork(final UpdateNetworkCmd cmd) {
30653062
if (networkOfferingChanged) {
30663063
throw new InvalidParameterValueException("Cannot specify this network offering change and guestVmCidr at same time. Specify only one.");
30673064
}
3068-
if (!(network.getState() == Network.State.Implemented)) {
3069-
throw new InvalidParameterValueException("The network must be in " + Network.State.Implemented + " state. IP Reservation cannot be applied in " + network.getState() + " state");
3065+
if (network.getState() != Network.State.Implemented && network.getState() != Network.State.Allocated) {
3066+
throw new InvalidParameterValueException(String.format("The network must be in %s or %s state. IP Reservation cannot be applied in %s state",
3067+
Network.State.Implemented, Network.State.Allocated, network.getState()));
30703068
}
30713069
if (!NetUtils.isValidIp4Cidr(guestVmCidr)) {
30723070
throw new InvalidParameterValueException("Invalid format of Guest VM CIDR.");
@@ -3125,6 +3123,9 @@ public Network updateGuestNetwork(final UpdateNetworkCmd cmd) {
31253123
}
31263124
}
31273125

3126+
// Check IP range overlap on shared networks and vpc private gateways associated to this network
3127+
checkIpRangeOverlapWithAssociatedNetworks(networkId, cidrIpRange[0], cidrIpRange[1]);
3128+
31283129
// When reservation is applied for the first time, network_cidr will be null
31293130
// Populate it with the actual network cidr
31303131
if (network.getNetworkCidr() == null) {
@@ -5911,6 +5912,30 @@ private List<Long> convertAccountNamesToAccountIds(final Account caller, final L
59115912
return accountIds;
59125913
}
59135914

5915+
private void checkIpRangeOverlapWithAssociatedNetworks(Long associatedNetworkId, String startIp, String endIp) {
5916+
List<NetworkDetailVO> associatedNetworks = _networkDetailsDao.findDetails(Network.AssociatedNetworkId, String.valueOf(associatedNetworkId), null);
5917+
for (NetworkDetailVO networkDetailVO : associatedNetworks) {
5918+
NetworkVO associatedNetwork2 = _networksDao.findById(networkDetailVO.getResourceId());
5919+
if (associatedNetwork2 != null) {
5920+
List<VlanVO> vlans = _vlanDao.listVlansByNetworkId(associatedNetwork2.getId());
5921+
if (vlans.isEmpty()) {
5922+
VpcGatewayVO vpcGateway = vpcGatewayDao.getVpcGatewayByNetworkId(associatedNetwork2.getId());
5923+
if (vpcGateway != null && NetUtils.ipRangesOverlap(startIp, endIp, vpcGateway.getIp4Address(), vpcGateway.getIp4Address())) {
5924+
throw new InvalidParameterValueException(String.format("The startIp/endIp (%s - %s) overlaps with vpc private gateway %s (%s): ",
5925+
startIp, endIp, associatedNetwork2.getName(), vpcGateway.getIp4Address()));
5926+
}
5927+
continue;
5928+
}
5929+
String startIP2 = vlans.get(0).getIpRange().split("-")[0];
5930+
String endIP2 = vlans.get(0).getIpRange().split("-")[1];
5931+
if (StringUtils.isNoneBlank(startIp, startIP2) && NetUtils.ipRangesOverlap(startIp, endIp, startIP2, endIP2)) {
5932+
throw new InvalidParameterValueException(String.format("The startIp/endIp (%s - %s) overlaps with network %s (%s - %s)",
5933+
startIp, endIp, associatedNetwork2.getName(), startIP2, endIP2));
5934+
}
5935+
}
5936+
}
5937+
}
5938+
59145939
@Override
59155940
public String getConfigComponentName() {
59165941
return NetworkService.class.getSimpleName();

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@
309309
import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd;
310310
import org.apache.cloudstack.api.command.admin.vpc.DeletePrivateGatewayCmd;
311311
import org.apache.cloudstack.api.command.admin.vpc.DeleteVPCOfferingCmd;
312+
import org.apache.cloudstack.api.command.admin.vpc.ListPrivateGatewaysCmdByAdminCmd;
312313
import org.apache.cloudstack.api.command.admin.vpc.ListVPCsCmdByAdmin;
313314
import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCCmdByAdmin;
314315
import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd;
@@ -3834,6 +3835,7 @@ public List<Class<?>> getCommands() {
38343835
cmdList.add(ListVPCsCmdByAdmin.class);
38353836
cmdList.add(UpdateVPCCmdByAdmin.class);
38363837
cmdList.add(CreatePrivateGatewayByAdminCmd.class);
3838+
cmdList.add(ListPrivateGatewaysCmdByAdminCmd.class);
38373839
cmdList.add(UpdateLBStickinessPolicyCmd.class);
38383840
cmdList.add(UpdateLBHealthCheckPolicyCmd.class);
38393841
cmdList.add(GetUploadParamsForTemplateCmd.class);

test/integration/component/test_ip_reservation.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,7 @@ def test_network_not_implemented(self):
10691069
# 1. update guestvmcidr of isolated network (non persistent)
10701070
#
10711071
# validation
1072-
# should throw exception as network is not in implemented state as no vm is created
1072+
# should succeed although network is not in implemented state
10731073

10741074
networkOffering = self.isolated_network_offering
10751075
resultSet = createIsolatedNetwork(self, networkOffering.id)
@@ -1078,9 +1078,7 @@ def test_network_not_implemented(self):
10781078
else:
10791079
isolated_network = resultSet[1]
10801080

1081-
with self.assertRaises(Exception):
1082-
isolated_network.update(self.apiclient, guestvmcidr="10.1.1.0/26")
1083-
return
1081+
isolated_network.update(self.apiclient, guestvmcidr="10.1.1.0/26")
10841082

10851083
@attr(tags=["advanced"], required_hardware="false")
10861084
def test_vm_create_after_reservation(self):

0 commit comments

Comments
 (0)