Possibility to choose the source NAT IP address on a isolated network or VPC (#6442)

Co-authored-by: NuxRo <nux@li.nux.ro>
Co-authored-by: Daniel Augusto Veronezi Salvador <38945620+GutoVeronezi@users.noreply.github.com>
This commit is contained in:
dahn 2023-06-09 14:51:53 +02:00 committed by GitHub
parent 41e8ad7487
commit ae10263b3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1398 additions and 347 deletions

View File

@ -80,7 +80,8 @@ jobs:
smoke/test_metrics_api
smoke/test_migration
smoke/test_multipleips_per_nic
smoke/test_nested_virtualization",
smoke/test_nested_virtualization
smoke/test_set_sourcenat",
"smoke/test_network
smoke/test_network_acl
smoke/test_network_ipv6

View File

@ -256,7 +256,7 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
public static class Capability {
private static List<Capability> supportedCapabilities = new ArrayList<Capability>();
private static List<Capability> supportedCapabilities = new ArrayList<>();
public static final Capability SupportedProtocols = new Capability("SupportedProtocols");
public static final Capability SupportedLBAlgorithms = new Capability("SupportedLbAlgorithms");

View File

@ -23,7 +23,9 @@ import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd;
import org.apache.cloudstack.api.command.user.vpc.CreateVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd;
import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd;
import org.apache.cloudstack.api.command.user.vpc.ListVPCsCmd;
import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientAddressCapacityException;
@ -37,7 +39,6 @@ import com.cloud.utils.Pair;
public interface VpcService {
public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException;
/**
* Persists VPC record in the database
*
@ -48,14 +49,25 @@ public interface VpcService {
* @param displayText
* @param cidr
* @param networkDomain TODO
* @param ip4Dns1
* @param ip4Dns2
* @param displayVpc TODO
* @return
* @throws ResourceAllocationException TODO
*/
public Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain,
String dns1, String dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu)
Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain,
String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu)
throws ResourceAllocationException;
/**
* Persists VPC record in the database
*
* @param cmd the command with specification data for the new vpc
* @return a data object describing the new vpc
* @throws ResourceAllocationException the resources for this VPC cannot be allocated
*/
Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException;
/**
* Deletes a VPC
*
@ -65,46 +77,46 @@ public interface VpcService {
* @throws ResourceUnavailableException
* @throws ConcurrentOperationException
*/
public boolean deleteVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException;
boolean deleteVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException;
/**
* Persists VPC record in the database
*
* @param cmd the command with specification data for updating the vpc
* @return a data object describing the new vpc state
* @throws ResourceUnavailableException if during restart some resources may not be available
* @throws InsufficientCapacityException if for instance no address space, compute or storage is sufficiently available
*/
Vpc updateVpc(UpdateVPCCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException;
/**
* Updates VPC with new name/displayText
*
* @param vpcId
* @param vpcName
* @param displayText
* @param customId TODO
* @param displayVpc TODO
* @param mtu
* @return
* @param vpcId the ID of the Vpc to update
* @param vpcName The new name to give the vpc
* @param displayText the new display text to use for describing the VPC
* @param customId A new custom (external) ID to associate this VPC with
* @param displayVpc should this VPC be displayed on public lists
* @param mtu what maximal transfer unit to us in this VPCs networks
* @param sourceNatIp the source NAT address to use for this VPC (must already be associated with the VPC)
* @return an object describing the current state of the VPC
* @throws ResourceUnavailableException if during restart some resources may not be available
* @throws InsufficientCapacityException if for instance no address space, compute or storage is sufficiently available
*/
public Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu);
Vpc updateVpc(long vpcId, String vpcName, String displayText, String customId, Boolean displayVpc, Integer mtu, String sourceNatIp) throws ResourceUnavailableException, InsufficientCapacityException;
/**
* Lists VPC(s) based on the parameters passed to the API call
*
* @param cmd object containing the search specs
* @return the List of VPCs
*/
Pair<List<? extends Vpc>, Integer> listVpcs(ListVPCsCmd cmd);
/**
* Lists VPC(s) based on the parameters passed to the method call
*
* @param id
* @param vpcName
* @param displayText
* @param supportedServicesStr
* @param cidr
* @param state TODO
* @param accountName
* @param domainId
* @param keyword
* @param startIndex
* @param pageSizeVal
* @param zoneId TODO
* @param isRecursive TODO
* @param listAll TODO
* @param restartRequired TODO
* @param tags TODO
* @param projectId TODO
* @param display TODO
* @param vpc
* @return
*/
public Pair<List<? extends Vpc>, Integer> listVpcs(Long id, String vpcName, String displayText, List<String> supportedServicesStr, String cidr, Long vpcOffId, String state,
Pair<List<? extends Vpc>, Integer> listVpcs(Long id, String vpcName, String displayText, List<String> supportedServicesStr, String cidr, Long vpcOffId, String state,
String accountName, Long domainId, String keyword, Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, Boolean restartRequired,
Map<String, String> tags, Long projectId, Boolean display);
@ -130,17 +142,17 @@ public interface VpcService {
*/
boolean shutdownVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException;
boolean restartVpc(RestartVPCCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
/**
* Restarts the VPC. VPC gets shutdown and started as a part of it
*
* @param id
* @param cleanUp
* @param makeredundant
* @return
* @throws InsufficientCapacityException
* @param networkId the network to restart
* @param cleanup throw away the existing VR and rebuild a new one?
* @param makeRedundant create two VRs for this network
* @return success or not
* @throws InsufficientCapacityException when there is no suitable deployment plan possible
*/
boolean restartVpc(RestartVPCCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
boolean restartVpc(Long networkId, boolean cleanup, boolean makeRedundant, boolean livePatch, User user) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;
/**
@ -154,23 +166,12 @@ public interface VpcService {
/**
* Persists VPC private gateway in the Database.
*
*
* @param vpcId TODO
* @param physicalNetworkId
* @param vlan
* @param ipAddress
* @param gateway
* @param netmask
* @param gatewayOwnerId
* @param networkOfferingId
* @param isSourceNat
* @param aclId
* @return
* @return data object describing the private gateway
* @throws InsufficientCapacityException
* @throws ConcurrentOperationException
* @throws ResourceAllocationException
*/
public PrivateGateway createVpcPrivateGateway(CreatePrivateGatewayCmd command) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException;
PrivateGateway createVpcPrivateGateway(CreatePrivateGatewayCmd command) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException;
/**
* Applies VPC private gateway on the backend, so it becomes functional
@ -181,12 +182,12 @@ public interface VpcService {
* @throws ResourceUnavailableException
* @throws ConcurrentOperationException
*/
public PrivateGateway applyVpcPrivateGateway(long gatewayId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException;
PrivateGateway applyVpcPrivateGateway(long gatewayId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException;
/**
* Deletes VPC private gateway
*
* @param id
* @param gatewayId
* @return
* @throws ResourceUnavailableException
* @throws ConcurrentOperationException
@ -199,7 +200,7 @@ public interface VpcService {
* @param listPrivateGatewaysCmd
* @return
*/
public Pair<List<PrivateGateway>, Integer> listPrivateGateway(ListPrivateGatewaysCmd listPrivateGatewaysCmd);
Pair<List<PrivateGateway>, Integer> listPrivateGateway(ListPrivateGatewaysCmd listPrivateGatewaysCmd);
/**
* Returns Static Route found by Id
@ -216,7 +217,7 @@ public interface VpcService {
* @return
* @throws ResourceUnavailableException
*/
public boolean applyStaticRoutesForVpc(long vpcId) throws ResourceUnavailableException;
boolean applyStaticRoutesForVpc(long vpcId) throws ResourceUnavailableException;
/**
* Deletes static route from the backend and the database
@ -225,7 +226,7 @@ public interface VpcService {
* @return TODO
* @throws ResourceUnavailableException
*/
public boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException;
boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException;
/**
* Persists static route entry in the Database
@ -234,15 +235,15 @@ public interface VpcService {
* @param cidr
* @return
*/
public StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException;
StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException;
/**
* Lists static routes based on parameters passed to the call
*
* @param listStaticRoutesCmd
* @param cmd Command object with parameters for { @see ListStaticRoutesCmd }
* @return
*/
public Pair<List<? extends StaticRoute>, Integer> listStaticRoutes(ListStaticRoutesCmd cmd);
Pair<List<? extends StaticRoute>, Integer> listStaticRoutes(ListStaticRoutesCmd cmd);
/**
* Associates IP address from the Public network, to the VPC
@ -262,6 +263,5 @@ public interface VpcService {
* @param routeId
* @return
*/
public boolean applyStaticRoute(long routeId) throws ResourceUnavailableException;
boolean applyStaticRoute(long routeId) throws ResourceUnavailableException;
}

View File

@ -1032,6 +1032,10 @@ public class ApiConstants {
public static final String AUTO_ENABLE_KVM_HOST = "autoenablekvmhost";
public static final String LIST_APIS = "listApis";
public static final String SOURCE_NAT_IP = "sourcenatipaddress";
public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid";
public static final String HAS_RULES = "hasrules";
/**
* This enum specifies IO Drivers, each option controls specific policies on I/O.
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).

View File

@ -192,8 +192,8 @@ public class ListPublicIpAddressesCmd extends BaseListTaggedResourcesCmd impleme
@Override
public void execute() {
Pair<List<? extends IpAddress>, Integer> result = _mgr.searchForIPAddresses(this);
ListResponse<IPAddressResponse> response = new ListResponse<IPAddressResponse>();
List<IPAddressResponse> ipAddrResponses = new ArrayList<IPAddressResponse>();
ListResponse<IPAddressResponse> response = new ListResponse<>();
List<IPAddressResponse> ipAddrResponses = new ArrayList<>();
for (IpAddress ipAddress : result.first()) {
IPAddressResponse ipResponse = _responseGenerator.createIPAddressResponse(getResponseView(), ipAddress);
ipResponse.setObjectName("publicipaddress");

View File

@ -16,7 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.network;
import com.cloud.network.NetworkService;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.RoleType;
@ -43,10 +43,10 @@ import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.network.Network;
import com.cloud.network.NetworkService;
import com.cloud.network.Network.GuestType;
import com.cloud.offering.NetworkOffering;
import com.cloud.utils.net.NetUtils;
import org.apache.commons.lang3.StringUtils;
@APICommand(name = "createNetwork", description = "Creates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@ -183,6 +183,14 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd {
@Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network", since = "4.18.0")
private String ip6Dns2;
@Parameter(name = ApiConstants.SOURCE_NAT_IP,
type = CommandType.STRING,
description = "IPV4 address to be assigned to the public interface of the network router. " +
"This address will be used as source NAT address for the network. " +
"\nIf an address is given and it cannot be acquired, an error will be returned and the network won´t be implemented,",
since = "4.19")
private String sourceNatIP;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -266,6 +274,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd {
return tungstenVirtualRouterUuid;
}
public String getSourceNatIP() {
return sourceNatIP;
}
@Override
public boolean isDisplay() {
if(displayNetwork == null)

View File

@ -104,6 +104,9 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd {
@Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network. Empty string will update the second IPv6 DNS with the value from the zone", since = "4.18.0")
private String ip6Dns2;
@Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to the public interface of the network router. This address must already be acquired for this network", since = "4.19")
private String sourceNatIP;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -181,6 +184,10 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd {
return ip6Dns2;
}
public String getSourceNatIP() {
return sourceNatIP;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -16,7 +16,6 @@
// under the License.
package org.apache.cloudstack.api.command.user.vpc;
import com.cloud.network.NetworkService;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
@ -41,6 +40,7 @@ import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.NetworkService;
import com.cloud.network.vpc.Vpc;
@APICommand(name = "createVPC", description = "Creates a VPC", responseObject = VpcResponse.class, responseView = ResponseView.Restricted, entityType = {Vpc.class},
@ -113,6 +113,12 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd {
@Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the VPC", since = "4.18.0")
private String ip6Dns2;
@Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to the public interface of the network router." +
"This address will be used as source NAT address for the networks in ths VPC. " +
"\nIf an address is given and it cannot be acquired, an error will be returned and the network won´t be implemented,",
since = "4.19")
private String sourceNatIP;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@ -180,6 +186,15 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd {
return display;
}
public String getSourceNatIP() {
return sourceNatIP;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public void create() throws ResourceAllocationException {
Vpc vpc = _vpcService.createVpc(this);

View File

@ -142,9 +142,7 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd implements UserCmd {
@Override
public void execute() {
Pair<List<? extends Vpc>, Integer> vpcs =
_vpcService.listVpcs(getId(), getVpcName(), getDisplayText(), getSupportedServices(), getCidr(), getVpcOffId(), getState(), getAccountName(), getDomainId(),
getKeyword(), getStartIndex(), getPageSizeVal(), getZoneId(), isRecursive(), listAll(), getRestartRequired(), getTags(),
getProjectId(), getDisplay());
_vpcService.listVpcs(this);
ListResponse<VpcResponse> response = new ListResponse<VpcResponse>();
List<VpcResponse> vpcResponses = new ArrayList<VpcResponse>();
for (Vpc vpc : vpcs.first()) {

View File

@ -34,6 +34,8 @@ import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.VpcResponse;
import com.cloud.event.EventTypes;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.vpc.Vpc;
import com.cloud.user.Account;
@ -63,6 +65,12 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
description = "MTU to be configured on the network VR's public facing interfaces", since = "4.18.0")
private Integer publicMtu;
@Parameter(name = ApiConstants.SOURCE_NAT_IP,
type = CommandType.STRING,
description = "IPV4 address to be assigned to the public interface of the network router. This address must already be acquired for this VPC",
since = "4.19")
private String sourceNatIP;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -87,6 +95,10 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
return publicMtu;
}
public String getSourceNatIP() {
return sourceNatIP;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -107,7 +119,8 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
@Override
public void execute() {
Vpc result = _vpcService.updateVpc(getId(), getVpcName(), getDisplayText(), getCustomId(), isDisplayVpc(), getPublicMtu());
try {
Vpc result = _vpcService.updateVpc(this);
if (result != null) {
VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result);
response.setResponseName(getCommandName());
@ -115,6 +128,14 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VPC");
}
} catch (final ResourceUnavailableException ex) {
s_logger.warn("Exception: ", ex);
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
} catch (final InsufficientCapacityException ex) {
s_logger.info(ex);
s_logger.trace(ex);
throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage());
}
}
@Override

View File

@ -163,10 +163,9 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
@Param(description="the name of the Network where ip belongs to")
private String networkName;
/*
@SerializedName(ApiConstants.JOB_ID) @Param(description="shows the current pending asynchronous job ID. This tag is not returned if no current pending jobs are acting on the volume")
private IdentityProxy jobId = new IdentityProxy("async_job");
*/
@SerializedName(ApiConstants.HAS_RULES)
@Param(description="whether the ip address has Firewall/PortForwarding/LoadBalancing rules defined")
private boolean hasRules;
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
@ -313,4 +312,8 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
public void setNetworkName(String networkName) {
this.networkName = networkName;
}
public void setHasRules(final boolean hasRules) {
this.hasRules = hasRules;
}
}

View File

@ -17,6 +17,8 @@
package org.apache.cloudstack.api.command.user.vpc;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcService;
import junit.framework.TestCase;
@ -72,7 +74,7 @@ public class UpdateVPCCmdTest extends TestCase {
Assert.assertEquals(cmd.getPublicMtu(), publicMtu);
}
public void testExecute() {
public void testExecute() throws ResourceUnavailableException, InsufficientCapacityException {
ReflectionTestUtils.setField(cmd, "id", 1L);
ReflectionTestUtils.setField(cmd, "vpcName", "updatedVpcName");
ReflectionTestUtils.setField(cmd, "displayText", "Updated VPC Name");
@ -85,10 +87,10 @@ public class UpdateVPCCmdTest extends TestCase {
responseGenerator = Mockito.mock(ResponseGenerator.class);
cmd._responseGenerator = responseGenerator;
Mockito.when(_vpcService.updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(),
Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt())).thenReturn(vpc);
Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(), Mockito.anyString())).thenReturn(vpc);
Mockito.when(responseGenerator.createVpcResponse(ResponseObject.ResponseView.Full, vpc)).thenReturn(response);
Mockito.verify(_vpcService, Mockito.times(0)).updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(),
Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt());
Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(), Mockito.anyString());
}
}

View File

@ -233,4 +233,6 @@ public interface IpAddressManager {
public static final String MESSAGE_ASSIGN_IPADDR_EVENT = "Message.AssignIpAddr.Event";
public static final String MESSAGE_RELEASE_IPADDR_EVENT = "Message.ReleaseIpAddr.Event";
void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception;
}

View File

@ -53,6 +53,12 @@ public interface FirewallRulesDao extends GenericDao<FirewallRuleVO, Long> {
List<FirewallRuleVO> listByIpAndNotRevoked(long ipAddressId);
/**
* counts the number of portforwarding rules for an IP address
*
* @param sourceIpId the id of the IP record
* @return the number of portforwarding rules for this IP
*/
long countRulesByIpId(long sourceIpId);
long countRulesByIpIdAndState(long sourceIpId, FirewallRule.State state);

View File

@ -75,7 +75,7 @@ public interface IPAddressDao extends GenericDao<IPAddressVO, Long> {
long countFreeIPsInNetwork(long networkId);
IPAddressVO findByVmIp(String vmIp);
IPAddressVO findByIp(String ipAddress);
IPAddressVO findByAssociatedVmIdAndVmIp(long vmId, String vmIp);

View File

@ -314,9 +314,9 @@ public class IPAddressDaoImpl extends GenericDaoBase<IPAddressVO, Long> implemen
}
@Override
public IPAddressVO findByVmIp(String vmIp) {
public IPAddressVO findByIp(String ipAddress) {
SearchCriteria<IPAddressVO> sc = AllFieldsSearch.create();
sc.setParameters("associatedVmIp", vmIp);
sc.setParameters("ipAddress", ipAddress);
return findOneBy(sc);
}

View File

@ -292,6 +292,7 @@ import com.cloud.network.as.AutoScaleVmProfileVO;
import com.cloud.network.as.Condition;
import com.cloud.network.as.ConditionVO;
import com.cloud.network.as.Counter;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.LoadBalancerVO;
@ -456,6 +457,8 @@ public class ApiResponseHelper implements ResponseGenerator {
UserVmJoinDao userVmJoinDao;
@Inject
NetworkServiceMapDao ntwkSrvcDao;
@Inject
FirewallRulesDao firewallRulesDao;
@Override
public UserResponse createUserResponse(User user) {
@ -1084,6 +1087,7 @@ public class ApiResponseHelper implements ResponseGenerator {
ipResponse.setHasAnnotation(annotationDao.hasAnnotations(ipAddr.getUuid(), AnnotationService.EntityType.PUBLIC_IP_ADDRESS.name(),
_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));
ipResponse.setHasRules(firewallRulesDao.countRulesByIpId(ipAddr.getId()) > 0);
ipResponse.setObjectName("ipaddress");
return ipResponse;
}

View File

@ -295,6 +295,8 @@ import com.googlecode.ipv6.IPv6Network;
public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class);
public static final String PERACCOUNT = "peraccount";
public static final String PERZONE = "perzone";
@Inject
EntityManager _entityMgr;
@ -6235,34 +6237,32 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
void validateSourceNatServiceCapablities(final Map<Capability, String> sourceNatServiceCapabilityMap) {
if (sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) {
if (sourceNatServiceCapabilityMap.keySet().size() > 2) {
throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter
+ " capabilities can be sepcified for source nat service");
if (MapUtils.isNotEmpty(sourceNatServiceCapabilityMap) && (sourceNatServiceCapabilityMap.size() > 2 || ! sourceNatCapabilitiesContainValidValues(sourceNatServiceCapabilityMap))) {
throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName()
+ ", " + Capability.RedundantRouter
+ " capabilities can be specified for source nat service");
}
}
for (final Map.Entry<Capability ,String> srcNatPair : sourceNatServiceCapabilityMap.entrySet()) {
boolean sourceNatCapabilitiesContainValidValues(Map<Capability, String> sourceNatServiceCapabilityMap) {
for (final Entry<Capability ,String> srcNatPair : sourceNatServiceCapabilityMap.entrySet()) {
final Capability capability = srcNatPair.getKey();
final String value = srcNatPair.getValue();
if (capability == Capability.SupportedSourceNatTypes) {
final boolean perAccount = value.contains("peraccount");
final boolean perZone = value.contains("perzone");
if (perAccount && perZone || !perAccount && !perZone) {
if (Capability.SupportedSourceNatTypes.equals(capability)) {
List<String> snatTypes = Arrays.asList(PERACCOUNT, PERZONE);
if (! snatTypes.contains(value) || ( value.contains(PERACCOUNT) && value.contains(PERZONE))) {
throw new InvalidParameterValueException("Either peraccount or perzone source NAT type can be specified for "
+ Capability.SupportedSourceNatTypes.getName());
}
} else if (capability == Capability.RedundantRouter) {
final boolean enabled = value.contains("true");
final boolean disabled = value.contains("false");
if (!enabled && !disabled) {
throw new InvalidParameterValueException("Unknown specified value for " + Capability.RedundantRouter.getName());
} else if (Capability.RedundantRouter.equals(capability)) {
if (! Arrays.asList("true", "false").contains(value.toLowerCase())) {
throw new InvalidParameterValueException("Unknown specified value for " + capability.getName());
}
} else {
throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter
+ " capabilities can be sepcified for source nat service");
}
return false;
}
}
return true;
}
void validateStaticNatServiceCapablities(final Map<Capability, String> staticNatServiceCapabilityMap) {
@ -6439,17 +6439,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final Map<Capability, String> sourceNatServiceCapabilityMap = serviceCapabilityMap.get(Service.SourceNat);
if (sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) {
final String sourceNatType = sourceNatServiceCapabilityMap.get(Capability.SupportedSourceNatTypes);
if (sourceNatType != null) {
_networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, sourceNatType);
sharedSourceNat = sourceNatType.contains("perzone");
}
final String param = sourceNatServiceCapabilityMap.get(Capability.RedundantRouter);
if (param != null) {
_networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.RedundantRouter, param);
redundantRouter = param.contains("true");
}
sharedSourceNat = isSharedSourceNat(serviceProviderMap, sourceNatServiceCapabilityMap);
redundantRouter = isRedundantRouter(serviceProviderMap, sourceNatServiceCapabilityMap);
}
final Map<Capability, String> staticNatServiceCapabilityMap = serviceCapabilityMap.get(Service.StaticNat);
@ -6599,6 +6590,26 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
});
}
boolean isRedundantRouter(Map<Service, Set<Provider>> serviceProviderMap, Map<Capability, String> sourceNatServiceCapabilityMap) {
boolean redundantRouter = false;
String param = sourceNatServiceCapabilityMap.get(Capability.RedundantRouter);
if (param != null) {
_networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.RedundantRouter, param);
redundantRouter = param.contains("true");
}
return redundantRouter;
}
boolean isSharedSourceNat(Map<Service, Set<Provider>> serviceProviderMap, Map<Capability, String> sourceNatServiceCapabilityMap) {
boolean sharedSourceNat = false;
String param = sourceNatServiceCapabilityMap.get(Capability.SupportedSourceNatTypes);
if (param != null) {
_networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, param);
sharedSourceNat = param.contains(PERZONE);
}
return sharedSourceNat;
}
protected void validateNtwkOffDetails(final Map<Detail, String> details, final Map<Service, Set<Provider>> serviceProviderMap) {
for (final Detail detail : details.keySet()) {

View File

@ -2358,4 +2358,19 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
public static ConfigKey<Boolean> getSystemvmpublicipreservationmodestrictness() {
return SystemVmPublicIpReservationModeStrictness;
}
@Override
public void updateSourceNatIpAddress(IPAddressVO requestedIp, List<IPAddressVO> userIps) throws Exception{
Transaction.execute((TransactionCallbackWithException<IpAddress, Exception>) status -> {
// update all other IPs to not be sourcenat, should be at most one
for(IPAddressVO oldIpAddress :userIps) {
oldIpAddress.setSourceNat(false);
_ipAddressDao.update(oldIpAddress.getId(), oldIpAddress);
}
requestedIp.setSourceNat(true);
_ipAddressDao.update(requestedIp.getId(),requestedIp);
return requestedIp;
});
}
}

View File

@ -297,6 +297,7 @@ public class NetworkMigrationManagerImpl implements NetworkMigrationManager {
Vpc copyOfVpc;
long copyOfVpcId;
try {
copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(),
vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(),
vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu());

View File

@ -80,6 +80,8 @@ import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -267,9 +269,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
private static final long MIN_GRE_KEY = 0L;
private static final long MAX_GRE_KEY = 4294967295L; // 2^32 -1
private static final long MIN_VXLAN_VNI = 0L;
private static final long MAX_VXLAN_VNI = 16777214L; // 2^24 -2
/**
// MAX_VXLAN_VNI should be 16777215L (2^24-1), but Linux vxlan interface doesn't accept VNI:2^24-1 now.
// It seems a bug.
// Is this still valid (per 2023?)
*/
private static final long MAX_VXLAN_VNI = 16777214L; // 2^24 -2
private static final String NETWORK_OFFERING_ID = "networkOfferingId";
@Inject
DataCenterDao _dcDao = null;
@ -1298,15 +1304,20 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
private void validateRouterIps(String routerIp, String routerIpv6, String startIp, String endIp, String gateway,
String netmask, String startIpv6, String endIpv6, String ip6Cidr) {
void validateSharedNetworkRouterIPs(String gateway, String startIP, String endIP, String netmask, String routerIPv4, String routerIPv6, String startIPv6, String endIPv6, String ip6Cidr, NetworkOffering ntwkOff) {
if (ntwkOff.getGuestType() == GuestType.Shared) {
validateSharedNetworkRouterIPv4(routerIPv4, startIP, endIP, gateway, netmask);
validateSharedNetworkRouterIPv6(routerIPv6, startIPv6, endIPv6, ip6Cidr);
}
}
private void validateSharedNetworkRouterIPv4(String routerIp, String startIp, String endIp, String gateway, String netmask) {
if (StringUtils.isNotBlank(routerIp)) {
if (startIp != null && endIp == null) {
endIp = startIp;
}
if (!NetUtils.isValidIp4(routerIp)) {
throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
}
isIPv4AddressValid(routerIp);
if (StringUtils.isNoneBlank(startIp, endIp)) {
if (!NetUtils.isIpInRange(routerIp, startIp, endIp)) {
throw new CloudRuntimeException("Router IPv4 IP provided is not within the specified range: " + startIp + " - " + endIp);
@ -1318,26 +1329,39 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
}
if (StringUtils.isNotBlank(routerIpv6)) {
if (startIpv6 != null && endIpv6 == null) {
endIpv6 = startIpv6;
}
if (!NetUtils.isValidIp6(routerIpv6)) {
throw new CloudRuntimeException("Router IPv6 address provided is of incorrect format");
private void validateSharedNetworkRouterIPv6(String routerIPv6, String startIPv6, String endIPv6, String cidrIPv6) {
if (StringUtils.isNotBlank(routerIPv6)) {
if (startIPv6 != null && endIPv6 == null) {
endIPv6 = startIPv6;
}
if (StringUtils.isNoneBlank(startIpv6, endIpv6)) {
String ipv6Range = startIpv6 + "-" + endIpv6;
if (!NetUtils.isIp6InRange(routerIpv6, ipv6Range)) {
throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIpv6 + " - " + endIpv6);
isIPv6AddressValid(routerIPv6);
if (StringUtils.isNoneBlank(startIPv6, endIPv6)) {
String ipv6Range = startIPv6 + "-" + endIPv6;
if (!NetUtils.isIp6InRange(routerIPv6, ipv6Range)) {
throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIPv6 + " - " + endIPv6);
}
} else {
if (!NetUtils.isIp6InNetwork(routerIpv6, ip6Cidr)) {
if (!NetUtils.isIp6InNetwork(routerIPv6, cidrIPv6)) {
throw new CloudRuntimeException("Router IPv6 address provided is not with the network range");
}
}
}
}
private void isIPv4AddressValid(String routerIp) {
if (!NetUtils.isValidIp4(routerIp)) {
throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
}
}
private void isIPv6AddressValid(String routerIPv6) {
if (!NetUtils.isValidIp6(routerIPv6)) {
throw new CloudRuntimeException("Router IPv6 address provided is of incorrect format");
}
}
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network")
@ -1348,34 +1372,26 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
String endIP = cmd.getEndIp();
String netmask = cmd.getNetmask();
String networkDomain = cmd.getNetworkDomain();
String vlanId = null;
boolean bypassVlanOverlapCheck = false;
boolean hideIpAddressUsage = false;
String routerIp = null;
String routerIpv6 = null;
if (cmd instanceof CreateNetworkCmdByAdmin) {
vlanId = ((CreateNetworkCmdByAdmin)cmd).getVlan();
bypassVlanOverlapCheck = ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
hideIpAddressUsage = ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
routerIp = ((CreateNetworkCmdByAdmin)cmd).getRouterIp();
routerIpv6 = ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6();
}
boolean adminCalledUs = cmd instanceof CreateNetworkCmdByAdmin;
String vlanId = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getVlan() : null;
boolean bypassVlanOverlapCheck = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
boolean hideIpAddressUsage = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
String routerIPv4 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIp() : null;
String routerIPv6 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6() : null;
String name = cmd.getNetworkName();
String displayText = cmd.getDisplayText();
Account caller = CallContext.current().getCallingAccount();
Long physicalNetworkId = cmd.getPhysicalNetworkId();
Long zoneId = cmd.getZoneId();
String aclTypeStr = cmd.getAclType();
Long domainId = cmd.getDomainId();
boolean isDomainSpecific = false;
Boolean subdomainAccess = cmd.getSubdomainAccess();
Long vpcId = cmd.getVpcId();
String startIPv6 = cmd.getStartIpv6();
String endIPv6 = cmd.getEndIpv6();
String ip6Gateway = cmd.getIp6Gateway();
String ip6Cidr = cmd.getIp6Cidr();
Boolean displayNetwork = cmd.getDisplayNetwork();
boolean displayNetwork = ! Boolean.FALSE.equals(cmd.getDisplayNetwork());
Long aclId = cmd.getAclId();
String isolatedPvlan = cmd.getIsolatedPvlan();
String externalId = cmd.getExternalId();
@ -1388,127 +1404,31 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
String ip6Dns1 = cmd.getIp6Dns1();
String ip6Dns2 = cmd.getIp6Dns2();
// Validate network offering
NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
if (ntwkOff == null || ntwkOff.isSystemOnly()) {
InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering by specified id");
if (ntwkOff != null) {
ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId");
}
throw ex;
}
// Validate network offering id
NetworkOffering ntwkOff = getAndValidateNetworkOffering(networkOfferingId);
Account owner = null;
if ((cmd.getAccountName() != null && domainId != null) || cmd.getProjectId() != null) {
owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), domainId, cmd.getProjectId());
} else {
s_logger.info(String.format("Assigning the network to caller:%s because either projectId or accountname and domainId are not provided", caller.getAccountName()));
owner = caller;
}
Account owner = getOwningAccount(cmd, caller);
// validate physical network and zone
// Check if physical network exists
PhysicalNetwork pNtwk = null;
if (physicalNetworkId != null) {
pNtwk = _physicalNetworkDao.findById(physicalNetworkId);
if (pNtwk == null) {
throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id");
}
}
PhysicalNetwork pNtwk = getAndValidatePhysicalNetwork(physicalNetworkId);
if (zoneId == null) {
zoneId = pNtwk.getDataCenterId();
}
if (displayNetwork == null) {
displayNetwork = true;
}
DataCenter zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Specified zone id was not found");
}
DataCenter zone = getAndValidateZone(cmd, pNtwk);
_accountMgr.checkAccess(owner, ntwkOff, zone);
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
// See DataCenterVO.java
PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled");
ex.addProxyObject(zone.getUuid(), "zoneId");
throw ex;
}
validateZoneAvailability(caller, zone);
// Only domain and account ACL types are supported in Acton.
ACLType aclType = null;
if (aclTypeStr != null) {
if (aclTypeStr.equalsIgnoreCase(ACLType.Account.toString())) {
aclType = ACLType.Account;
} else if (aclTypeStr.equalsIgnoreCase(ACLType.Domain.toString())) {
aclType = ACLType.Domain;
} else {
throw new InvalidParameterValueException("Incorrect aclType specified. Check the API documentation for supported types");
}
// In 3.0 all Shared networks should have aclType == Domain, all Isolated networks aclType==Account
if (ntwkOff.getGuestType() == GuestType.Isolated) {
if (aclType != ACLType.Account) {
throw new InvalidParameterValueException("AclType should be " + ACLType.Account + " for network of type " + Network.GuestType.Isolated);
}
} else if (ntwkOff.getGuestType() == GuestType.Shared) {
if (!(aclType == ACLType.Domain || aclType == ACLType.Account)) {
throw new InvalidParameterValueException("AclType should be " + ACLType.Domain + " or " + ACLType.Account + " for network of type " + Network.GuestType.Shared);
}
}
} else {
if (ntwkOff.getGuestType() == GuestType.Isolated || ntwkOff.getGuestType() == GuestType.L2) {
aclType = ACLType.Account;
} else if (ntwkOff.getGuestType() == GuestType.Shared) {
if (_accountMgr.isRootAdmin(caller.getId())) {
aclType = ACLType.Domain;
} else if (_accountMgr.isNormalUser(caller.getId())) {
aclType = ACLType.Account;
} else {
throw new InvalidParameterValueException("AclType must be specified for shared network created by domain admin");
}
}
}
ACLType aclType = getAclType(caller, cmd.getAclType(), ntwkOff);
if (ntwkOff.getGuestType() != GuestType.Shared && (!StringUtils.isAllBlank(routerIp, routerIpv6))) {
if (ntwkOff.getGuestType() != GuestType.Shared && (!StringUtils.isAllBlank(routerIPv4, routerIPv6))) {
throw new InvalidParameterValueException("Router IP can be specified only for Shared networks");
}
if (ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.isProviderForNetworkOffering(Provider.VirtualRouter, networkOfferingId)
&& (!StringUtils.isAllBlank(routerIp, routerIpv6))) {
&& (!StringUtils.isAllBlank(routerIPv4, routerIPv6))) {
throw new InvalidParameterValueException("Virtual Router is not a supported provider for the Shared network, hence router ip should not be provided");
}
// Check if the network is domain specific
if (aclType == ACLType.Domain) {
// only Admin can create domain with aclType=Domain
if (!_accountMgr.isAdmin(caller.getId())) {
throw new PermissionDeniedException("Only admin can create networks with aclType=Domain");
}
// only shared networks can be Domain specific
if (ntwkOff.getGuestType() != GuestType.Shared) {
throw new InvalidParameterValueException("Only " + GuestType.Shared + " networks can have aclType=" + ACLType.Domain);
}
if (domainId != null) {
if (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != Network.GuestType.Shared) {
throw new InvalidParameterValueException("Domain level networks are supported just for traffic type " + TrafficType.Guest + " and guest type " + Network.GuestType.Shared);
}
DomainVO domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find domain by specified id");
}
_accountMgr.checkAccess(caller, domain);
}
isDomainSpecific = true;
} else if (subdomainAccess != null) {
throw new InvalidParameterValueException("Parameter subDomainAccess can be specified only with aclType=Domain");
}
boolean isDomainSpecific = isDomainSpecificNetworkRequested(caller, domainId, subdomainAccess, ntwkOff, aclType);
if (aclType == ACLType.Domain) {
owner = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM);
@ -1610,7 +1530,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
validateRouterIps(routerIp, routerIpv6, startIP, endIP, gateway, netmask, startIPv6, endIPv6, ip6Cidr);
validateSharedNetworkRouterIPs(gateway, startIP, endIP, netmask, routerIPv4, routerIPv6, startIPv6, endIPv6, ip6Cidr, ntwkOff);
Pair<String, String> ip6GatewayCidr = null;
if (zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getGuestType() == GuestType.Isolated) {
ipv6 = _networkOfferingDao.isIpv6Supported(ntwkOff.getId());
@ -1682,7 +1603,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (cidr != null && providersConfiguredForExternalNetworking(ntwkProviders)) {
if (ntwkOff.getGuestType() == GuestType.Shared && (zone.getNetworkType() == NetworkType.Advanced) && isSharedNetworkOfferingWithServices(networkOfferingId)) {
// validate if CIDR specified overlaps with any of the CIDR's allocated for isolated networks and shared networks in the zone
checkSharedNetworkCidrOverlap(zoneId, pNtwk.getId(), cidr);
checkSharedNetworkCidrOverlap(zone.getId(), pNtwk.getId(), cidr);
} else {
// if the guest network is for the VPC, if any External Provider are supported in VPC
// cidr will not be null as it is generated from the super cidr of vpc.
@ -1707,10 +1628,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
// Can add vlan range only to the network which allows it
if (createVlan && !ntwkOff.isSpecifyIpRanges()) {
throwInvalidIdException("Network offering with specified id doesn't support adding multiple ip ranges", ntwkOff.getUuid(), "networkOfferingId");
throwInvalidIdException("Network offering with specified id doesn't support adding multiple ip ranges", ntwkOff.getUuid(), NETWORK_OFFERING_ID);
}
Pair<Integer, Integer> interfaceMTUs = validateMtuConfig(publicMtu, privateMtu, zoneId);
Pair<Integer, Integer> interfaceMTUs = validateMtuConfig(publicMtu, privateMtu, zone.getId());
mtuCheckForVpcNetwork(vpcId, interfaceMTUs, publicMtu, privateMtu);
Network associatedNetwork = null;
@ -1729,9 +1650,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
checkNetworkDns(ipv6, ntwkOff, vpcId, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2);
Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zoneId,
Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zone.getId(),
domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId, privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan,
externalId, routerIp, routerIpv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs);
externalId, routerIPv4, routerIPv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs);
// retrieve, acquire and associate the correct ip adresses
checkAndSetRouterSourceNatIp(owner, cmd, network);
if (hideIpAddressUsage) {
_networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false));
@ -1748,6 +1672,213 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
return network;
}
void checkAndSetRouterSourceNatIp(Account owner, CreateNetworkCmd cmd, Network network) throws InsufficientAddressCapacityException, ResourceAllocationException {
String sourceNatIp = cmd.getSourceNatIP();
if (sourceNatIp == null) {
s_logger.debug(String.format("no source nat ip given for create network %s command, using something arbitrary.", cmd.getNetworkName()));
return; // nothing to try
}
IpAddress ip = allocateIP(owner, cmd.getZoneId(), network.getId(), null, sourceNatIp);
try {
associateIPToNetwork(ip.getId(), network.getId());
} catch (ResourceUnavailableException e) {
String msg = String.format("can´t use %s as sourcenat IP address for network %s/%s as it is un available", sourceNatIp, network.getName(), network.getUuid());
s_logger.error(msg);
throw new CloudRuntimeException(msg,e);
}
}
/**
* @param cmd
* @param network
* @return whether the sourceNat is changed, and consequently restart is needed
* @throws InsufficientAddressCapacityException
* @throws ResourceAllocationException
*/
private boolean checkAndUpdateRouterSourceNatIp(UpdateNetworkCmd cmd, Network network) {
IPAddressVO requestedIp = checkSourceNatIpAddressForUpdate(cmd, network);
if (requestedIp == null) return false; // ip not associated with this network
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), true);
if (! userIps.isEmpty()) {
try {
_ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
} catch (Exception e) { // pokemon execption from transaction
String msg = String.format("Update of source NAT ip to %s for network \"%s\"/%s failed due to %s",
requestedIp.getAddress().addr(), network.getName(), network.getUuid(), e.getLocalizedMessage());
s_logger.error(msg);
throw new CloudRuntimeException(msg, e);
}
}
return true;
}
@Nullable
private IPAddressVO checkSourceNatIpAddressForUpdate(UpdateNetworkCmd cmd, Network network) {
String sourceNatIp = cmd.getSourceNatIP();
if (sourceNatIp == null) {
s_logger.trace(String.format("no source NAT ip given to update network %s with.", cmd.getNetworkName()));
return null;
} else {
s_logger.info(String.format("updating network %s to have source NAT ip %s", cmd.getNetworkName(), sourceNatIp));
}
// check if the address is already aqcuired for this network
IPAddressVO requestedIp = _ipAddressDao.findByIp(sourceNatIp);
if (requestedIp == null || requestedIp.getAssociatedWithNetworkId() == null || ! requestedIp.getAssociatedWithNetworkId().equals(network.getId())) {
s_logger.warn(String.format("Source NAT IP %s is not associated with network %s/%s. It cannot be used as source NAT IP.",
sourceNatIp, network.getName(), network.getUuid()));
return null;
}
// check if it is the current source NAT address
if (requestedIp.isSourceNat()) {
s_logger.info(String.format("IP address %s is allready the source Nat address. Not updating!", sourceNatIp));
return null;
}
return requestedIp;
}
@Nullable
private ACLType getAclType(Account caller, String aclTypeStr, NetworkOffering ntwkOff) {
// Only domain and account ACL types are supported in Acton.
ACLType aclType = null;
if (aclTypeStr != null) {
aclType = getAclType(aclTypeStr, ntwkOff);
} else {
aclType = getAclType(caller, ntwkOff, aclType);
}
return aclType;
}
@NotNull
private static ACLType getAclType(String aclTypeStr, NetworkOffering ntwkOff) {
ACLType aclType;
if (aclTypeStr.equalsIgnoreCase(ACLType.Account.toString())) {
aclType = ACLType.Account;
} else if (aclTypeStr.equalsIgnoreCase(ACLType.Domain.toString())) {
aclType = ACLType.Domain;
} else {
throw new InvalidParameterValueException("Incorrect aclType specified. Check the API documentation for supported types");
}
// In 3.0 all Shared networks should have aclType == Domain, all Isolated networks aclType==Account
if (ntwkOff.getGuestType() == GuestType.Isolated && aclType != ACLType.Account) {
throw new InvalidParameterValueException("AclType should be " + ACLType.Account + " for network of type " + GuestType.Isolated);
}
return aclType;
}
private ACLType getAclType(Account caller, NetworkOffering ntwkOff, ACLType aclType) {
if (ntwkOff.getGuestType() == GuestType.Isolated || ntwkOff.getGuestType() == GuestType.L2) {
aclType = ACLType.Account;
} else if (ntwkOff.getGuestType() == GuestType.Shared) {
if (_accountMgr.isRootAdmin(caller.getId())) {
aclType = ACLType.Domain;
} else if (_accountMgr.isNormalUser(caller.getId())) {
aclType = ACLType.Account;
} else {
throw new InvalidParameterValueException("AclType must be specified for shared network created by domain admin");
}
}
return aclType;
}
private void validateZoneAvailability(Account caller, DataCenter zone) {
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) {
// See DataCenterVO.java
PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled");
ex.addProxyObject(zone.getUuid(), "zoneId");
throw ex;
}
}
private boolean isDomainSpecificNetworkRequested(Account caller, Long domainId, Boolean subdomainAccess, NetworkOffering ntwkOff, ACLType aclType) {
boolean isDomainSpecific = false;
// Check if the network is domain specific
if (aclType == ACLType.Domain) {
// only Admin can create domain with aclType=Domain
if (!_accountMgr.isAdmin(caller.getId())) {
throw new PermissionDeniedException("Only admin can create networks with aclType=Domain");
}
// only shared networks can be Domain specific
if (ntwkOff.getGuestType() != GuestType.Shared) {
throw new InvalidParameterValueException("Only " + GuestType.Shared + " networks can have aclType=" + ACLType.Domain);
}
if (domainId != null) {
if (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != GuestType.Shared) {
throw new InvalidParameterValueException("Domain level networks are supported just for traffic type " + TrafficType.Guest + " and guest type " + GuestType.Shared);
}
DomainVO domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Unable to find domain by specified id");
}
_accountMgr.checkAccess(caller, domain);
}
isDomainSpecific = true;
} else if (subdomainAccess != null) {
throw new InvalidParameterValueException("Parameter subDomainAccess can be specified only with aclType=Domain");
}
return isDomainSpecific;
}
@NotNull
private DataCenter getAndValidateZone(CreateNetworkCmd cmd, PhysicalNetwork pNtwk) {
Long zoneId = (cmd.getZoneId() == null) ? pNtwk.getDataCenterId() : cmd.getZoneId();
DataCenter zone = _dcDao.findById(zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Specified zone id was not found");
}
return zone;
}
/**
// validate physical network and zone
// Check if physical network exists
*
* @param physicalNetworkId the id of the required physical network
* @return the data object for the physical network
*/
@NotNull
private PhysicalNetwork getAndValidatePhysicalNetwork(Long physicalNetworkId) {
PhysicalNetwork pNtwk = null;
if (physicalNetworkId != null) {
pNtwk = getPhysicalNetwork(physicalNetworkId);
if (pNtwk == null) {
throw new InvalidParameterValueException("Unable to find a physical network having the specified physical network id");
}
} else {
throw new CloudRuntimeException("cannot create Guestnetwork without physical network.");
}
return pNtwk;
}
private Account getOwningAccount(CreateNetworkCmd cmd, Account caller) {
Account owner = null;
Long domainId = cmd.getDomainId();
if ((cmd.getAccountName() != null && domainId != null) || cmd.getProjectId() != null) {
owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), domainId, cmd.getProjectId());
} else {
s_logger.info(String.format("Assigning the network to caller:%s because either projectId or accountname and domainId are not provided", caller.getAccountName()));
owner = caller;
}
return owner;
}
@NotNull
private NetworkOffering getAndValidateNetworkOffering(Long networkOfferingId) {
NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
if (ntwkOff == null || ntwkOff.isSystemOnly()) {
InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering by specified id");
if (ntwkOff != null) {
ex.addProxyObject(ntwkOff.getUuid(), NETWORK_OFFERING_ID);
}
throw ex;
}
return ntwkOff;
}
protected void mtuCheckForVpcNetwork(Long vpcId, Pair<Integer, Integer> interfaceMTUs, Integer publicMtu, Integer privateMtu) {
if (vpcId != null && publicMtu != null) {
VpcVO vpc = _vpcDao.findById(vpcId);
@ -1862,7 +1993,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
private void validateNetworkOfferingForNonRootAdminUser(NetworkOfferingVO ntwkOff) {
private void validateNetworkOfferingForNonRootAdminUser(NetworkOffering ntwkOff) {
if (ntwkOff.getTrafficType() != TrafficType.Guest) {
throw new InvalidParameterValueException("This user can only create a Guest network");
}
@ -1924,7 +2055,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, final String vlanIdFinal,
final Boolean bypassVlanOverlapCheck, final String name, final String displayText, final Account caller, final Long physicalNetworkId, final Long zoneId, final Long domainId,
final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway, final String ip6Cidr,
final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal,
final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOffering ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal,
final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6,
final Network associatedNetwork, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs) throws InsufficientCapacityException, ResourceAllocationException {
try {
@ -2373,7 +2504,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
if (networkOfferingId != null) {
sc.addAnd("networkOfferingId", SearchCriteria.Op.EQ, networkOfferingId);
sc.addAnd(NETWORK_OFFERING_ID, SearchCriteria.Op.EQ, networkOfferingId);
}
if (associatedNetworkId != null) {
@ -2796,6 +2927,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
_accountMgr.checkAccess(callerAccount, AccessType.OperateEntry, true, network);
_accountMgr.checkAccess(_accountMgr.getActiveAccountById(network.getAccountId()), offering, _dcDao.findById(network.getDataCenterId()));
restartNetwork |= checkAndUpdateRouterSourceNatIp(cmd, network);
if (cmd instanceof UpdateNetworkCmdByAdmin) {
final Boolean hideIpAddressUsage = ((UpdateNetworkCmdByAdmin) cmd).getHideIpAddressUsage();
if (hideIpAddressUsage != null) {
@ -2844,13 +2977,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId);
if (networkOfferingId != null) {
if (networkOffering == null || networkOffering.isSystemOnly()) {
throwInvalidIdException("Unable to find network offering with specified id", networkOfferingId.toString(), "networkOfferingId");
throwInvalidIdException("Unable to find network offering with specified id", networkOfferingId.toString(), NETWORK_OFFERING_ID);
}
// network offering should be in Enabled state
if (networkOffering.getState() != NetworkOffering.State.Enabled) {
throwInvalidIdException("Network offering with specified id is not in " + NetworkOffering.State.Enabled + " state, can't upgrade to it", networkOffering.getUuid(),
"networkOfferingId");
NETWORK_OFFERING_ID);
}
//can't update from vpc to non-vpc network offering
boolean forVpcNew = _configMgr.isOfferingForVpc(networkOffering);
@ -2893,10 +3026,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
if (checkAndUpdateNetworkDns(network, networkOfferingChanged ? networkOffering : oldNtwkOff, ip4Dns1, ip4Dns2,
ip6Dns1, ip6Dns2)) {
restartNetwork = true;
}
restartNetwork |= checkAndUpdateNetworkDns(network, networkOfferingChanged ? networkOffering : oldNtwkOff, ip4Dns1, ip4Dns2,
ip6Dns1, ip6Dns2);
final Map<String, String> newSvcProviders = networkOfferingChanged
? _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId())
@ -3093,7 +3224,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (servicesNotInNewOffering != null && !servicesNotInNewOffering.isEmpty()) {
_networkMgr.cleanupConfigForServicesInNetwork(servicesNotInNewOffering, network);
}
} catch (Throwable e) {
} catch (Exception e) { // old pokemon catch that used to catch throwable
s_logger.debug("failed to cleanup config related to unused services error:" + e.getMessage());
}
@ -3647,7 +3778,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (newNtwkOff == null || newNtwkOff.isSystemOnly()) {
InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering.");
if (newNtwkOff != null) {
ex.addProxyObject(String.valueOf(newNtwkOff.getId()), "networkOfferingId");
ex.addProxyObject(String.valueOf(newNtwkOff.getId()), NETWORK_OFFERING_ID);
}
throw ex;
}

View File

@ -53,7 +53,9 @@ import org.apache.cloudstack.api.command.user.vpc.CreateVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd;
import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd;
import org.apache.cloudstack.api.command.user.vpc.ListVPCOfferingsCmd;
import org.apache.cloudstack.api.command.user.vpc.ListVPCsCmd;
import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@ -62,6 +64,7 @@ import org.apache.cloudstack.query.QueryService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@ -1091,6 +1094,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff,
vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2);
vpc.setPublicMtu(publicMtu);
vpc.setDisplay(Boolean.TRUE.equals(displayVpc));
return createVpc(displayVpc, vpc);
}
@ -1098,9 +1102,24 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
@Override
@ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true)
public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException {
return createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(),
cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu());
// associate cmd.getSourceNatIP() with this vpc
allocateSourceNatIp(vpc, cmd.getSourceNatIP());
return vpc;
}
private void allocateSourceNatIp(Vpc vpc, String sourceNatIP) {
Account account = _accountMgr.getAccount(vpc.getAccountId());
DataCenter zone = _dcDao.findById(vpc.getZoneId());
// reserve this ip and then
try {
IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId(), zone, null, sourceNatIP);
this.associateIPToVpc(ip.getId(), vpc.getId());
} catch (ResourceAllocationException | ResourceUnavailableException | InsufficientAddressCapacityException e){
throw new CloudRuntimeException("new source NAT address cannot be acquired", e);
}
}
@DB
@ -1126,10 +1145,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
return Transaction.execute(new TransactionCallback<VpcVO>() {
@Override
public VpcVO doInTransaction(final TransactionStatus status) {
if (displayVpc != null) {
vpc.setDisplay(displayVpc);
}
final VpcVO persistedVpc = vpcDao.persist(vpc, finalizeServicesAndProvidersForVpc(vpc.getZoneId(), vpc.getVpcOfferingId()));
_resourceLimitMgr.incrementResourceCount(vpc.getAccountId(), ResourceType.vpc);
s_logger.debug("Created VPC " + persistedVpc);
@ -1242,9 +1257,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
}
}
@Override
public Vpc updateVpc(UpdateVPCCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException {
return updateVpc(cmd.getId(), cmd.getVpcName(), cmd.getDisplayText(), cmd.getCustomId(), cmd.isDisplayVpc(), cmd.getPublicMtu(), cmd.getSourceNatIP());
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VPC_UPDATE, eventDescription = "updating vpc")
public Vpc updateVpc(final long vpcId, final String vpcName, final String displayText, final String customId, final Boolean displayVpc, Integer mtu) {
public Vpc updateVpc(final long vpcId, final String vpcName, final String displayText, final String customId, final Boolean displayVpc, Integer mtu, String sourceNatIp) throws ResourceUnavailableException, InsufficientCapacityException {
CallContext.current().setEventDetails(" Id: " + vpcId);
final Account caller = CallContext.current().getCallingAccount();
@ -1279,14 +1299,80 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
updateMtuOfVpcNetwork(vpcToUpdate, vpc, mtu);
}
if (vpcDao.update(vpcId, vpc)) {
boolean restartRequired = checkAndUpdateRouterSourceNatIp(vpcToUpdate, sourceNatIp);
if (vpcDao.update(vpcId, vpc) || restartRequired) { // Note that the update may fail because nothing has changed, other than the sourcenat ip
s_logger.debug("Updated VPC id=" + vpcId);
if (restartRequired) {
if (s_logger.isDebugEnabled()) {
s_logger.debug(String.format("restarting vpc %s/%s, due to changing sourcenat in Update VPC call", vpc.getName(), vpc.getUuid()));
}
final User callingUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId());
restartVpc(vpcId, true, false, false, callingUser);
} else {
if (s_logger.isDebugEnabled()) {
s_logger.debug("no restart needed.");
}
}
return vpcDao.findById(vpcId);
} else {
s_logger.error(String.format("failed to update vpc %s/%s",vpc.getName(), vpc.getUuid()));
return null;
}
}
private boolean checkAndUpdateRouterSourceNatIp(Vpc vpc, String sourceNatIp) {
IPAddressVO requestedIp = validateSourceNatip(vpc, sourceNatIp);
if (requestedIp == null) return false; // ip not associated with this network
List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedVpc(vpc.getId(), true);
if (! userIps.isEmpty()) {
try {
_ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps);
} catch (Exception e) { // pokemon exception from transaction
String msg = String.format("Update of source NAT ip to %s for network \"%s\"/%s failed due to %s",
requestedIp.getAddress().addr(), vpc.getName(), vpc.getUuid(), e.getLocalizedMessage());
s_logger.error(msg);
throw new CloudRuntimeException(msg, e);
}
}
return true;
}
@Nullable
protected IPAddressVO validateSourceNatip(Vpc vpc, String sourceNatIp) {
if (sourceNatIp == null) {
s_logger.trace(String.format("no source NAT ip given to update vpc %s with.", vpc.getName()));
return null;
} else {
s_logger.info(String.format("updating VPC %s to have source NAT ip %s", vpc.getName(), sourceNatIp));
}
IPAddressVO requestedIp = getIpAddressVO(vpc, sourceNatIp);
if (requestedIp == null) return null;
// check if it is the current source NAT address
if (requestedIp.isSourceNat()) {
s_logger.info(String.format("IP address %s is already the source Nat address. Not updating!", sourceNatIp));
return null;
}
if (_firewallDao.countRulesByIpId(requestedIp.getId()) > 0) {
s_logger.info(String.format("IP address %s has firewall/portforwarding rules. Not updating!", sourceNatIp));
return null;
}
return requestedIp;
}
@Nullable
private IPAddressVO getIpAddressVO(Vpc vpc, String sourceNatIp) {
// check if the address is already aqcuired for this network
IPAddressVO requestedIp = _ipAddressDao.findByIp(sourceNatIp);
if (requestedIp == null || requestedIp.getVpcId() == null || ! requestedIp.getVpcId().equals(vpc.getId())) {
s_logger.warn(String.format("Source NAT IP %s is not associated with network %s/%s. It cannot be used as source NAT IP.",
sourceNatIp, vpc.getName(), vpc.getUuid()));
return null;
}
return requestedIp;
}
protected Integer validateMtu(VpcVO vpcToUpdate, Integer mtu) {
Long zoneId = vpcToUpdate.getZoneId();
if (mtu == null || NetworkService.AllowUsersToSpecifyVRMtu.valueIn(zoneId)) {
@ -1373,6 +1459,13 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
return success;
}
@Override
public Pair<List<? extends Vpc>, Integer> listVpcs(ListVPCsCmd cmd) {
return listVpcs(cmd.getId(), cmd.getVpcName(), cmd.getDisplayText(), cmd.getSupportedServices(), cmd.getCidr(), cmd.getVpcOffId(),
cmd.getState(), cmd.getAccountName(), cmd.getDomainId(), cmd.getKeyword(), cmd.getStartIndex(), cmd.getPageSizeVal(),
cmd.getZoneId(), cmd.isRecursive(), cmd.listAll(), cmd.getRestartRequired(), cmd.getTags(), cmd.getProjectId(),
cmd.getDisplay());
}
@Override
public Pair<List<? extends Vpc>, Integer> listVpcs(final Long id, final String vpcName, final String displayText, final List<String> supportedServicesStr, final String cidr,
final Long vpcOffId, final String state, final String accountName, Long domainId, final String keyword, final Long startIndex, final Long pageSizeVal,
@ -1476,7 +1569,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
final boolean listBySupportedServices = supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty();
if (listBySupportedServices) {
final List<VpcVO> supportedVpcs = new ArrayList<VpcVO>();
final List<Vpc> supportedVpcs = new ArrayList<>();
Service[] supportedServices = null;
if (listBySupportedServices) {
@ -1501,22 +1594,20 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
final List<? extends Vpc> wPagination = StringUtils.applyPagination(supportedVpcs, startIndex, pageSizeVal);
if (wPagination != null) {
final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, supportedVpcs.size());
return listWPagination;
return new Pair<>(wPagination, supportedVpcs.size());
}
return new Pair<List<? extends Vpc>, Integer>(supportedVpcs, supportedVpcs.size());
return new Pair<>(supportedVpcs, supportedVpcs.size());
} else {
final List<? extends Vpc> wPagination = StringUtils.applyPagination(vpcs, startIndex, pageSizeVal);
if (wPagination != null) {
final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, vpcs.size());
return listWPagination;
return new Pair<>(wPagination, vpcs.size());
}
return new Pair<List<? extends Vpc>, Integer>(vpcs, vpcs.size());
return new Pair<>(vpcs, vpcs.size());
}
}
protected List<Service> getSupportedServices() {
final List<Service> services = new ArrayList<Service>();
final List<Service> services = new ArrayList<>();
services.add(Network.Service.Dhcp);
services.add(Network.Service.Dns);
services.add(Network.Service.UserData);

View File

@ -37,6 +37,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd;
@ -547,16 +548,68 @@ public class ConfigurationManagerTest {
assertThat(configurationMgr.searchForNetworkOfferings(cmd).second(), is(2));
}
@Test
public void validateEmptySourceNatServiceCapablitiesTest() {
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
}
@Test
public void validateInvalidSourceNatTypeForSourceNatServiceCapablitiesTest() {
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "perDomain");
boolean caught = false;
try {
configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
} catch (InvalidParameterValueException e) {
Assert.assertTrue(e.getMessage(), e.getMessage().contains("Either peraccount or perzone source NAT type can be specified for SupportedSourceNatTypes"));
caught = true;
}
Assert.assertTrue("should not be accepted", caught);
}
@Test
public void validateInvalidBooleanValueForSourceNatServiceCapablitiesTest() {
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "maybe");
boolean caught = false;
try {
configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
} catch (InvalidParameterValueException e) {
Assert.assertTrue(e.getMessage(), e.getMessage().contains("Unknown specified value for RedundantRouter"));
caught = true;
}
Assert.assertTrue("should not be accepted", caught);
}
@Test
public void validateInvalidCapabilityForSourceNatServiceCapablitiesTest() {
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.ElasticIp, "perDomain");
boolean caught = false;
try {
configurationMgr.validateSourceNatServiceCapablities(sourceNatServiceCapabilityMap);
} catch (InvalidParameterValueException e) {
Assert.assertTrue(e.getMessage(), e.getMessage().contains("Only SupportedSourceNatTypes, Network.Capability[name=RedundantRouter] capabilities can be specified for source nat service"));
caught = true;
}
Assert.assertTrue("should not be accepted", caught);
}
@Test
public void validateEmptyStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap);
}
@Test
public void validateInvalidStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "Frue and Talse");
boolean caught = false;
@ -569,9 +622,43 @@ public class ConfigurationManagerTest {
Assert.assertTrue("should not be accepted", caught);
}
@Test
public void isRedundantRouter() {
Map<Network.Service, Set<Network.Provider>> serviceCapabilityMap = new HashMap<>();
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount");
sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "true");
Assert.assertTrue(configurationMgr.isRedundantRouter(serviceCapabilityMap, sourceNatServiceCapabilityMap));
}
@Test
public void isSharedSourceNat() {
Map<Network.Service, Set<Network.Provider>> serviceCapabilityMap = new HashMap<>();
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "perzone");
Assert.assertTrue(configurationMgr.isSharedSourceNat(serviceCapabilityMap, sourceNatServiceCapabilityMap));
}
@Test
public void isNotSharedSourceNat() {
Map<Network.Service, Set<Network.Provider>> serviceCapabilityMap = new HashMap<>();
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount");
Assert.assertFalse(configurationMgr.isSharedSourceNat(serviceCapabilityMap, sourceNatServiceCapabilityMap));
}
@Test
public void sourceNatCapabilitiesContainValidValues() {
Map<Capability, String> sourceNatServiceCapabilityMap = new HashMap<>();
sourceNatServiceCapabilityMap.put(Capability.SupportedSourceNatTypes, "peraccount");
sourceNatServiceCapabilityMap.put(Capability.RedundantRouter, "True");
Assert.assertTrue(configurationMgr.sourceNatCapabilitiesContainValidValues(sourceNatServiceCapabilityMap));
}
@Test
public void validateTTStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "true and Talse");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True");
@ -580,7 +667,7 @@ public class ConfigurationManagerTest {
@Test
public void validateFTStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "false");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True");
@ -589,7 +676,7 @@ public class ConfigurationManagerTest {
@Test
public void validateTFStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "true and Talse");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "false");
@ -608,7 +695,7 @@ public class ConfigurationManagerTest {
@Test
public void validateFFStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>();
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "false");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "False");

View File

@ -23,11 +23,13 @@ import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import com.cloud.user.Account;
import org.junit.Assert;
@ -230,4 +232,14 @@ public class IpAddressManagerTest {
return network;
}
@Test
public void updateSourceNatIpAddress() throws Exception {
IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class);
IPAddressVO oldIp = Mockito.mock(IPAddressVO.class);
List<IPAddressVO> userIps = new Vector<>();
userIps.add(oldIp);
ipAddressManager.updateSourceNatIpAddress(requestedIp, userIps);
verify(requestedIp).setSourceNat(true);
verify(oldIp).setSourceNat(false);
}
}

View File

@ -17,8 +17,10 @@
package com.cloud.network;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@ -34,6 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.cloud.exception.InsufficientAddressCapacityException;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
@ -81,7 +84,6 @@ import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
import com.cloud.org.Grouping;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountManagerImpl;
import com.cloud.user.AccountService;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
@ -122,8 +124,6 @@ public class NetworkServiceImplTest {
@Mock
NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
@Mock
AccountManager accountMgr;
@Mock
EntityManager entityMgr;
@Mock
NetworkService networkService;
@ -162,8 +162,8 @@ public class NetworkServiceImplTest {
@Mock
ServiceOfferingVO serviceOfferingVoMock;
@InjectMocks
AccountManagerImpl accountManagerImpl;
@Mock
IpAddressManager ipAddressManager;
@Mock
ConfigKey<Integer> privateMtuKey;
@Mock
@ -223,7 +223,7 @@ public class NetworkServiceImplTest {
service._networkOfferingDao = networkOfferingDao;
service._physicalNetworkDao = physicalNetworkDao;
service._dcDao = dcDao;
service._accountMgr = accountMgr;
service._accountMgr = accountManager;
service._networkMgr = networkManager;
service.alertManager = alertManager;
service._configMgr = configMgr;
@ -236,6 +236,7 @@ public class NetworkServiceImplTest {
service.routerDao = routerDao;
service.commandSetupHelper = commandSetupHelper;
service.networkHelper = networkHelper;
service._ipAddrMgr = ipAddressManager;
PowerMockito.mockStatic(CallContext.class);
CallContext callContextMock = PowerMockito.mock(CallContext.class);
PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
@ -248,8 +249,8 @@ public class NetworkServiceImplTest {
Mockito.when(networkOfferingDao.findById(1L)).thenReturn(offering);
Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(phyNet);
Mockito.when(dcDao.findById(Mockito.anyLong())).thenReturn(dc);
Mockito.lenient().doNothing().when(accountMgr).checkAccess(accountMock, networkOffering, dc);
Mockito.when(accountMgr.isRootAdmin(accountMock.getId())).thenReturn(true);
Mockito.lenient().doNothing().when(accountManager).checkAccess(accountMock, networkOffering, dc);
Mockito.when(accountManager.isRootAdmin(accountMock.getId())).thenReturn(true);
}
@Test
@ -352,6 +353,7 @@ public class NetworkServiceImplTest {
ReflectionTestUtils.setField(createNetworkCmd, "privateMtu", privateMtu);
ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
Mockito.when(offering.isSystemOnly()).thenReturn(false);
Mockito.when(dc.getId()).thenReturn(1L);
Mockito.when(dc.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
Map<String, String> networkProvidersMap = new HashMap<String, String>();
Mockito.when(networkManager.finalizeServicesAndProvidersForNetwork(ArgumentMatchers.any(NetworkOffering.class), anyLong())).thenReturn(networkProvidersMap);
@ -409,6 +411,7 @@ public class NetworkServiceImplTest {
ReflectionTestUtils.setField(createNetworkCmd, "privateMtu", privateMtu);
ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
ReflectionTestUtils.setField(createNetworkCmd, "vpcId", 1L);
Mockito.when(dc.getId()).thenReturn(1L);
Mockito.when(configMgr.isOfferingForVpc(offering)).thenReturn(true);
Mockito.when(vpcDao.findById(anyLong())).thenReturn(vpc);
@ -562,7 +565,7 @@ public class NetworkServiceImplTest {
}
}
@Test(expected = InvalidParameterValueException.class)
@Test(expected = CloudRuntimeException.class)
public void testCreateNetworkDnsOfferingServiceFailure() {
registerCallContext();
CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
@ -577,7 +580,7 @@ public class NetworkServiceImplTest {
}
}
@Test(expected = InvalidParameterValueException.class)
@Test(expected = CloudRuntimeException.class)
public void testCreateIp4NetworkIp6DnsFailure() {
registerCallContext();
CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
@ -770,4 +773,143 @@ public class NetworkServiceImplTest {
networkServiceImplMock.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(1l);
}
@Test
public void validateNotSharedNetworkRouterIPv4() {
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.L2);
service.validateSharedNetworkRouterIPs(null, null, null, null, null, null, null, null, null, ntwkOff);
}
@Test
public void validateSharedNetworkRouterIPs() {
String startIP = "10.0.16.2";
String endIP = "10.0.16.100";
String routerIPv4 = "10.0.16.100";
String routerPv6 = "fd17:ac56:1234:2000::fb";
String startIPv6 = "fd17:ac56:1234:2000::1";
String endIPv6 = "fd17:ac56:1234:2000::fc";
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
service.validateSharedNetworkRouterIPs(IP4_GATEWAY, startIP, endIP, IP4_NETMASK, routerIPv4, routerPv6, startIPv6, endIPv6, IP6_CIDR, ntwkOff);
}
@Test
public void validateSharedNetworkWrongRouterIPv4() {
String startIP = "10.0.16.2";
String endIP = "10.0.16.100";
String routerIPv4 = "10.0.16.101";
String routerPv6 = "fd17:ac56:1234:2000::fb";
String startIPv6 = "fd17:ac56:1234:2000::1";
String endIPv6 = "fd17:ac56:1234:2000::fc";
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
boolean passing = false;
try {
service.validateSharedNetworkRouterIPs(IP4_GATEWAY, startIP, endIP, IP4_NETMASK, routerIPv4, routerPv6, startIPv6, endIPv6, IP6_CIDR, ntwkOff);
} catch (CloudRuntimeException e) {
Assert.assertTrue(e.getMessage().contains("Router IPv4 IP provided is not within the specified range: "));
passing = true;
}
Assert.assertTrue(passing);
}
@Test
public void validateSharedNetworkNoEndOfIPv6Range() {
String startIP = null;
String endIP = null;
String routerIPv4 = null;
String routerPv6 = "fd17:ac56:1234:2000::1";
String startIPv6 = "fd17:ac56:1234:2000::1";
String endIPv6 = null;
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
service.validateSharedNetworkRouterIPs(IP4_GATEWAY, startIP, endIP, IP4_NETMASK, routerIPv4, routerPv6, startIPv6, endIPv6, IP6_CIDR, ntwkOff);
}
@Test
public void validateSharedNetworkIPv6RouterNotInRange() {
String routerIPv4 = null;
String routerIPv6 = "fd17:ac56:1234:2001::1";
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
boolean passing = true;
try {
service.validateSharedNetworkRouterIPs(IP4_GATEWAY, null, null, IP4_NETMASK, routerIPv4, routerIPv6, null, null, IP6_CIDR, ntwkOff);
passing = false;
} catch (CloudRuntimeException e) {
Assert.assertTrue(e.getMessage().contains("Router IPv6 address provided is not with the network range"));
}
Assert.assertTrue(passing);
}
@Test
public void invalidateSharedNetworkIPv6RouterAddress() {
String routerIPv6 = "fd17:ac56:1234:2000::fg";
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
boolean passing = false;
try {
service.validateSharedNetworkRouterIPs(IP4_GATEWAY, null, null, IP4_NETMASK, null, routerIPv6, null, null, IP6_CIDR, ntwkOff);
} catch (CloudRuntimeException e) {
Assert.assertTrue(e.getMessage().contains("Router IPv6 address provided is of incorrect format"));
passing = true;
}
Assert.assertTrue(passing);
}
@Test
public void invalidateSharedNetworkIPv4RouterAddress() {
String routerIPv4 = "10.100.1000.1";
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
when(ntwkOff.getGuestType()).thenReturn(Network.GuestType.Shared);
boolean passing = false;
try {
service.validateSharedNetworkRouterIPs(IP4_GATEWAY, null, null, IP4_NETMASK, routerIPv4, null, null, null, IP6_CIDR, ntwkOff);
} catch (CloudRuntimeException e) {
Assert.assertTrue(e.getMessage().contains("Router IPv4 IP provided is of incorrect format"));
passing = true;
}
Assert.assertTrue(passing);
}
@Test
public void checkAndDontSetSourceNatIp() {
CreateNetworkCmd cmd = new CreateNetworkCmd();
try {
service.checkAndSetRouterSourceNatIp(account, cmd, null);
} catch (InsufficientAddressCapacityException | ResourceAllocationException e) {
Assert.fail(e.getMessage());
}
}
@Test
public void checkAndSetSourceNatIp() {
String srcNatIp = "10.100.1000.10000";
Long networkOfferingId = 2l;
Long zoneId = 3l;
registerCallContext();
ReflectionTestUtils.setField(createNetworkCmd, "networkOfferingId", networkOfferingId);
ReflectionTestUtils.setField(createNetworkCmd, "sourceNatIP", srcNatIp);
ReflectionTestUtils.setField(createNetworkCmd, "zoneId", zoneId);
ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
NetworkVO networkVO = Mockito.spy(NetworkVO.class);
IpAddress ipAddress = Mockito.mock(IPAddressVO.class);
NetworkOffering ntwkOff = Mockito.mock(NetworkOffering.class);
Long networkId = 7l;
when(networkVO.getId()).thenReturn(networkId);
when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
when(networkDao.findById(networkId)).thenReturn(networkVO);
when(entityMgr.findById(NetworkOffering.class, networkOfferingId)).thenReturn(ntwkOff);
when(entityMgr.findById(eq(DataCenter.class), anyLong())).thenReturn(dc);
when(ipAddress.getId()).thenReturn(5l);
when(networkVO.getId()).thenReturn(networkId);
when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
try {
when(ipAddressManager.allocateIp(any(), anyBoolean(), any(), anyLong(), any(), any(), eq(srcNatIp))).thenReturn(ipAddress);
service.checkAndSetRouterSourceNatIp(account, createNetworkCmd, networkVO);
} catch (InsufficientAddressCapacityException | ResourceAllocationException e) {
Assert.fail(e.getMessage());
}
}
}

View File

@ -42,8 +42,10 @@ import java.util.UUID;
import com.cloud.alert.AlertManager;
import com.cloud.network.NetworkService;
import com.cloud.network.dao.FirewallRulesDao;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.junit.After;
@ -149,6 +151,8 @@ public class VpcManagerImplTest {
AlertManager alertManager;
@Mock
NetworkService networkServiceMock;
@Mock
FirewallRulesDao firewallDao;
public static final long ACCOUNT_ID = 1;
private AccountVO account;
@ -200,6 +204,7 @@ public class VpcManagerImplTest {
manager._vpcOffDao = vpcOfferingDao;
manager._dcDao = dataCenterDao;
manager._ntwkSvc = networkServiceMock;
manager._firewallDao = firewallDao;
CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
registerCallContext();
}
@ -354,9 +359,10 @@ public class VpcManagerImplTest {
}
@Test
public void testUpdateVpcNetwork() throws ResourceUnavailableException {
public void testUpdateVpcNetwork() throws ResourceUnavailableException, InsufficientCapacityException {
long vpcId = 1L;
Integer publicMtu = 1450;
String sourceNatIp = "1.2.3.4";
Account accountMock = Mockito.mock(Account.class);
VpcVO vpcVO = new VpcVO();
@ -398,9 +404,31 @@ public class VpcManagerImplTest {
Mockito.when(networkDao.update(anyLong(), any())).thenReturn(true);
Mockito.when(vpcDao.update(vpcId, vpcVO)).thenReturn(true);
manager.updateVpc(vpcId, null, null, null, true, publicMtu);
Assert.assertEquals(publicMtu, vpcVO.getPublicMtu());
UpdateVPCCmd cmd = Mockito.mock(UpdateVPCCmd.class);
Mockito.when(cmd.getId()).thenReturn(vpcId);
Mockito.when(cmd.getVpcName()).thenReturn(null);
Mockito.when(cmd.getDisplayText()).thenReturn(null);
Mockito.when(cmd.getCustomId()).thenReturn(null);
Mockito.when(cmd.isDisplayVpc()).thenReturn(true);
Mockito.when(cmd.getPublicMtu()).thenReturn(publicMtu);
Mockito.when(cmd.getSourceNatIP()).thenReturn(sourceNatIp);
manager.updateVpc(cmd);
Assert.assertEquals(publicMtu, vpcVO.getPublicMtu());
}
@Test
public void verifySourceNatIp() {
String sourceNatIp = "1.2.3.4";
VpcVO vpcVO = Mockito.mock(VpcVO.class); //new VpcVO(1l, "vpc", null, 10l, 1l, 1l, "10.1.0.0/16", null, false, false, false, null, null, null, null);
Mockito.when(vpcVO.getId()).thenReturn(1l);
IPAddressVO requestedIp = Mockito.mock(IPAddressVO.class);//new IPAddressVO(new Ip(sourceNatIp), 1l, 1l, 1l, true);
Mockito.when(ipAddressDao.findByIp(sourceNatIp)).thenReturn(requestedIp);
Mockito.when(requestedIp.getVpcId()).thenReturn(1l);
Mockito.when(requestedIp.getVpcId()).thenReturn(1l);
Mockito.when(firewallDao.countRulesByIpId(1l)).thenReturn(0l);
Assert.assertNull(manager.validateSourceNatip(vpcVO, null));
Assert.assertEquals(requestedIp, manager.validateSourceNatip(vpcVO, sourceNatIp));
}
@Test

View File

@ -72,9 +72,6 @@ logger.addHandler(stream_handler)
class TestPublicIP(cloudstackTestCase):
def setUp(self):
self.apiclient = self.testClient.getApiClient()
@classmethod
def setUpClass(cls):
testClient = super(TestPublicIP, cls).getClsTestClient()
@ -85,6 +82,7 @@ class TestPublicIP(cloudstackTestCase):
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.services['mode'] = cls.zone.networktype
cls._cleanup = []
# Create Accounts & networks
cls.account = Account.create(
cls.apiclient,
@ -92,18 +90,21 @@ class TestPublicIP(cloudstackTestCase):
admin=True,
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
cls.user = Account.create(
cls.apiclient,
cls.services["account"],
domainid=cls.domain.id
)
cls._cleanup.append(cls.user)
cls.services["network"]["zoneid"] = cls.zone.id
cls.network_offering = NetworkOffering.create(
cls.apiclient,
cls.services["network_offering"],
)
cls._cleanup.append(cls.network_offering)
# Enable Network offering
cls.network_offering.update(cls.apiclient, state='Enabled')
@ -114,17 +115,20 @@ class TestPublicIP(cloudstackTestCase):
cls.account.name,
cls.account.domainid
)
cls._cleanup.append(cls.account_network)
cls.user_network = Network.create(
cls.apiclient,
cls.services["network"],
cls.user.name,
cls.user.domainid
)
cls._cleanup.append(cls.user_network)
cls.service_offering = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["tiny"],
)
cls._cleanup.append(cls.service_offering)
cls.hypervisor = testClient.getHypervisorInfo()
cls.template = get_test_template(
@ -146,6 +150,7 @@ class TestPublicIP(cloudstackTestCase):
networkids=cls.account_network.id,
serviceofferingid=cls.service_offering.id
)
cls._cleanup.append(cls.account_vm)
cls.user_vm = VirtualMachine.create(
cls.apiclient,
@ -156,6 +161,7 @@ class TestPublicIP(cloudstackTestCase):
networkids=cls.user_network.id,
serviceofferingid=cls.service_offering.id
)
cls._cleanup.append(cls.user_vm)
# Create Source NAT IP addresses
PublicIPAddress.create(
@ -170,25 +176,11 @@ class TestPublicIP(cloudstackTestCase):
cls.zone.id,
cls.user.domainid
)
cls._cleanup = [
cls.account_vm,
cls.user_vm,
cls.account_network,
cls.user_network,
cls.account,
cls.user,
cls.network_offering
]
return
@classmethod
def tearDownClass(cls):
try:
# Cleanup resources used
cleanup_resources(cls.apiclient, cls._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
super(TestPublicIP, cls).tearDownClass()
@attr(tags=["advanced", "advancedns", "smoke", "dvs"], required_hardware="false")
def test_public_ip_admin_account(self):

View File

@ -0,0 +1,274 @@
# Licensed to the Apache Software Foundation (ASF) under one
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
""" BVT tests for Network Life Cycle
"""
# Import Local Modules
from marvin.codes import (FAILED, STATIC_NAT_RULE, LB_RULE,
NAT_RULE, PASS)
from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.lib.base import (Account,
VPC,
VpcOffering,
ServiceOffering,
PublicIPAddress,
Network,
NetworkOffering)
from marvin.lib.common import (get_domain,
get_free_vlan,
get_zone,
get_template,
get_test_template,
list_publicIP)
from nose.plugins.attrib import attr
# Import System modules
import time
import logging
_multiprocess_shared_ = True
logger = logging.getLogger('TestSetSourceNatIp')
stream_handler = logging.StreamHandler()
logger.setLevel(logging.DEBUG)
logger.addHandler(stream_handler)
class TestSetSourceNatIp(cloudstackTestCase):
@classmethod
def setUpClass(cls):
testClient = super(TestSetSourceNatIp, cls).getClsTestClient()
cls.apiclient = testClient.getApiClient()
cls.services = testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
cls.services['mode'] = cls.zone.networktype
cls._cleanup = []
# Create Accounts & networks
cls.account = Account.create(
cls.apiclient,
cls.services["account"],
admin=True,
domainid=cls.domain.id
)
cls._cleanup.append(cls.account)
cls.services["network"]["zoneid"] = cls.zone.id
cls.vpc_offering = VpcOffering.create(
cls.apiclient,
cls.services["vpc_offering"],
)
cls._cleanup.append(cls.vpc_offering)
cls.vpc_offering.update(cls.apiclient, state='Enabled')
cls.services["vpc"]["vpcoffering"] = cls.vpc_offering.id
cls.network_offering = NetworkOffering.create(
cls.apiclient,
cls.services["network_offering"],
)
cls._cleanup.append(cls.network_offering)
# Enable Network offering
cls.network_offering.update(cls.apiclient, state='Enabled')
cls.services["network"]["networkoffering"] = cls.network_offering.id
cls.service_offering = ServiceOffering.create(
cls.apiclient,
cls.services["service_offerings"]["tiny"],
)
cls._cleanup.append(cls.service_offering)
cls.hypervisor = testClient.getHypervisorInfo()
cls.template = get_test_template(
cls.apiclient,
cls.zone.id,
cls.hypervisor
)
if cls.template == FAILED:
assert False, "get_test_template() failed to return template"
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
network = Network.create(
cls.apiclient,
cls.services["network"],
cls.account.name,
cls.account.domainid
)
cls._cleanup.append(network)
ip_address = PublicIPAddress.create(
cls.apiclient,
cls.account.name,
cls.zone.id,
cls.account.domainid
)
cls._cleanup.append(ip_address)
cls.ip_to_want = ip_address.ipaddress.ipaddress
cls.debug(f'==== my local ip: {cls.ip_to_want}')
ip_address.delete(cls.apiclient)
cls._cleanup.remove(ip_address)
network.delete(cls.apiclient)
cls._cleanup.remove(network)
return
@classmethod
def tearDownClass(cls):
super(TestSetSourceNatIp, cls).tearDownClass()
def setUp(self):
self.cleanup = []
def tearDown(self):
super(TestSetSourceNatIp, self).tearDown()
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
def test_01_create_network_with_specified_source_nat_ip_address(self):
"""
For creation of network witjh a specified address
"""
self.services["network"]["networkoffering"] = self.network_offering.id
network = Network.create(
self.apiclient,
self.services["network"],
self.account.name,
self.account.domainid,
sourcenatipaddress = self.ip_to_want
)
self.cleanup.append(network)
self.validate_source_nat(network=network, ip=self.ip_to_want)
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
def test_02_change_source_nat_ip_address_for_network(self):
"""
Test changing a networks source NAT IP address
"""
network = Network.create(
self.apiclient,
self.services["network"],
self.account.name,
self.account.domainid,
sourcenatipaddress = self.ip_to_want
)
self.cleanup.append(network)
second_ip = PublicIPAddress.create(
self.apiclient,
self.account.name,
self.zone.id,
self.account.domainid,
networkid=network.id
)
self.cleanup.append(second_ip)
self.debug(f'==== second ip: {second_ip.ipaddress.ipaddress}')
self.validate_source_nat(network=network, ip=self.ip_to_want)
network.update(self.apiclient, sourcenatipaddress=second_ip.ipaddress.ipaddress)
self.validate_source_nat(network=network, ip=second_ip.ipaddress.ipaddress)
return
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
def test_03_create_vpc_with_specified_source_nat_ip_address(self):
"""
Test for creation of a VPC with a specified address
"""
vpc = VPC.create(
self.apiclient,
self.services["vpc"],
self.vpc_offering.id,
self.zone.id,
sourcenatipaddress = self.ip_to_want
)
self.cleanup.append(vpc)
self.validate_source_nat(vpc=vpc, ip=self.ip_to_want)
@attr(tags=["advanced", "advancedns", "smoke"], required_hardware="false")
def test_04_change_source_nat_ip_address_for_vpc(self):
"""
Test changing a networks source NAT IP address
"""
vpc = VPC.create(
self.apiclient,
self.services["vpc"],
self.vpc_offering.id,
self.zone.id,
account=self.account.name,
domainid = self.account.domainid,
sourcenatipaddress = self.ip_to_want
)
self.cleanup.append(vpc)
second_ip = PublicIPAddress.create(
self.apiclient,
self.account.name,
self.zone.id,
self.account.domainid,
vpcid=vpc.id
)
self.debug(f'==== second ip: {second_ip.ipaddress.ipaddress}')
self.validate_source_nat(vpc=vpc, ip=self.ip_to_want)
vpc.update(self.apiclient, sourcenatipaddress=second_ip.ipaddress.ipaddress)
self.validate_source_nat(vpc=vpc, ip=second_ip.ipaddress.ipaddress)
return
def validate_source_nat(self, network=None, vpc=None, ip=None):
list_pub_ip_addr_resp = None
if network:
list_pub_ip_addr_resp = list_publicIP(
self.apiclient,
associatednetworkid=network.id,
listall=True,
issourcenat=True
)
elif vpc:
list_pub_ip_addr_resp = list_publicIP(
self.apiclient,
vpc=vpc.id,
listall=True,
issourcenat=True
)
self.assertEqual(
isinstance(list_pub_ip_addr_resp, list),
True,
"Check list response returns a valid list"
)
self.assertNotEqual(
len(list_pub_ip_addr_resp),
0,
"Check if new IP Address is associated"
)
self.debug(f'==== my result {list_pub_ip_addr_resp[0]}')
self.assertEqual(
list_pub_ip_addr_resp[0].ipaddress,
ip,
f"Check Correct IP Address is returned in the List, expected {ip} but got {list_pub_ip_addr_resp[0].ipaddress}"
)

View File

@ -3485,7 +3485,8 @@ class Network:
networkofferingid=None, projectid=None,
subdomainaccess=None, zoneid=None,
gateway=None, netmask=None, vpcid=None, aclid=None, vlan=None,
externalid=None, bypassvlanoverlapcheck=None, associatednetworkid=None, publicmtu=None, privatemtu=None):
externalid=None, bypassvlanoverlapcheck=None, associatednetworkid=None, publicmtu=None, privatemtu=None,
sourcenatipaddress=None):
"""Create Network for account"""
cmd = createNetwork.createNetworkCmd()
cmd.name = services["name"]
@ -3567,6 +3568,8 @@ class Network:
cmd.publicmtu = publicmtu
if privatemtu:
cmd.privatemtu = privatemtu
if sourcenatipaddress:
cmd.sourcenatipaddress = sourcenatipaddress
return Network(apiclient.createNetwork(cmd).__dict__)
def delete(self, apiclient):

View File

@ -1578,6 +1578,8 @@
"label.shared": "Κοινόχρηστο",
"label.sharedexecutable": "Κοινόχρηστο",
"label.sharedmountpoint": "Κοινόχρηστο μέσο",
"label.sharedrouterip": "Διεύθυνση IPv4 του δρομολογητή στο κοινόχρηστο δίκτυο ",
"label.sharedrouteripv6": "Διεύθυνση IPv6 του δρομολογητή στο κοινόχρηστο δίκτυο",
"label.sharewith": "Κοινή χρήση με",
"label.showing": "Προβολή",
"label.shrinkok": "Συρρίκνωση OK",

View File

@ -165,6 +165,7 @@
"label.action.router.health.checks": "Get health checks result",
"label.action.run.diagnostics": "Run diagnostics",
"label.action.secure.host": "Provision host security keys",
"label.action.set.as.source.nat.ip": "make source NAT",
"label.action.setup.2FA.user.auth": "Setup User Two Factor Authentication",
"label.action.start.instance": "Start instance",
"label.action.start.router": "Start router",
@ -915,6 +916,7 @@
"label.haenable": "HA enabled",
"label.haprovider": "HA provider",
"label.hardware": "Hardware",
"label.hasrules":"FW rules defined",
"label.hastate": "HA state",
"label.header.backup.schedule": "You can set up recurring backup schedules by selecting from the available options below and applying your policy preference.",
"label.header.volume.snapshot": "You can set up recurring snapshot schedules by selecting from the available options below and applying your policy preference.",
@ -1668,8 +1670,8 @@
"label.router.health.check.success": "Success",
"label.router.health.checks": "Health checks",
"label.routercount": "Total of virtual routers",
"label.routerip": "IPv4 address for the VR in this shared network.",
"label.routeripv6": "IPv6 address for the VR in this shared network.",
"label.routerip": "IPv4 address for the VR in this network.",
"label.routeripv6": "IPv6 address for the VR in this network.",
"label.resourcegroup": "Resource group",
"label.routing.policy": "Routing policy",
"label.routing.policy.terms": "Routing policy terms",
@ -1775,6 +1777,8 @@
"label.shared": "Shared",
"label.sharedexecutable": "Shared",
"label.sharedmountpoint": "SharedMountPoint",
"label.sharedrouterip": "IPv4 address for the VR in this shared network.",
"label.sharedrouteripv6": "IPv6 address for the VR in this shared network.",
"label.sharewith": "Share with",
"label.showing": "Showing",
"label.shrinkok": "Shrink OK",
@ -1803,6 +1807,7 @@
"label.sourceipaddress": "Source IP address",
"label.sourceipaddressnetworkid": "Network ID of source IP address",
"label.sourcenat": "Source NAT",
"label.sourcenatipaddress": "Source NAT IP address",
"label.sourcenatsupported": "Source NAT supported",
"label.sourcenattype": "Supported source NAT type",
"label.sourceport": "Source port",
@ -1854,6 +1859,7 @@
"label.startport": "Start port",
"label.startquota": "Quota value",
"label.state": "State",
"label.staticnat": "Static NAT",
"label.static.routes": "Static routes",
"label.status": "Status",
"label.step.1": "Step 1",
@ -2888,6 +2894,8 @@
"message.setup.physical.network.during.zone.creation.basic": "When adding a basic zone, you can set up one physical network, which corresponds to a NIC on the hypervisor. The network carries several types of traffic.<br/><br/>You may also <strong>add</strong> other traffic types onto the physical network.",
"message.shared.network.offering.warning": "Domain admins and regular users can only create shared networks from network offering with the setting specifyvlan=false. Please contact an administrator to create a network offering if this list is empty.",
"message.shutdown.triggered": "A shutdown has been triggered. CloudStack will not accept new jobs",
"message.sourcenatip.change.warning": "WARNING: Changing the sourcenat IP address of the network will cause connectivity downtime for the VMs with NICs in the network.",
"message.sourcenatip.change.inhibited": "Changing the sourcenat to this IP of the network to this address is inhibited as firewall rules are defined for it. This can include port forwarding or load balancing rules.\n - If this is an isolated network, please use updateNetwork/click the edit button.\n - If this is a VPC, first clear all other rules for this address.",
"message.specify.tag.key": "Please specify a tag key.",
"message.specify.tag.value": "Please specify a tag value.",
"message.step.2.continue": "Please select a service offering to continue.",

View File

@ -2046,6 +2046,8 @@
"label.shared": "共有",
"label.sharedexecutable": "共有",
"label.sharedmountpoint": "SharedMountPoint",
"label.sharedrouterip": "共有ネットワークのルーターのIPv4アドレス",
"label.sharedrouteripv6": "共有ネットワークのルーターのIPv6アドレス",
"label.sharewith": "共有",
"label.show.ingress.rule": "受信ルールの表示",
"label.showing": "表示中",

View File

@ -1373,6 +1373,8 @@
"label.shared": "shared",
"label.sharedexecutable": "\uacf5\uc720",
"label.sharedmountpoint": "\uacf5\uc720 \ub9c8\uc6b4\ud2b8 \ud3ec\uc778\ud2b8",
"label.sharedrouterip": "\uc11c\ube44\uc2a4\uc6a9 \ub124\ud2b8\uc6cc\ud06c\uc758 \ub77c\uc6b0\ud130\uc5d0 \ub300\ud55c IPv4 \uc8fc\uc18c",
"label.sharedrouteripv6": "\uc11c\ube44\uc2a4\uc6a9 \ub124\ud2b8\uc6cc\ud06c\uc758 \ub77c\uc6b0\ud130\uc5d0 \ub300\ud55c IPv6 \uc8fc\uc18c",
"label.sharewith": "\uacf5\uc720",
"label.showing": "\ubcf4\uae30",
"label.shrinkok": "\ubcc0\uacbd \uc644\ub8cc",

View File

@ -1468,6 +1468,8 @@
"label.shared": "Compatilhado",
"label.sharedexecutable": "Compatilhado",
"label.sharedmountpoint": "SharedMountPoint",
"label.sharedrouterip": "Endere\u00e7os IPv4 para o roteador dentro da rede compartilhada",
"label.sharedrouteripv6": "Endere\u00e7os IPv6 para o roteador dentro da rede compartilhada",
"label.sharewith": "Compartilhar com",
"label.showing": "Exibindo",
"label.shrinkok": "Encolhimento OK",

View File

@ -2333,6 +2333,8 @@
"label.shared": "\u5DF2\u5171\u4EAB",
"label.sharedexecutable": "\u5DF2\u5171\u4EAB",
"label.sharedmountpoint": "\u5171\u4EAB\u6302\u8F7D\u70B9",
"label.sharedrouterip": "\u5171\u4EAB\u7F51\u7EDC\u4E2D\u8DEF\u7531\u5668\u7684 IPv4 \u5730\u5740",
"label.sharedrouteripv6": "\u5171\u4EAB\u7F51\u7EDC\u4E2D\u8DEF\u7531\u5668\u7684 IPv6 \u5730\u5740",
"label.sharewith": "\u5206\u4EAB",
"label.show.ingress.rule": "\u663E\u793A\u5165\u53E3\u89C4\u5219",

View File

@ -228,7 +228,7 @@ export default {
icon: 'edit-outlined',
label: 'label.edit',
dataView: true,
args: ['name', 'displaytext', 'publicmtu']
args: ['name', 'displaytext', 'publicmtu', 'sourcenatipaddress']
},
{
api: 'restartVPC',

View File

@ -16,6 +16,7 @@
// under the License.
import {
AimOutlined,
ApartmentOutlined,
ApiOutlined,
AppstoreOutlined,
@ -170,6 +171,7 @@ import renderIcon from '@/utils/renderIcon'
export default {
install: (app) => {
app.component('AimOutlined', AimOutlined)
app.component('ApartmentOutlined', ApartmentOutlined)
app.component('ApiOutlined', ApiOutlined)
app.component('AppstoreOutlined', AppstoreOutlined)

View File

@ -291,6 +291,36 @@
</a-col>
</a-row>
</div>
<a-form-item v-if="selectedNetworkOfferingSupportsSourceNat" name="sourcenatipaddress" ref="sourcenatipaddress">
<template #label>
<tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress.description"/>
</template>
<a-input
v-model:value="form.sourcenatipaddress"
:placeholder="apiParams.sourcenatipaddress.description"/>
</a-form-item>
<a-form-item
ref="networkdomain"
name="networkdomain"
v-if="!isObjectEmpty(selectedNetworkOffering) && !selectedNetworkOffering.forvpc">
<template #label>
<tooltip-label :title="$t('label.networkdomain')" :tooltip="apiParams.networkdomain.description"/>
</template>
<a-input
v-model:value="form.networkdomain"
:placeholder="apiParams.networkdomain.description"/>
</a-form-item>
<a-form-item
ref="account"
name="account"
v-if="accountVisible">
<template #label>
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
</template>
<a-input
v-model:value="form.account"
:placeholder="apiParams.account.description"/>
</a-form-item>
<div :span="24" class="action-button">
<a-button
:loading="actionLoading"
@ -397,6 +427,14 @@ export default {
return dnsServices && dnsServices.length === 1
}
return false
},
selectedNetworkOfferingSupportsSourceNat () {
if (this.selectedNetworkOffering) {
const services = this.selectedNetworkOffering?.service || []
const sourcenatService = services.filter(service => service.name === 'SourceNat')
return sourcenatService && sourcenatService.length === 1
}
return false
}
},
methods: {
@ -596,7 +634,7 @@ export default {
displayText: values.displaytext,
networkOfferingId: this.selectedNetworkOffering.id
}
var usefulFields = ['gateway', 'netmask', 'startip', 'endip', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'externalid', 'vpcid', 'vlan', 'networkdomain']
var usefulFields = ['gateway', 'netmask', 'startip', 'startipv4', 'endip', 'endipv4', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'sourcenatipaddress', 'externalid', 'vpcid', 'vlan', 'networkdomain']
for (var field of usefulFields) {
if (this.isValidTextValueForKey(values, field)) {
params[field] = values[field]

View File

@ -155,6 +155,14 @@
</a-form-item>
</a-col>
</a-row>
<a-form-item v-if="selectedNetworkOfferingSupportsSourceNat" name="sourcenatipaddress" ref="sourcenatipaddress">
<template #label>
<tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress.description"/>
</template>
<a-input
v-model:value="form.sourcenatipaddress"
:placeholder="apiParams.sourcenatipaddress.description"/>
</a-form-item>
<a-form-item name="start" ref="start">
<template #label>
<tooltip-label :title="$t('label.start')" :tooltip="apiParams.start.description"/>
@ -212,6 +220,14 @@ export default {
return dnsServices && dnsServices.length === 1
}
return false
},
selectedNetworkOfferingSupportsSourceNat () {
if (this.selectedVpcOffering) {
const services = this.selectedVpcOffering?.service || []
const sourcenatService = services.filter(service => service.name === 'SourceNat')
return sourcenatService && sourcenatService.length === 1
}
return false
}
},
methods: {
@ -274,6 +290,10 @@ export default {
this.selectedVpcOffering = this.vpcOfferings[0] || {}
}).finally(() => {
this.loadingOffering = false
if (this.vpcOfferings.length > 0) {
this.form.vpcofferingid = 0
this.handleVpcOfferingChange(this.vpcOfferings[0].id)
}
})
},
handleVpcOfferingChange (value) {
@ -284,6 +304,7 @@ export default {
for (var offering of this.vpcOfferings) {
if (offering.id === value) {
this.selectedVpcOffering = offering
this.form.vpcofferingid = offering.id
return
}
}

View File

@ -67,9 +67,29 @@
:pagination="false" >
<template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'ipaddress'">
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }} </router-link>
<router-link v-if="record.forvirtualnetwork === true" :to="{ path: '/publicip/' + record.id }" >{{ text }}&nbsp;</router-link>
<div v-else>{{ text }}</div>
<a-tag v-if="record.issourcenat === true">source-nat</a-tag>
<template v-if="record.issourcenat === true">
<a-tag>{{ $t('label.sourcenat') }}</a-tag>
</template>
<template v-else-if="record.isstaticnat === true">
<a-tag>{{ $t('label.staticnat') }}</a-tag>
</template>
<template v-else-if="record.hasrules === false">
<tooltip-button
v-if="record.forvirtualnetwork === true"
:tooltip="$t('label.action.set.as.source.nat.ip')"
type="primary"
:danger="false"
icon="aim-outlined"
:disabled="!('updateNetwork' in $store.getters.apis)"
@onClick="showChangeSourceNat(record)"></tooltip-button>
</template>
<template v-else><!-- -if="record.hasrules === true" -->
<Tooltip placement="topLeft" :title="$t('message.sourcenatip.change.inhibited')" >
<a-tag>{{ $t('label.hasrules') }}</a-tag>
</Tooltip>
</template>
</template>
<template v-if="column.key === 'state'">
@ -150,6 +170,24 @@
</a-form>
</a-spin>
</a-modal>
<a-modal
v-if="changeSourceNat"
:title="$t('message.sourcenatip.change.warning')"
:visible="changeSourceNat"
:closable="true"
:footer="null"
@cancel="cancelChangeSourceNat"
centered
:disabled="!('updateNetwork' in $store.getters.apis)"
width="450px">
<template>
<a-alert :message="$t('message.sourcenatip.change.warning')" type="warning" />
</template>
<div :span="24" class="action-button">
<a-button @click="cancelChangeSourceNat">{{ $t('label.cancel') }}</a-button>
<a-button ref="submit" type="primary" @click="setSourceNatIp(record)">{{ $t('label.ok') }}</a-button>
</div>
</a-modal>
<bulk-action-view
v-if="showConfirmationAction || showGroupActionModal"
:showConfirmationAction="showConfirmationAction"
@ -167,6 +205,7 @@
@close-modal="closeModal" />
</div>
</template>
<script>
import { api } from '@/api'
import Status from '@/components/widgets/Status'
@ -242,7 +281,8 @@ export default {
showAcquireIp: false,
acquireLoading: false,
acquireIp: null,
listPublicIpAddress: []
listPublicIpAddress: [],
changeSourceNat: false
}
},
created () {
@ -315,6 +355,50 @@ export default {
return selection.indexOf(item.id) !== -1
}))
},
setSourceNatIp (ipaddress) {
if (this.settingsourcenat) return
if (this.$route.path.startsWith('/vpc')) {
this.updateVpc(ipaddress)
} else {
this.updateNetwork(ipaddress)
}
},
updateNetwork (ipaddress) {
const params = {}
params.sourcenatipaddress = this.sourceNatIp.ipaddress
params.id = this.resource.id
this.settingsourcenat = true
api('updateNetwork', params).then(response => {
this.fetchData()
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.updatenetworkresponse.errortext || error.response.data.errorresponse.errortext,
duration: 0
})
}).finally(() => {
this.settingsourcenat = false
this.cancelChangeSourceNat()
})
},
updateVpc (ipaddress) {
const params = {}
params.sourcenatipaddress = this.sourceNatIp.ipaddress
params.id = this.resource.id
this.settingsourcenat = true
api('updateVPC', params).then(response => {
this.fetchData()
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.updatevpcresponse.errortext || error.response.data.errorresponse.errortext,
duration: 0
})
}).finally(() => {
this.settingsourcenat = false
this.cancelChangeSourceNat()
})
},
resetSelection () {
this.setSelection([])
},
@ -482,6 +566,13 @@ export default {
},
closeModal () {
this.showConfirmationAction = false
},
showChangeSourceNat (ipaddress) {
this.changeSourceNat = true
this.sourceNatIp = ipaddress
},
cancelChangeSourceNat () {
this.changeSourceNat = false
}
}
}

View File

@ -80,6 +80,24 @@
</a-col>
</a-row>
</div>
<a-form-item name="sourcenatipaddress" ref="sourcenatipaddress">
<template #label>
<tooltip-label :title="$t('label.sourcenatipaddress')" :tooltip="apiParams.sourcenatipaddress.description"/>
</template>
<span v-if="sourcenatchange">
<a-alert type="warning">
<template #message>
<span v-html="$t('message.sourcenatip.change.warning')" />
</template>
</a-alert>
<br/>
</span>
<a-input
v-model:value="form.sourcenatipaddress"
:placeholder="apiParams.sourcenatipaddress.description"
v-focus="true"
@change="sourcenatchange = form.sourcenatipaddress.length > 0"/>
</a-form-item>
<a-form-item name="networkofferingid" ref="networkofferingid" v-if="isUpdatingIsolatedNetwork">
<template #label>
<tooltip-label :title="$t('label.networkofferingid')" :tooltip="apiParams.networkofferingid.description"/>
@ -238,7 +256,8 @@ export default {
minMTU: 68,
errorPrivateMtu: '',
errorPublicMtu: '',
setMTU: false
setMTU: false,
sourcenatchange: false
}
},
beforeCreate () {