Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ jobs:
smoke/test_vm_snapshot_kvm
smoke/test_vm_snapshots
smoke/test_volumes
smoke/test_vpc_conserve_mode
smoke/test_vpc_ipv6
smoke/test_vpc_redundant
smoke/test_vpc_router_nics
Expand Down
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/network/NetworkService.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,6 @@ Network createPrivateNetwork(String networkName, String displayText, long physic
IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress);

String getNicVlanValueForExternalVm(NicTO nic);

Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId);
}
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/network/vpc/VpcOffering.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@ public enum State {
NetworkOffering.RoutingMode getRoutingMode();

Boolean isSpecifyAsNumber();

boolean isConserveMode();
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ VpcOffering createVpcOffering(String name, String displayText, List<String> supp
Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol,
Long serviceOfferingId, String externalProvider, NetworkOffering.NetworkMode networkMode,
List<Long> domainIds, List<Long> zoneIds, VpcOffering.State state,
NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber);
NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber, boolean conserveMode);


Pair<List<? extends VpcOffering>,Integer> listVpcOfferings(ListVPCOfferingsCmd cmd);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,7 @@ public class ApiConstants {
public static final String REGION_ID = "regionid";
public static final String VPC_OFF_ID = "vpcofferingid";
public static final String VPC_OFF_NAME = "vpcofferingname";
public static final String VPC_OFFERING_CONSERVE_MODE = "vpcofferingconservemode";
public static final String NETWORK = "network";
public static final String VPC_ID = "vpcid";
public static final String VPC_NAME = "vpcname";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd {
description = "the routing mode for the VPC offering. Supported types are: Static or Dynamic.")
private String routingMode;

@Parameter(name = ApiConstants.CONSERVE_MODE, type = CommandType.BOOLEAN,
since = "4.23.0",
description = "True if the VPC offering is IP conserve mode enabled, allowing public IPs to be used across multiple VPC tiers. Default value is false")
private Boolean conserveMode;


/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -311,6 +317,10 @@ public String getRoutingMode() {
return routingMode;
}

public boolean isConserveMode() {
return BooleanUtils.toBoolean(conserveMode);
}

@Override
public void create() throws ResourceAllocationException {
VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements PortForwardingRule {


// ///////////////////////////////////////////////////
// ////////////// API parameters /////////////////////
// ///////////////////////////////////////////////////
Expand Down Expand Up @@ -278,13 +277,7 @@ public State getState() {
@Override
public long getNetworkId() {
IpAddress ip = _entityMgr.findById(IpAddress.class, getIpAddressId());
Long ntwkId = null;

if (ip.getAssociatedWithNetworkId() != null) {
ntwkId = ip.getAssociatedWithNetworkId();
} else {
ntwkId = networkId;
}
Long ntwkId = _networkService.getPreferredNetworkIdForPublicIpRuleAssignment(ip, networkId);
if (ntwkId == null) {
throw new InvalidParameterValueException("Unable to create port forwarding rule for the ipAddress id=" + ipAddressId +
" as ip is not associated with any network and no networkId is passed in");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ public class VpcOfferingResponse extends BaseResponse {
@Param(description = "The routing mode for the network offering, supported types are Static or Dynamic.")
private String routingMode;

@SerializedName(ApiConstants.CONSERVE_MODE)
@Param(description = "True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers.", since = "4.23.0")
private Boolean conserveMode;

public void setId(String id) {
this.id = id;
}
Expand Down Expand Up @@ -201,4 +205,12 @@ public String getRoutingMode() {
public void setRoutingMode(String routingMode) {
this.routingMode = routingMode;
}

public Boolean getConserveMode() {
return conserveMode;
}

public void setConserveMode(Boolean conserveMode) {
this.conserveMode = conserveMode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll
@Param(description = "VPC offering name the VPC is created from", since = "4.13.2")
private String vpcOfferingName;

@SerializedName(ApiConstants.VPC_OFFERING_CONSERVE_MODE)
@Param(description = "true if VPC offering is ip conserve mode enabled", since = "4.23")
private Boolean vpcOfferingConserveMode;

@SerializedName(ApiConstants.CREATED)
@Param(description = "The date this VPC was created")
private Date created;
Expand Down Expand Up @@ -197,6 +201,10 @@ public void setDisplayText(final String displayText) {
this.displayText = displayText;
}

public void setVpcOfferingConserveMode(Boolean vpcOfferingConserveMode) {
this.vpcOfferingConserveMode = vpcOfferingConserveMode;
}

public void setCreated(final Date created) {
this.created = created;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,6 @@ List<IPAddressVO> listAvailablePublicIps(final long dcId,
PublicIpQuarantine updatePublicIpAddressInQuarantine(Long quarantineProcessId, Date endDate);

void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception;

Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
public interface LoadBalancingRulesManager {

LoadBalancer createPublicLoadBalancer(String xId, String name, String description, int srcPort, int destPort, long sourceIpId, String protocol, String algorithm,
boolean openFirewall, CallContext caller, String lbProtocol, Boolean forDisplay, String cidrList) throws NetworkRuleConflictException;
boolean openFirewall, CallContext caller, String lbProtocol, Boolean forDisplay, String cidrList, Long networkId) throws NetworkRuleConflictException;

boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public class VpcOfferingVO implements VpcOffering {
@Column(name = "specify_as_number")
private Boolean specifyAsNumber = false;

@Column(name = "conserve_mode")
private boolean conserveMode;

public VpcOfferingVO() {
this.uuid = UUID.randomUUID().toString();
}
Expand Down Expand Up @@ -242,4 +245,13 @@ public Boolean isSpecifyAsNumber() {
public void setSpecifyAsNumber(Boolean specifyAsNumber) {
this.specifyAsNumber = specifyAsNumber;
}

@Override
public boolean isConserveMode() {
return conserveMode;
}

public void setConserveMode(boolean conserveMode) {
this.conserveMode = conserveMode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` (
INDEX `i_webhook_filter__webhook_id`(`webhook_id`),
CONSTRAINT `fk_webhook_filter__webhook_id` FOREIGN KEY(`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Add conserve mode for VPC offerings
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tinyint(1) unsigned NULL DEFAULT 0 COMMENT ''True if the VPC offering is IP conserve mode enabled, allowing public IP services to be used across multiple VPC tiers'' ');
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ select
`vpc_offerings`.`sort_key` AS `sort_key`,
`vpc_offerings`.`routing_mode` AS `routing_mode`,
`vpc_offerings`.`specify_as_number` AS `specify_as_number`,
`vpc_offerings`.`conserve_mode` AS `conserve_mode`,
group_concat(distinct `domain`.`id` separator ',') AS `domain_id`,
group_concat(distinct `domain`.`uuid` separator ',') AS `domain_uuid`,
group_concat(distinct `domain`.`name` separator ',') AS `domain_name`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ private LoadBalancer handleCreateLoadBalancerRuleWithLock(final CreateLoadBalanc
lb.setSourceIpAddressId(ipId);

result = _lbMgr.createPublicLoadBalancer(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourcePortStart(), lb.getDefaultPortStart(), ipId.longValue(),
lb.getProtocol(), lb.getAlgorithm(), false, CallContext.current(), lb.getLbProtocol(), true, null);
lb.getProtocol(), lb.getAlgorithm(), false, CallContext.current(), lb.getLbProtocol(), true, null, networkId);
} catch (final NetworkRuleConflictException e) {
logger.warn("Failed to create LB rule, not continuing with ELB deployment");
if (newIp) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ private VpcOffering locateVpcOffering() {
}
serviceProviderMap.put(svc, providerSet);
}
vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, null, null, null, null, VpcOffering.State.Enabled, null, false);
vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, null, null, null, null, VpcOffering.State.Enabled, null, false, false);
long id = vpcOffer.getId();
_vpcOffDao.update(id, (VpcOfferingVO)vpcOffer);
return _vpcOffDao.findById(id);
Expand Down
1 change: 1 addition & 0 deletions server/src/main/java/com/cloud/api/ApiResponseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3499,6 +3499,7 @@ public VpcResponse createVpcResponse(ResponseView view, Vpc vpc) {
if (voff != null) {
response.setVpcOfferingId(voff.getUuid());
response.setVpcOfferingName(voff.getName());
response.setVpcOfferingConserveMode(voff.isConserveMode());
}
response.setCidr(vpc.getCidr());
response.setRestartRequired(vpc.isRestartRequired());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public VpcOfferingResponse newVpcOfferingResponse(VpcOffering offering) {
if (offering.isSpecifyAsNumber() != null) {
offeringResponse.setSpecifyAsNumber(offering.isSpecifyAsNumber());
}
offeringResponse.setConserveMode(offering.isConserveMode());
if (offering instanceof VpcOfferingJoinVO) {
VpcOfferingJoinVO offeringJoinVO = (VpcOfferingJoinVO) offering;
offeringResponse.setDomainId(offeringJoinVO.getDomainUuid());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ public class VpcOfferingJoinVO implements VpcOffering {
@Column(name = "specify_as_number")
private Boolean specifyAsNumber = false;

@Column(name = "conserve_mode")
private boolean conserveMode;

public VpcOfferingJoinVO() {
}

Expand Down Expand Up @@ -178,6 +181,11 @@ public Boolean isSpecifyAsNumber() {
return specifyAsNumber;
}

@Override
public boolean isConserveMode() {
return conserveMode;
}

public void setSpecifyAsNumber(Boolean specifyAsNumber) {
this.specifyAsNumber = specifyAsNumber;
}
Expand Down
41 changes: 40 additions & 1 deletion server/src/main/java/com/cloud/network/IpAddressManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,14 @@ public IPAddressVO doInTransaction(TransactionStatus status) throws Insufficient
return ipaddr;
}

protected IPAddressVO getExistingSourceNatInVPC(Long vpcId) {
List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpcId, true);
if (CollectionUtils.isEmpty(ips)) {
return null;
}
return ips.get(0);
}

protected IPAddressVO getExistingSourceNatInNetwork(long ownerId, Long networkId) {
List<? extends IpAddress> addrs;
Network guestNetwork = _networksDao.findById(networkId);
Expand Down Expand Up @@ -1723,7 +1731,11 @@ protected boolean isSourceNatAvailableForNetwork(Account owner, IPAddressVO ipTo
NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
boolean sharedSourceNat = offering.isSharedSourceNat();
boolean isSourceNat = false;
if (!sharedSourceNat) {
if (network.getVpcId() != null) {
// For VPCs: Check if the VPC Source NAT IP address is the same we are associating
IPAddressVO vpcSourceNatIpAddress = getExistingSourceNatInVPC(network.getVpcId());
isSourceNat = vpcSourceNatIpAddress != null && vpcSourceNatIpAddress.getId() == ipToAssoc.getId();
} else if (!sharedSourceNat) {
if (getExistingSourceNatInNetwork(owner.getId(), network.getId()) == null) {
if (network.getGuestType() == GuestType.Isolated && network.getVpcId() == null && !ipToAssoc.isPortable()) {
isSourceNat = true;
Expand Down Expand Up @@ -2647,4 +2659,31 @@ public void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO>
});
}

@Override
public Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId) {
boolean vpcConserveMode = isPublicIpOnVpcConserveMode(ip);
return getPreferredNetworkIdForRule(ip, vpcConserveMode, networkId);
}

protected Long getPreferredNetworkIdForRule(IpAddress ip, boolean vpcConserveModeEnabled, Long networkId) {
if (vpcConserveModeEnabled) {
// Since VPC Conserve mode allows rules from multiple VPC tiers, always check the networkId parameter first
return networkId != null ? networkId : ip.getAssociatedWithNetworkId();
} else {
// In case of Guest Networks or VPC Tier Networks VPC Conserve mode disabled prefer the associated networkId
return ip.getAssociatedWithNetworkId() != null ? ip.getAssociatedWithNetworkId() : networkId;
}
}

protected boolean isPublicIpOnVpcConserveMode(IpAddress ip) {
if (ip.getVpcId() == null) {
return false;
}
Vpc vpc = _vpcMgr.getActiveVpc(ip.getVpcId());
if (vpc == null) {
return false;
}
VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId());
return vpcOffering != null && vpcOffering.isConserveMode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6415,6 +6415,11 @@ public String getNicVlanValueForExternalVm(NicTO nic) {
return Networks.BroadcastDomainType.getValue(nic.getBroadcastUri());
}

@Override
public Long getPreferredNetworkIdForPublicIpRuleAssignment(IpAddress ip, Long networkId) {
return _ipAddrMgr.getPreferredNetworkIdForPublicIpRuleAssignment(ip, networkId);
}

@Override
public Network.IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress) {
if (ip6Address != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
import javax.inject.Inject;
import javax.naming.ConfigurationException;

import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcOfferingVO;
import com.cloud.network.vpc.dao.VpcOfferingDao;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -159,6 +162,8 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService,
IpAddressManager _ipAddrMgr;
@Inject
RoutedIpv4Manager routedIpv4Manager;
@Inject
VpcOfferingDao vpcOfferingDao;

private boolean _elbEnabled = false;
static Boolean rulesContinueOnErrFlag = true;
Expand Down Expand Up @@ -395,6 +400,10 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict
assert (rules.size() >= 1);
}

NetworkVO newRuleNetwork = getNewRuleNetwork(newRule);
boolean newRuleIsOnVpcNetwork = isNewRuleOnVpcNetwork(newRuleNetwork);
boolean vpcConserveModeEnabled = isVpcConserveModeEnabled(newRuleNetwork);

for (FirewallRuleVO rule : rules) {
if (rule.getId() == newRule.getId()) {
continue; // Skips my own rule.
Expand Down Expand Up @@ -443,8 +452,15 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict
}

// Checking if the rule applied is to the same network that is passed in the rule.
if (rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) {
throw new NetworkRuleConflictException("New rule is for a different network than what's specified in rule " + rule.getXid());
// (except for VPCs with conserve mode = true)
if ((!newRuleIsOnVpcNetwork || !vpcConserveModeEnabled)
&& rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) {
String errMsg = String.format("New rule is for a different network than what's specified in rule %s", rule.getXid());
if (newRuleIsOnVpcNetwork) {
Vpc vpc = _vpcMgr.getActiveVpc(newRuleNetwork.getVpcId());
errMsg += String.format(" - VPC id=%s is not using conserve mode", vpc.getUuid());
}
throw new NetworkRuleConflictException(errMsg);
}
Comment on lines 455 to 464
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new method


//Check for the ICMP protocol. This has to be done separately from other protocols as we need to check the ICMP codes and ICMP type also.
Expand Down Expand Up @@ -493,6 +509,27 @@ public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflict
}
}

protected boolean isVpcConserveModeEnabled(NetworkVO newRuleNetwork) {
if (isNewRuleOnVpcNetwork(newRuleNetwork)) {
Vpc vpc = _vpcMgr.getActiveVpc(newRuleNetwork.getVpcId());
VpcOfferingVO vpcOffering = vpc != null ? vpcOfferingDao.findById(vpc.getVpcOfferingId()) : null;
return vpcOffering != null && vpcOffering.isConserveMode();
}
return false;
}

protected boolean isNewRuleOnVpcNetwork(NetworkVO newRuleNetwork) {
return newRuleNetwork.getVpcId() != null;
}

protected NetworkVO getNewRuleNetwork(FirewallRule newRule) {
NetworkVO newRuleNetwork = _networkDao.findById(newRule.getNetworkId());
if (newRuleNetwork == null) {
throw new InvalidParameterValueException("Unable to create firewall rule as cannot find network by id=" + newRule.getNetworkId());
}
return newRuleNetwork;
}

protected boolean checkIfRulesHaveConflictingPortRanges(FirewallRule newRule, FirewallRule rule, boolean oneOfRulesIsFirewall, boolean bothRulesFirewall, boolean bothRulesPortForwarding, boolean duplicatedCidrs) {
String rulesAsString = String.format("[%s] and [%s]", rule, newRule);

Expand Down
Loading
Loading