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_metrics_api
smoke/test_migration smoke/test_migration
smoke/test_multipleips_per_nic smoke/test_multipleips_per_nic
smoke/test_nested_virtualization", smoke/test_nested_virtualization
smoke/test_set_sourcenat",
"smoke/test_network "smoke/test_network
smoke/test_network_acl smoke/test_network_acl
smoke/test_network_ipv6 smoke/test_network_ipv6

View File

@ -256,7 +256,7 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
public static class Capability { 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 SupportedProtocols = new Capability("SupportedProtocols");
public static final Capability SupportedLBAlgorithms = new Capability("SupportedLbAlgorithms"); 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.CreateVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd; 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.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.RestartVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientAddressCapacityException;
@ -37,7 +39,6 @@ import com.cloud.utils.Pair;
public interface VpcService { public interface VpcService {
public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException;
/** /**
* Persists VPC record in the database * Persists VPC record in the database
* *
@ -48,14 +49,25 @@ public interface VpcService {
* @param displayText * @param displayText
* @param cidr * @param cidr
* @param networkDomain TODO * @param networkDomain TODO
* @param ip4Dns1
* @param ip4Dns2
* @param displayVpc TODO * @param displayVpc TODO
* @return * @return
* @throws ResourceAllocationException TODO * @throws ResourceAllocationException TODO
*/ */
public Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain, 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) String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu)
throws ResourceAllocationException; 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 * Deletes a VPC
* *
@ -65,46 +77,46 @@ public interface VpcService {
* @throws ResourceUnavailableException * @throws ResourceUnavailableException
* @throws ConcurrentOperationException * @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 * Updates VPC with new name/displayText
* *
* @param vpcId * @param vpcId the ID of the Vpc to update
* @param vpcName * @param vpcName The new name to give the vpc
* @param displayText * @param displayText the new display text to use for describing the VPC
* @param customId TODO * @param customId A new custom (external) ID to associate this VPC with
* @param displayVpc TODO * @param displayVpc should this VPC be displayed on public lists
* @param mtu * @param mtu what maximal transfer unit to us in this VPCs networks
* @return * @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 * 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, 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); Map<String, String> tags, Long projectId, Boolean display);
@ -130,17 +142,17 @@ public interface VpcService {
*/ */
boolean shutdownVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException; 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 * Restarts the VPC. VPC gets shutdown and started as a part of it
* *
* @param id * @param networkId the network to restart
* @param cleanUp * @param cleanup throw away the existing VR and rebuild a new one?
* @param makeredundant * @param makeRedundant create two VRs for this network
* @return * @return success or not
* @throws InsufficientCapacityException * @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; 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. * Persists VPC private gateway in the Database.
* *
* * @return data object describing the private gateway
* @param vpcId TODO
* @param physicalNetworkId
* @param vlan
* @param ipAddress
* @param gateway
* @param netmask
* @param gatewayOwnerId
* @param networkOfferingId
* @param isSourceNat
* @param aclId
* @return
* @throws InsufficientCapacityException * @throws InsufficientCapacityException
* @throws ConcurrentOperationException * @throws ConcurrentOperationException
* @throws ResourceAllocationException * @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 * Applies VPC private gateway on the backend, so it becomes functional
@ -181,12 +182,12 @@ public interface VpcService {
* @throws ResourceUnavailableException * @throws ResourceUnavailableException
* @throws ConcurrentOperationException * @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 * Deletes VPC private gateway
* *
* @param id * @param gatewayId
* @return * @return
* @throws ResourceUnavailableException * @throws ResourceUnavailableException
* @throws ConcurrentOperationException * @throws ConcurrentOperationException
@ -199,7 +200,7 @@ public interface VpcService {
* @param listPrivateGatewaysCmd * @param listPrivateGatewaysCmd
* @return * @return
*/ */
public Pair<List<PrivateGateway>, Integer> listPrivateGateway(ListPrivateGatewaysCmd listPrivateGatewaysCmd); Pair<List<PrivateGateway>, Integer> listPrivateGateway(ListPrivateGatewaysCmd listPrivateGatewaysCmd);
/** /**
* Returns Static Route found by Id * Returns Static Route found by Id
@ -216,7 +217,7 @@ public interface VpcService {
* @return * @return
* @throws ResourceUnavailableException * @throws ResourceUnavailableException
*/ */
public boolean applyStaticRoutesForVpc(long vpcId) throws ResourceUnavailableException; boolean applyStaticRoutesForVpc(long vpcId) throws ResourceUnavailableException;
/** /**
* Deletes static route from the backend and the database * Deletes static route from the backend and the database
@ -225,7 +226,7 @@ public interface VpcService {
* @return TODO * @return TODO
* @throws ResourceUnavailableException * @throws ResourceUnavailableException
*/ */
public boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException; boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException;
/** /**
* Persists static route entry in the Database * Persists static route entry in the Database
@ -234,15 +235,15 @@ public interface VpcService {
* @param cidr * @param cidr
* @return * @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 * Lists static routes based on parameters passed to the call
* *
* @param listStaticRoutesCmd * @param cmd Command object with parameters for { @see ListStaticRoutesCmd }
* @return * @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 * Associates IP address from the Public network, to the VPC
@ -262,6 +263,5 @@ public interface VpcService {
* @param routeId * @param routeId
* @return * @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 AUTO_ENABLE_KVM_HOST = "autoenablekvmhost";
public static final String LIST_APIS = "listApis"; 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. * 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). * 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 @Override
public void execute() { public void execute() {
Pair<List<? extends IpAddress>, Integer> result = _mgr.searchForIPAddresses(this); Pair<List<? extends IpAddress>, Integer> result = _mgr.searchForIPAddresses(this);
ListResponse<IPAddressResponse> response = new ListResponse<IPAddressResponse>(); ListResponse<IPAddressResponse> response = new ListResponse<>();
List<IPAddressResponse> ipAddrResponses = new ArrayList<IPAddressResponse>(); List<IPAddressResponse> ipAddrResponses = new ArrayList<>();
for (IpAddress ipAddress : result.first()) { for (IpAddress ipAddress : result.first()) {
IPAddressResponse ipResponse = _responseGenerator.createIPAddressResponse(getResponseView(), ipAddress); IPAddressResponse ipResponse = _responseGenerator.createIPAddressResponse(getResponseView(), ipAddress);
ipResponse.setObjectName("publicipaddress"); ipResponse.setObjectName("publicipaddress");

View File

@ -16,7 +16,7 @@
// under the License. // under the License.
package org.apache.cloudstack.api.command.user.network; 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.log4j.Logger;
import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.RoleType;
@ -43,10 +43,10 @@ import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceAllocationException;
import com.cloud.network.Network; import com.cloud.network.Network;
import com.cloud.network.NetworkService;
import com.cloud.network.Network.GuestType; import com.cloud.network.Network.GuestType;
import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering;
import com.cloud.utils.net.NetUtils; 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}, @APICommand(name = "createNetwork", description = "Creates a network", responseObject = NetworkResponse.class, responseView = ResponseView.Restricted, entityType = {Network.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) 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") @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network", since = "4.18.0")
private String ip6Dns2; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -266,6 +274,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd {
return tungstenVirtualRouterUuid; return tungstenVirtualRouterUuid;
} }
public String getSourceNatIP() {
return sourceNatIP;
}
@Override @Override
public boolean isDisplay() { public boolean isDisplay() {
if(displayNetwork == null) 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") @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; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -181,6 +184,10 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd {
return ip6Dns2; return ip6Dns2;
} }
public String getSourceNatIP() {
return sourceNatIP;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////

View File

@ -16,7 +16,6 @@
// under the License. // under the License.
package org.apache.cloudstack.api.command.user.vpc; package org.apache.cloudstack.api.command.user.vpc;
import com.cloud.network.NetworkService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -41,6 +40,7 @@ import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.NetworkService;
import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.Vpc;
@APICommand(name = "createVPC", description = "Creates a VPC", responseObject = VpcResponse.class, responseView = ResponseView.Restricted, entityType = {Vpc.class}, @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") @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the VPC", since = "4.18.0")
private String ip6Dns2; 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 /////////////////////// // ///////////////// Accessors ///////////////////////
// /////////////////////////////////////////////////// // ///////////////////////////////////////////////////
@ -180,6 +186,15 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd {
return display; return display;
} }
public String getSourceNatIP() {
return sourceNatIP;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override @Override
public void create() throws ResourceAllocationException { public void create() throws ResourceAllocationException {
Vpc vpc = _vpcService.createVpc(this); Vpc vpc = _vpcService.createVpc(this);

View File

@ -142,9 +142,7 @@ public class ListVPCsCmd extends BaseListTaggedResourcesCmd implements UserCmd {
@Override @Override
public void execute() { public void execute() {
Pair<List<? extends Vpc>, Integer> vpcs = Pair<List<? extends Vpc>, Integer> vpcs =
_vpcService.listVpcs(getId(), getVpcName(), getDisplayText(), getSupportedServices(), getCidr(), getVpcOffId(), getState(), getAccountName(), getDomainId(), _vpcService.listVpcs(this);
getKeyword(), getStartIndex(), getPageSizeVal(), getZoneId(), isRecursive(), listAll(), getRestartRequired(), getTags(),
getProjectId(), getDisplay());
ListResponse<VpcResponse> response = new ListResponse<VpcResponse>(); ListResponse<VpcResponse> response = new ListResponse<VpcResponse>();
List<VpcResponse> vpcResponses = new ArrayList<VpcResponse>(); List<VpcResponse> vpcResponses = new ArrayList<VpcResponse>();
for (Vpc vpc : vpcs.first()) { 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 org.apache.cloudstack.api.response.VpcResponse;
import com.cloud.event.EventTypes; import com.cloud.event.EventTypes;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.Vpc;
import com.cloud.user.Account; 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") description = "MTU to be configured on the network VR's public facing interfaces", since = "4.18.0")
private Integer publicMtu; 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 /////////////////////// /////////////////// Accessors ///////////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -87,6 +95,10 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
return publicMtu; return publicMtu;
} }
public String getSourceNatIP() {
return sourceNatIP;
}
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////// API Implementation/////////////////// /////////////// API Implementation///////////////////
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
@ -107,7 +119,8 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
@Override @Override
public void execute() { public void execute() {
Vpc result = _vpcService.updateVpc(getId(), getVpcName(), getDisplayText(), getCustomId(), isDisplayVpc(), getPublicMtu()); try {
Vpc result = _vpcService.updateVpc(this);
if (result != null) { if (result != null) {
VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result); VpcResponse response = _responseGenerator.createVpcResponse(getResponseView(), result);
response.setResponseName(getCommandName()); response.setResponseName(getCommandName());
@ -115,6 +128,14 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd implements UserCmd {
} else { } else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VPC"); 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 @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") @Param(description="the name of the Network where ip belongs to")
private String networkName; private String networkName;
/* @SerializedName(ApiConstants.HAS_RULES)
@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") @Param(description="whether the ip address has Firewall/PortForwarding/LoadBalancing rules defined")
private IdentityProxy jobId = new IdentityProxy("async_job"); private boolean hasRules;
*/
public void setIpAddress(String ipAddress) { public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress; this.ipAddress = ipAddress;
@ -313,4 +312,8 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co
public void setNetworkName(String networkName) { public void setNetworkName(String networkName) {
this.networkName = 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; 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.Vpc;
import com.cloud.network.vpc.VpcService; import com.cloud.network.vpc.VpcService;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -72,7 +74,7 @@ public class UpdateVPCCmdTest extends TestCase {
Assert.assertEquals(cmd.getPublicMtu(), publicMtu); Assert.assertEquals(cmd.getPublicMtu(), publicMtu);
} }
public void testExecute() { public void testExecute() throws ResourceUnavailableException, InsufficientCapacityException {
ReflectionTestUtils.setField(cmd, "id", 1L); ReflectionTestUtils.setField(cmd, "id", 1L);
ReflectionTestUtils.setField(cmd, "vpcName", "updatedVpcName"); ReflectionTestUtils.setField(cmd, "vpcName", "updatedVpcName");
ReflectionTestUtils.setField(cmd, "displayText", "Updated VPC Name"); ReflectionTestUtils.setField(cmd, "displayText", "Updated VPC Name");
@ -85,10 +87,10 @@ public class UpdateVPCCmdTest extends TestCase {
responseGenerator = Mockito.mock(ResponseGenerator.class); responseGenerator = Mockito.mock(ResponseGenerator.class);
cmd._responseGenerator = responseGenerator; cmd._responseGenerator = responseGenerator;
Mockito.when(_vpcService.updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), 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.when(responseGenerator.createVpcResponse(ResponseObject.ResponseView.Full, vpc)).thenReturn(response);
Mockito.verify(_vpcService, Mockito.times(0)).updateVpc(Mockito.anyLong(), Mockito.anyString(), Mockito.anyString(), 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_ASSIGN_IPADDR_EVENT = "Message.AssignIpAddr.Event";
public static final String MESSAGE_RELEASE_IPADDR_EVENT = "Message.ReleaseIpAddr.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); 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 countRulesByIpId(long sourceIpId);
long countRulesByIpIdAndState(long sourceIpId, FirewallRule.State state); long countRulesByIpIdAndState(long sourceIpId, FirewallRule.State state);

View File

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

View File

@ -314,9 +314,9 @@ public class IPAddressDaoImpl extends GenericDaoBase<IPAddressVO, Long> implemen
} }
@Override @Override
public IPAddressVO findByVmIp(String vmIp) { public IPAddressVO findByIp(String ipAddress) {
SearchCriteria<IPAddressVO> sc = AllFieldsSearch.create(); SearchCriteria<IPAddressVO> sc = AllFieldsSearch.create();
sc.setParameters("associatedVmIp", vmIp); sc.setParameters("ipAddress", ipAddress);
return findOneBy(sc); 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.Condition;
import com.cloud.network.as.ConditionVO; import com.cloud.network.as.ConditionVO;
import com.cloud.network.as.Counter; import com.cloud.network.as.Counter;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.LoadBalancerVO;
@ -456,6 +457,8 @@ public class ApiResponseHelper implements ResponseGenerator {
UserVmJoinDao userVmJoinDao; UserVmJoinDao userVmJoinDao;
@Inject @Inject
NetworkServiceMapDao ntwkSrvcDao; NetworkServiceMapDao ntwkSrvcDao;
@Inject
FirewallRulesDao firewallRulesDao;
@Override @Override
public UserResponse createUserResponse(User user) { 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(), ipResponse.setHasAnnotation(annotationDao.hasAnnotations(ipAddr.getUuid(), AnnotationService.EntityType.PUBLIC_IP_ADDRESS.name(),
_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId()))); _accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));
ipResponse.setHasRules(firewallRulesDao.countRulesByIpId(ipAddr.getId()) > 0);
ipResponse.setObjectName("ipaddress"); ipResponse.setObjectName("ipaddress");
return ipResponse; return ipResponse;
} }

View File

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

View File

@ -2358,4 +2358,19 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
public static ConfigKey<Boolean> getSystemvmpublicipreservationmodestrictness() { public static ConfigKey<Boolean> getSystemvmpublicipreservationmodestrictness() {
return SystemVmPublicIpReservationModeStrictness; 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; Vpc copyOfVpc;
long copyOfVpcId; long copyOfVpcId;
try { try {
copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(), copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(),
vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(), vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(),
vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu()); 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.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger; 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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; 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 MIN_GRE_KEY = 0L;
private static final long MAX_GRE_KEY = 4294967295L; // 2^32 -1 private static final long MAX_GRE_KEY = 4294967295L; // 2^32 -1
private static final long MIN_VXLAN_VNI = 0L; 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. // 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. // 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 @Inject
DataCenterDao _dcDao = null; 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, void validateSharedNetworkRouterIPs(String gateway, String startIP, String endIP, String netmask, String routerIPv4, String routerIPv6, String startIPv6, String endIPv6, String ip6Cidr, NetworkOffering ntwkOff) {
String netmask, String startIpv6, String endIpv6, String ip6Cidr) { 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 (StringUtils.isNotBlank(routerIp)) {
if (startIp != null && endIp == null) { if (startIp != null && endIp == null) {
endIp = startIp; endIp = startIp;
} }
if (!NetUtils.isValidIp4(routerIp)) { isIPv4AddressValid(routerIp);
throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
}
if (StringUtils.isNoneBlank(startIp, endIp)) { if (StringUtils.isNoneBlank(startIp, endIp)) {
if (!NetUtils.isIpInRange(routerIp, startIp, endIp)) { if (!NetUtils.isIpInRange(routerIp, startIp, endIp)) {
throw new CloudRuntimeException("Router IPv4 IP provided is not within the specified range: " + 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)) { isIPv6AddressValid(routerIPv6);
String ipv6Range = startIpv6 + "-" + endIpv6; if (StringUtils.isNoneBlank(startIPv6, endIPv6)) {
if (!NetUtils.isIp6InRange(routerIpv6, ipv6Range)) { String ipv6Range = startIPv6 + "-" + endIPv6;
throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIpv6 + " - " + endIpv6); if (!NetUtils.isIp6InRange(routerIPv6, ipv6Range)) {
throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIPv6 + " - " + endIPv6);
} }
} else { } else {
if (!NetUtils.isIp6InNetwork(routerIpv6, ip6Cidr)) { if (!NetUtils.isIp6InNetwork(routerIPv6, cidrIPv6)) {
throw new CloudRuntimeException("Router IPv6 address provided is not with the network range"); 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 @Override
@DB @DB
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network") @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 endIP = cmd.getEndIp();
String netmask = cmd.getNetmask(); String netmask = cmd.getNetmask();
String networkDomain = cmd.getNetworkDomain(); String networkDomain = cmd.getNetworkDomain();
String vlanId = null;
boolean bypassVlanOverlapCheck = false; boolean adminCalledUs = cmd instanceof CreateNetworkCmdByAdmin;
boolean hideIpAddressUsage = false; String vlanId = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getVlan() : null;
String routerIp = null; boolean bypassVlanOverlapCheck = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
String routerIpv6 = null; boolean hideIpAddressUsage = adminCalledUs && ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
if (cmd instanceof CreateNetworkCmdByAdmin) { String routerIPv4 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIp() : null;
vlanId = ((CreateNetworkCmdByAdmin)cmd).getVlan(); String routerIPv6 = adminCalledUs ? ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6() : null;
bypassVlanOverlapCheck = ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
hideIpAddressUsage = ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
routerIp = ((CreateNetworkCmdByAdmin)cmd).getRouterIp();
routerIpv6 = ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6();
}
String name = cmd.getNetworkName(); String name = cmd.getNetworkName();
String displayText = cmd.getDisplayText(); String displayText = cmd.getDisplayText();
Account caller = CallContext.current().getCallingAccount(); Account caller = CallContext.current().getCallingAccount();
Long physicalNetworkId = cmd.getPhysicalNetworkId(); Long physicalNetworkId = cmd.getPhysicalNetworkId();
Long zoneId = cmd.getZoneId();
String aclTypeStr = cmd.getAclType();
Long domainId = cmd.getDomainId(); Long domainId = cmd.getDomainId();
boolean isDomainSpecific = false;
Boolean subdomainAccess = cmd.getSubdomainAccess(); Boolean subdomainAccess = cmd.getSubdomainAccess();
Long vpcId = cmd.getVpcId(); Long vpcId = cmd.getVpcId();
String startIPv6 = cmd.getStartIpv6(); String startIPv6 = cmd.getStartIpv6();
String endIPv6 = cmd.getEndIpv6(); String endIPv6 = cmd.getEndIpv6();
String ip6Gateway = cmd.getIp6Gateway(); String ip6Gateway = cmd.getIp6Gateway();
String ip6Cidr = cmd.getIp6Cidr(); String ip6Cidr = cmd.getIp6Cidr();
Boolean displayNetwork = cmd.getDisplayNetwork(); boolean displayNetwork = ! Boolean.FALSE.equals(cmd.getDisplayNetwork());
Long aclId = cmd.getAclId(); Long aclId = cmd.getAclId();
String isolatedPvlan = cmd.getIsolatedPvlan(); String isolatedPvlan = cmd.getIsolatedPvlan();
String externalId = cmd.getExternalId(); String externalId = cmd.getExternalId();
@ -1388,127 +1404,31 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
String ip6Dns1 = cmd.getIp6Dns1(); String ip6Dns1 = cmd.getIp6Dns1();
String ip6Dns2 = cmd.getIp6Dns2(); String ip6Dns2 = cmd.getIp6Dns2();
// Validate network offering // Validate network offering id
NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); NetworkOffering ntwkOff = getAndValidateNetworkOffering(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;
}
Account owner = null; Account owner = getOwningAccount(cmd, caller);
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;
}
// validate physical network and zone PhysicalNetwork pNtwk = getAndValidatePhysicalNetwork(physicalNetworkId);
// 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");
}
}
if (zoneId == null) { DataCenter zone = getAndValidateZone(cmd, pNtwk);
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");
}
_accountMgr.checkAccess(owner, ntwkOff, zone); _accountMgr.checkAccess(owner, ntwkOff, zone);
if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) { validateZoneAvailability(caller, zone);
// See DataCenterVO.java
PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled");
ex.addProxyObject(zone.getUuid(), "zoneId");
throw ex;
}
// Only domain and account ACL types are supported in Acton. ACLType aclType = getAclType(caller, cmd.getAclType(), ntwkOff);
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");
}
}
}
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"); throw new InvalidParameterValueException("Router IP can be specified only for Shared networks");
} }
if (ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.isProviderForNetworkOffering(Provider.VirtualRouter, networkOfferingId) 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"); 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 boolean isDomainSpecific = isDomainSpecificNetworkRequested(caller, domainId, subdomainAccess, ntwkOff, aclType);
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");
}
if (aclType == ACLType.Domain) { if (aclType == ACLType.Domain) {
owner = _accountDao.findById(Account.ACCOUNT_ID_SYSTEM); 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; Pair<String, String> ip6GatewayCidr = null;
if (zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getGuestType() == GuestType.Isolated) { if (zone.getNetworkType() == NetworkType.Advanced && ntwkOff.getGuestType() == GuestType.Isolated) {
ipv6 = _networkOfferingDao.isIpv6Supported(ntwkOff.getId()); ipv6 = _networkOfferingDao.isIpv6Supported(ntwkOff.getId());
@ -1682,7 +1603,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (cidr != null && providersConfiguredForExternalNetworking(ntwkProviders)) { if (cidr != null && providersConfiguredForExternalNetworking(ntwkProviders)) {
if (ntwkOff.getGuestType() == GuestType.Shared && (zone.getNetworkType() == NetworkType.Advanced) && isSharedNetworkOfferingWithServices(networkOfferingId)) { 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 // 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 { } else {
// if the guest network is for the VPC, if any External Provider are supported in VPC // 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. // 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 // Can add vlan range only to the network which allows it
if (createVlan && !ntwkOff.isSpecifyIpRanges()) { 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); mtuCheckForVpcNetwork(vpcId, interfaceMTUs, publicMtu, privateMtu);
Network associatedNetwork = null; Network associatedNetwork = null;
@ -1729,9 +1650,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
checkNetworkDns(ipv6, ntwkOff, vpcId, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); 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, 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) { if (hideIpAddressUsage) {
_networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false)); _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; 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) { protected void mtuCheckForVpcNetwork(Long vpcId, Pair<Integer, Integer> interfaceMTUs, Integer publicMtu, Integer privateMtu) {
if (vpcId != null && publicMtu != null) { if (vpcId != null && publicMtu != null) {
VpcVO vpc = _vpcDao.findById(vpcId); 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) { if (ntwkOff.getTrafficType() != TrafficType.Guest) {
throw new InvalidParameterValueException("This user can only create a Guest network"); 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, 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 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 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 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 { final Network associatedNetwork, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs) throws InsufficientCapacityException, ResourceAllocationException {
try { try {
@ -2373,7 +2504,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
} }
if (networkOfferingId != null) { if (networkOfferingId != null) {
sc.addAnd("networkOfferingId", SearchCriteria.Op.EQ, networkOfferingId); sc.addAnd(NETWORK_OFFERING_ID, SearchCriteria.Op.EQ, networkOfferingId);
} }
if (associatedNetworkId != null) { if (associatedNetworkId != null) {
@ -2796,6 +2927,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
_accountMgr.checkAccess(callerAccount, AccessType.OperateEntry, true, network); _accountMgr.checkAccess(callerAccount, AccessType.OperateEntry, true, network);
_accountMgr.checkAccess(_accountMgr.getActiveAccountById(network.getAccountId()), offering, _dcDao.findById(network.getDataCenterId())); _accountMgr.checkAccess(_accountMgr.getActiveAccountById(network.getAccountId()), offering, _dcDao.findById(network.getDataCenterId()));
restartNetwork |= checkAndUpdateRouterSourceNatIp(cmd, network);
if (cmd instanceof UpdateNetworkCmdByAdmin) { if (cmd instanceof UpdateNetworkCmdByAdmin) {
final Boolean hideIpAddressUsage = ((UpdateNetworkCmdByAdmin) cmd).getHideIpAddressUsage(); final Boolean hideIpAddressUsage = ((UpdateNetworkCmdByAdmin) cmd).getHideIpAddressUsage();
if (hideIpAddressUsage != null) { if (hideIpAddressUsage != null) {
@ -2844,13 +2977,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId);
if (networkOfferingId != null) { if (networkOfferingId != null) {
if (networkOffering == null || networkOffering.isSystemOnly()) { 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 // network offering should be in Enabled state
if (networkOffering.getState() != NetworkOffering.State.Enabled) { 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(), 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 //can't update from vpc to non-vpc network offering
boolean forVpcNew = _configMgr.isOfferingForVpc(networkOffering); 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, restartNetwork |= checkAndUpdateNetworkDns(network, networkOfferingChanged ? networkOffering : oldNtwkOff, ip4Dns1, ip4Dns2,
ip6Dns1, ip6Dns2)) { ip6Dns1, ip6Dns2);
restartNetwork = true;
}
final Map<String, String> newSvcProviders = networkOfferingChanged final Map<String, String> newSvcProviders = networkOfferingChanged
? _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId()) ? _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()) { if (servicesNotInNewOffering != null && !servicesNotInNewOffering.isEmpty()) {
_networkMgr.cleanupConfigForServicesInNetwork(servicesNotInNewOffering, network); _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()); 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()) { if (newNtwkOff == null || newNtwkOff.isSystemOnly()) {
InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering."); InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find network offering.");
if (newNtwkOff != null) { if (newNtwkOff != null) {
ex.addProxyObject(String.valueOf(newNtwkOff.getId()), "networkOfferingId"); ex.addProxyObject(String.valueOf(newNtwkOff.getId()), NETWORK_OFFERING_ID);
} }
throw ex; 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.ListPrivateGatewaysCmd;
import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd; 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.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.RestartVPCCmd;
import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao; 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.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; 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, final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff,
vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2);
vpc.setPublicMtu(publicMtu); vpc.setPublicMtu(publicMtu);
vpc.setDisplay(Boolean.TRUE.equals(displayVpc));
return createVpc(displayVpc, vpc); return createVpc(displayVpc, vpc);
} }
@ -1098,9 +1102,24 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
@Override @Override
@ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true) @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true)
public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException { 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.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(),
cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu()); 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 @DB
@ -1126,10 +1145,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
return Transaction.execute(new TransactionCallback<VpcVO>() { return Transaction.execute(new TransactionCallback<VpcVO>() {
@Override @Override
public VpcVO doInTransaction(final TransactionStatus status) { public VpcVO doInTransaction(final TransactionStatus status) {
if (displayVpc != null) {
vpc.setDisplay(displayVpc);
}
final VpcVO persistedVpc = vpcDao.persist(vpc, finalizeServicesAndProvidersForVpc(vpc.getZoneId(), vpc.getVpcOfferingId())); final VpcVO persistedVpc = vpcDao.persist(vpc, finalizeServicesAndProvidersForVpc(vpc.getZoneId(), vpc.getVpcOfferingId()));
_resourceLimitMgr.incrementResourceCount(vpc.getAccountId(), ResourceType.vpc); _resourceLimitMgr.incrementResourceCount(vpc.getAccountId(), ResourceType.vpc);
s_logger.debug("Created VPC " + persistedVpc); 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 @Override
@ActionEvent(eventType = EventTypes.EVENT_VPC_UPDATE, eventDescription = "updating vpc") @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); CallContext.current().setEventDetails(" Id: " + vpcId);
final Account caller = CallContext.current().getCallingAccount(); final Account caller = CallContext.current().getCallingAccount();
@ -1279,14 +1299,80 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
updateMtuOfVpcNetwork(vpcToUpdate, vpc, mtu); 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); 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); return vpcDao.findById(vpcId);
} else { } else {
s_logger.error(String.format("failed to update vpc %s/%s",vpc.getName(), vpc.getUuid()));
return null; 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) { protected Integer validateMtu(VpcVO vpcToUpdate, Integer mtu) {
Long zoneId = vpcToUpdate.getZoneId(); Long zoneId = vpcToUpdate.getZoneId();
if (mtu == null || NetworkService.AllowUsersToSpecifyVRMtu.valueIn(zoneId)) { if (mtu == null || NetworkService.AllowUsersToSpecifyVRMtu.valueIn(zoneId)) {
@ -1373,6 +1459,13 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
return success; 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 @Override
public Pair<List<? extends Vpc>, Integer> listVpcs(final Long id, final String vpcName, final String displayText, final List<String> supportedServicesStr, final String cidr, 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, 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(); final boolean listBySupportedServices = supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty();
if (listBySupportedServices) { if (listBySupportedServices) {
final List<VpcVO> supportedVpcs = new ArrayList<VpcVO>(); final List<Vpc> supportedVpcs = new ArrayList<>();
Service[] supportedServices = null; Service[] supportedServices = null;
if (listBySupportedServices) { if (listBySupportedServices) {
@ -1501,22 +1594,20 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
final List<? extends Vpc> wPagination = StringUtils.applyPagination(supportedVpcs, startIndex, pageSizeVal); final List<? extends Vpc> wPagination = StringUtils.applyPagination(supportedVpcs, startIndex, pageSizeVal);
if (wPagination != null) { if (wPagination != null) {
final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, supportedVpcs.size()); return new Pair<>(wPagination, supportedVpcs.size());
return listWPagination;
} }
return new Pair<List<? extends Vpc>, Integer>(supportedVpcs, supportedVpcs.size()); return new Pair<>(supportedVpcs, supportedVpcs.size());
} else { } else {
final List<? extends Vpc> wPagination = StringUtils.applyPagination(vpcs, startIndex, pageSizeVal); final List<? extends Vpc> wPagination = StringUtils.applyPagination(vpcs, startIndex, pageSizeVal);
if (wPagination != null) { if (wPagination != null) {
final Pair<List<? extends Vpc>, Integer> listWPagination = new Pair<List<? extends Vpc>, Integer>(wPagination, vpcs.size()); return new Pair<>(wPagination, vpcs.size());
return listWPagination;
} }
return new Pair<List<? extends Vpc>, Integer>(vpcs, vpcs.size()); return new Pair<>(vpcs, vpcs.size());
} }
} }
protected List<Service> getSupportedServices() { 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.Dhcp);
services.add(Network.Service.Dns); services.add(Network.Service.Dns);
services.add(Network.Service.UserData); services.add(Network.Service.UserData);

View File

@ -37,6 +37,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd; import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd;
@ -547,16 +548,68 @@ public class ConfigurationManagerTest {
assertThat(configurationMgr.searchForNetworkOfferings(cmd).second(), is(2)); 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 @Test
public void validateEmptyStaticNatServiceCapablitiesTest() { public void validateEmptyStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>(); Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap);
} }
@Test @Test
public void validateInvalidStaticNatServiceCapablitiesTest() { public void validateInvalidStaticNatServiceCapablitiesTest() {
Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<Capability, String>(); Map<Capability, String> staticNatServiceCapabilityMap = new HashMap<>();
staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "Frue and Talse"); staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "Frue and Talse");
boolean caught = false; boolean caught = false;
@ -569,9 +622,43 @@ public class ConfigurationManagerTest {
Assert.assertTrue("should not be accepted", caught); 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 @Test
public void validateTTStaticNatServiceCapablitiesTest() { 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.AssociatePublicIP, "true and Talse");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True"); staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True");
@ -580,7 +667,7 @@ public class ConfigurationManagerTest {
@Test @Test
public void validateFTStaticNatServiceCapablitiesTest() { 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.AssociatePublicIP, "false");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True"); staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True");
@ -589,7 +676,7 @@ public class ConfigurationManagerTest {
@Test @Test
public void validateTFStaticNatServiceCapablitiesTest() { 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.AssociatePublicIP, "true and Talse");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "false"); staticNatServiceCapabilityMap.put(Capability.ElasticIp, "false");
@ -608,7 +695,7 @@ public class ConfigurationManagerTest {
@Test @Test
public void validateFFStaticNatServiceCapablitiesTest() { 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.AssociatePublicIP, "false");
staticNatServiceCapabilityMap.put(Capability.ElasticIp, "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.doReturn;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Vector;
import com.cloud.user.Account; import com.cloud.user.Account;
import org.junit.Assert; import org.junit.Assert;
@ -230,4 +232,14 @@ public class IpAddressManagerTest {
return network; 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; package com.cloud.network;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -34,6 +36,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import com.cloud.exception.InsufficientAddressCapacityException;
import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; 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.org.Grouping;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.user.AccountManagerImpl;
import com.cloud.user.AccountService; import com.cloud.user.AccountService;
import com.cloud.user.AccountVO; import com.cloud.user.AccountVO;
import com.cloud.user.User; import com.cloud.user.User;
@ -122,8 +124,6 @@ public class NetworkServiceImplTest {
@Mock @Mock
NetworkOfferingServiceMapDao networkOfferingServiceMapDao; NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
@Mock @Mock
AccountManager accountMgr;
@Mock
EntityManager entityMgr; EntityManager entityMgr;
@Mock @Mock
NetworkService networkService; NetworkService networkService;
@ -162,8 +162,8 @@ public class NetworkServiceImplTest {
@Mock @Mock
ServiceOfferingVO serviceOfferingVoMock; ServiceOfferingVO serviceOfferingVoMock;
@InjectMocks @Mock
AccountManagerImpl accountManagerImpl; IpAddressManager ipAddressManager;
@Mock @Mock
ConfigKey<Integer> privateMtuKey; ConfigKey<Integer> privateMtuKey;
@Mock @Mock
@ -223,7 +223,7 @@ public class NetworkServiceImplTest {
service._networkOfferingDao = networkOfferingDao; service._networkOfferingDao = networkOfferingDao;
service._physicalNetworkDao = physicalNetworkDao; service._physicalNetworkDao = physicalNetworkDao;
service._dcDao = dcDao; service._dcDao = dcDao;
service._accountMgr = accountMgr; service._accountMgr = accountManager;
service._networkMgr = networkManager; service._networkMgr = networkManager;
service.alertManager = alertManager; service.alertManager = alertManager;
service._configMgr = configMgr; service._configMgr = configMgr;
@ -236,6 +236,7 @@ public class NetworkServiceImplTest {
service.routerDao = routerDao; service.routerDao = routerDao;
service.commandSetupHelper = commandSetupHelper; service.commandSetupHelper = commandSetupHelper;
service.networkHelper = networkHelper; service.networkHelper = networkHelper;
service._ipAddrMgr = ipAddressManager;
PowerMockito.mockStatic(CallContext.class); PowerMockito.mockStatic(CallContext.class);
CallContext callContextMock = PowerMockito.mock(CallContext.class); CallContext callContextMock = PowerMockito.mock(CallContext.class);
PowerMockito.when(CallContext.current()).thenReturn(callContextMock); PowerMockito.when(CallContext.current()).thenReturn(callContextMock);
@ -248,8 +249,8 @@ public class NetworkServiceImplTest {
Mockito.when(networkOfferingDao.findById(1L)).thenReturn(offering); Mockito.when(networkOfferingDao.findById(1L)).thenReturn(offering);
Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(phyNet); Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(phyNet);
Mockito.when(dcDao.findById(Mockito.anyLong())).thenReturn(dc); Mockito.when(dcDao.findById(Mockito.anyLong())).thenReturn(dc);
Mockito.lenient().doNothing().when(accountMgr).checkAccess(accountMock, networkOffering, dc); Mockito.lenient().doNothing().when(accountManager).checkAccess(accountMock, networkOffering, dc);
Mockito.when(accountMgr.isRootAdmin(accountMock.getId())).thenReturn(true); Mockito.when(accountManager.isRootAdmin(accountMock.getId())).thenReturn(true);
} }
@Test @Test
@ -352,6 +353,7 @@ public class NetworkServiceImplTest {
ReflectionTestUtils.setField(createNetworkCmd, "privateMtu", privateMtu); ReflectionTestUtils.setField(createNetworkCmd, "privateMtu", privateMtu);
ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null); ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
Mockito.when(offering.isSystemOnly()).thenReturn(false); Mockito.when(offering.isSystemOnly()).thenReturn(false);
Mockito.when(dc.getId()).thenReturn(1L);
Mockito.when(dc.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled); Mockito.when(dc.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
Map<String, String> networkProvidersMap = new HashMap<String, String>(); Map<String, String> networkProvidersMap = new HashMap<String, String>();
Mockito.when(networkManager.finalizeServicesAndProvidersForNetwork(ArgumentMatchers.any(NetworkOffering.class), anyLong())).thenReturn(networkProvidersMap); 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, "privateMtu", privateMtu);
ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null); ReflectionTestUtils.setField(createNetworkCmd, "physicalNetworkId", null);
ReflectionTestUtils.setField(createNetworkCmd, "vpcId", 1L); ReflectionTestUtils.setField(createNetworkCmd, "vpcId", 1L);
Mockito.when(dc.getId()).thenReturn(1L);
Mockito.when(configMgr.isOfferingForVpc(offering)).thenReturn(true); Mockito.when(configMgr.isOfferingForVpc(offering)).thenReturn(true);
Mockito.when(vpcDao.findById(anyLong())).thenReturn(vpc); 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() { public void testCreateNetworkDnsOfferingServiceFailure() {
registerCallContext(); registerCallContext();
CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
@ -577,7 +580,7 @@ public class NetworkServiceImplTest {
} }
} }
@Test(expected = InvalidParameterValueException.class) @Test(expected = CloudRuntimeException.class)
public void testCreateIp4NetworkIp6DnsFailure() { public void testCreateIp4NetworkIp6DnsFailure() {
registerCallContext(); registerCallContext();
CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
@ -770,4 +773,143 @@ public class NetworkServiceImplTest {
networkServiceImplMock.validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(1l); 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.alert.AlertManager;
import com.cloud.network.NetworkService; import com.cloud.network.NetworkService;
import com.cloud.network.dao.FirewallRulesDao;
import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.alert.AlertService; 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.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.junit.After; import org.junit.After;
@ -149,6 +151,8 @@ public class VpcManagerImplTest {
AlertManager alertManager; AlertManager alertManager;
@Mock @Mock
NetworkService networkServiceMock; NetworkService networkServiceMock;
@Mock
FirewallRulesDao firewallDao;
public static final long ACCOUNT_ID = 1; public static final long ACCOUNT_ID = 1;
private AccountVO account; private AccountVO account;
@ -200,6 +204,7 @@ public class VpcManagerImplTest {
manager._vpcOffDao = vpcOfferingDao; manager._vpcOffDao = vpcOfferingDao;
manager._dcDao = dataCenterDao; manager._dcDao = dataCenterDao;
manager._ntwkSvc = networkServiceMock; manager._ntwkSvc = networkServiceMock;
manager._firewallDao = firewallDao;
CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class)); CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class));
registerCallContext(); registerCallContext();
} }
@ -354,9 +359,10 @@ public class VpcManagerImplTest {
} }
@Test @Test
public void testUpdateVpcNetwork() throws ResourceUnavailableException { public void testUpdateVpcNetwork() throws ResourceUnavailableException, InsufficientCapacityException {
long vpcId = 1L; long vpcId = 1L;
Integer publicMtu = 1450; Integer publicMtu = 1450;
String sourceNatIp = "1.2.3.4";
Account accountMock = Mockito.mock(Account.class); Account accountMock = Mockito.mock(Account.class);
VpcVO vpcVO = new VpcVO(); VpcVO vpcVO = new VpcVO();
@ -398,9 +404,31 @@ public class VpcManagerImplTest {
Mockito.when(networkDao.update(anyLong(), any())).thenReturn(true); Mockito.when(networkDao.update(anyLong(), any())).thenReturn(true);
Mockito.when(vpcDao.update(vpcId, vpcVO)).thenReturn(true); Mockito.when(vpcDao.update(vpcId, vpcVO)).thenReturn(true);
manager.updateVpc(vpcId, null, null, null, true, publicMtu); UpdateVPCCmd cmd = Mockito.mock(UpdateVPCCmd.class);
Assert.assertEquals(publicMtu, vpcVO.getPublicMtu()); 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 @Test

View File

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

View File

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

View File

@ -165,6 +165,7 @@
"label.action.router.health.checks": "Get health checks result", "label.action.router.health.checks": "Get health checks result",
"label.action.run.diagnostics": "Run diagnostics", "label.action.run.diagnostics": "Run diagnostics",
"label.action.secure.host": "Provision host security keys", "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.setup.2FA.user.auth": "Setup User Two Factor Authentication",
"label.action.start.instance": "Start instance", "label.action.start.instance": "Start instance",
"label.action.start.router": "Start router", "label.action.start.router": "Start router",
@ -915,6 +916,7 @@
"label.haenable": "HA enabled", "label.haenable": "HA enabled",
"label.haprovider": "HA provider", "label.haprovider": "HA provider",
"label.hardware": "Hardware", "label.hardware": "Hardware",
"label.hasrules":"FW rules defined",
"label.hastate": "HA state", "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.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.", "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.check.success": "Success",
"label.router.health.checks": "Health checks", "label.router.health.checks": "Health checks",
"label.routercount": "Total of virtual routers", "label.routercount": "Total of virtual routers",
"label.routerip": "IPv4 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 shared network.", "label.routeripv6": "IPv6 address for the VR in this network.",
"label.resourcegroup": "Resource group", "label.resourcegroup": "Resource group",
"label.routing.policy": "Routing policy", "label.routing.policy": "Routing policy",
"label.routing.policy.terms": "Routing policy terms", "label.routing.policy.terms": "Routing policy terms",
@ -1775,6 +1777,8 @@
"label.shared": "Shared", "label.shared": "Shared",
"label.sharedexecutable": "Shared", "label.sharedexecutable": "Shared",
"label.sharedmountpoint": "SharedMountPoint", "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.sharewith": "Share with",
"label.showing": "Showing", "label.showing": "Showing",
"label.shrinkok": "Shrink OK", "label.shrinkok": "Shrink OK",
@ -1803,6 +1807,7 @@
"label.sourceipaddress": "Source IP address", "label.sourceipaddress": "Source IP address",
"label.sourceipaddressnetworkid": "Network ID of source IP address", "label.sourceipaddressnetworkid": "Network ID of source IP address",
"label.sourcenat": "Source NAT", "label.sourcenat": "Source NAT",
"label.sourcenatipaddress": "Source NAT IP address",
"label.sourcenatsupported": "Source NAT supported", "label.sourcenatsupported": "Source NAT supported",
"label.sourcenattype": "Supported source NAT type", "label.sourcenattype": "Supported source NAT type",
"label.sourceport": "Source port", "label.sourceport": "Source port",
@ -1854,6 +1859,7 @@
"label.startport": "Start port", "label.startport": "Start port",
"label.startquota": "Quota value", "label.startquota": "Quota value",
"label.state": "State", "label.state": "State",
"label.staticnat": "Static NAT",
"label.static.routes": "Static routes", "label.static.routes": "Static routes",
"label.status": "Status", "label.status": "Status",
"label.step.1": "Step 1", "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.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.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.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.key": "Please specify a tag key.",
"message.specify.tag.value": "Please specify a tag value.", "message.specify.tag.value": "Please specify a tag value.",
"message.step.2.continue": "Please select a service offering to continue.", "message.step.2.continue": "Please select a service offering to continue.",

View File

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

View File

@ -1373,6 +1373,8 @@
"label.shared": "shared", "label.shared": "shared",
"label.sharedexecutable": "\uacf5\uc720", "label.sharedexecutable": "\uacf5\uc720",
"label.sharedmountpoint": "\uacf5\uc720 \ub9c8\uc6b4\ud2b8 \ud3ec\uc778\ud2b8", "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.sharewith": "\uacf5\uc720",
"label.showing": "\ubcf4\uae30", "label.showing": "\ubcf4\uae30",
"label.shrinkok": "\ubcc0\uacbd \uc644\ub8cc", "label.shrinkok": "\ubcc0\uacbd \uc644\ub8cc",

View File

@ -1468,6 +1468,8 @@
"label.shared": "Compatilhado", "label.shared": "Compatilhado",
"label.sharedexecutable": "Compatilhado", "label.sharedexecutable": "Compatilhado",
"label.sharedmountpoint": "SharedMountPoint", "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.sharewith": "Compartilhar com",
"label.showing": "Exibindo", "label.showing": "Exibindo",
"label.shrinkok": "Encolhimento OK", "label.shrinkok": "Encolhimento OK",

View File

@ -2333,6 +2333,8 @@
"label.shared": "\u5DF2\u5171\u4EAB", "label.shared": "\u5DF2\u5171\u4EAB",
"label.sharedexecutable": "\u5DF2\u5171\u4EAB", "label.sharedexecutable": "\u5DF2\u5171\u4EAB",
"label.sharedmountpoint": "\u5171\u4EAB\u6302\u8F7D\u70B9", "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.sharewith": "\u5206\u4EAB",
"label.show.ingress.rule": "\u663E\u793A\u5165\u53E3\u89C4\u5219", "label.show.ingress.rule": "\u663E\u793A\u5165\u53E3\u89C4\u5219",

View File

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

View File

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

View File

@ -291,6 +291,36 @@
</a-col> </a-col>
</a-row> </a-row>
</div> </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"> <div :span="24" class="action-button">
<a-button <a-button
:loading="actionLoading" :loading="actionLoading"
@ -397,6 +427,14 @@ export default {
return dnsServices && dnsServices.length === 1 return dnsServices && dnsServices.length === 1
} }
return false 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: { methods: {
@ -596,7 +634,7 @@ export default {
displayText: values.displaytext, displayText: values.displaytext,
networkOfferingId: this.selectedNetworkOffering.id 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) { for (var field of usefulFields) {
if (this.isValidTextValueForKey(values, field)) { if (this.isValidTextValueForKey(values, field)) {
params[field] = values[field] params[field] = values[field]

View File

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

View File

@ -67,9 +67,29 @@
:pagination="false" > :pagination="false" >
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'ipaddress'"> <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> <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>
<template v-if="column.key === 'state'"> <template v-if="column.key === 'state'">
@ -150,6 +170,24 @@
</a-form> </a-form>
</a-spin> </a-spin>
</a-modal> </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 <bulk-action-view
v-if="showConfirmationAction || showGroupActionModal" v-if="showConfirmationAction || showGroupActionModal"
:showConfirmationAction="showConfirmationAction" :showConfirmationAction="showConfirmationAction"
@ -167,6 +205,7 @@
@close-modal="closeModal" /> @close-modal="closeModal" />
</div> </div>
</template> </template>
<script> <script>
import { api } from '@/api' import { api } from '@/api'
import Status from '@/components/widgets/Status' import Status from '@/components/widgets/Status'
@ -242,7 +281,8 @@ export default {
showAcquireIp: false, showAcquireIp: false,
acquireLoading: false, acquireLoading: false,
acquireIp: null, acquireIp: null,
listPublicIpAddress: [] listPublicIpAddress: [],
changeSourceNat: false
} }
}, },
created () { created () {
@ -315,6 +355,50 @@ export default {
return selection.indexOf(item.id) !== -1 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 () { resetSelection () {
this.setSelection([]) this.setSelection([])
}, },
@ -482,6 +566,13 @@ export default {
}, },
closeModal () { closeModal () {
this.showConfirmationAction = false this.showConfirmationAction = false
},
showChangeSourceNat (ipaddress) {
this.changeSourceNat = true
this.sourceNatIp = ipaddress
},
cancelChangeSourceNat () {
this.changeSourceNat = false
} }
} }
} }

View File

@ -80,6 +80,24 @@
</a-col> </a-col>
</a-row> </a-row>
</div> </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"> <a-form-item name="networkofferingid" ref="networkofferingid" v-if="isUpdatingIsolatedNetwork">
<template #label> <template #label>
<tooltip-label :title="$t('label.networkofferingid')" :tooltip="apiParams.networkofferingid.description"/> <tooltip-label :title="$t('label.networkofferingid')" :tooltip="apiParams.networkofferingid.description"/>
@ -238,7 +256,8 @@ export default {
minMTU: 68, minMTU: 68,
errorPrivateMtu: '', errorPrivateMtu: '',
errorPublicMtu: '', errorPublicMtu: '',
setMTU: false setMTU: false,
sourcenatchange: false
} }
}, },
beforeCreate () { beforeCreate () {