network: Specify IP for VR in shared networks (#4503)

This PR enables admins to specify IP for a VR in a shared network.
This commit is contained in:
Pearl Dsilva 2021-02-18 13:54:09 +05:30 committed by GitHub
parent b6fe9f99eb
commit aa01580381
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 452 additions and 34 deletions

View File

@ -452,4 +452,8 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
String getExternalId();
PVlanType getPvlanType();
String getRouterIp();
String getRouterIpv6();
}

View File

@ -319,4 +319,14 @@ public class NetworkProfile implements Network {
return null;
}
@Override
public String getRouterIp() {
return null;
}
@Override
public String getRouterIpv6() {
return null;
}
}

View File

@ -802,6 +802,8 @@ public class ApiConstants {
public static final String ROUTER_HEALTH_CHECKS = "healthchecks";
public static final String ROUTER_CHECK_NAME = "checkname";
public static final String ROUTER_CHECK_TYPE = "checktype";
public static final String ROUTER_IP = "routerip";
public static final String ROUTER_IPV6 = "routeripv6";
public static final String LAST_UPDATED = "lastupdated";
public static final String PERFORM_FRESH_CHECKS = "performfreshchecks";
public static final String CACHE_MODE = "cachemode";

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.admin.network;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
@ -42,6 +43,14 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm
@Parameter(name=ApiConstants.HIDE_IP_ADDRESS_USAGE, type=CommandType.BOOLEAN, description="when true ip address usage for the network will not be exported by the listUsageRecords API")
private Boolean hideIpAddressUsage;
@Parameter(name = ApiConstants.ROUTER_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to a router in a shared network", since = "4.16",
validations = {ApiArgValidator.NotNullOrEmpty})
private String routerIp;
@Parameter(name = ApiConstants.ROUTER_IPV6, type = CommandType.STRING, description = "IPV6 address to be assigned to a router in a shared network", since = "4.16",
validations = {ApiArgValidator.NotNullOrEmpty})
private String routerIpv6;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -63,4 +72,12 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd implements AdminCm
}
return false;
}
public String getRouterIp() {
return routerIp;
}
public String getRouterIpv6() {
return routerIpv6;
}
}

View File

@ -182,7 +182,7 @@ public interface NetworkOrchestrationService {
Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, boolean bypassVlanOverlapCheck, String networkDomain, Account owner,
Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr,
Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
UserDataServiceProvider getPasswordResetProvider(Network network);

View File

@ -623,6 +623,15 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
setStateMachine();
}
private void updateRouterIpInNetworkDetails(Long networkId, String routerIp, String routerIpv6) {
if (isNotBlank(routerIp)) {
networkDetailsDao.addDetail(networkId, ApiConstants.ROUTER_IP, routerIp, true);
}
if (isNotBlank(routerIpv6)) {
networkDetailsDao.addDetail(networkId, ApiConstants.ROUTER_IPV6, routerIpv6, true);
}
}
@Override
public List<? extends Network> setupNetwork(final Account owner, final NetworkOffering offering, final DeploymentPlan plan, final String name, final String displayText, final boolean isDefault)
throws ConcurrentOperationException {
@ -705,6 +714,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
networkDetailsDao.persist(detailVO);
}
updateRouterIpInNetworkDetails(networkPersisted.getId(), network.getRouterIp(), network.getRouterIpv6());
if (predefined instanceof NetworkVO && guru instanceof NetworkGuruAdditionalFunctions){
final NetworkGuruAdditionalFunctions functions = (NetworkGuruAdditionalFunctions) guru;
functions.finalizeNetworkDesign(networkPersisted.getId(), ((NetworkVO)predefined).getVlanIdAsUUID());
@ -2297,7 +2308,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
// create network for private gateway
return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId,
bypassVlanOverlapCheck, null, owner, null, pNtwk, pNtwk.getDataCenterId(), ACLType.Account, null,
vpcId, null, null, true, null, null, null, true);
vpcId, null, null, true, null, null, null, true, null, null);
}
@Override
@ -2305,18 +2316,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
public Network createGuestNetwork(final long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId,
boolean bypassVlanOverlapCheck, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk,
final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr,
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
// create Isolated/Shared/L2 network
return createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck,
networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr,
isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false);
isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6);
}
@DB
private Network createGuestNetwork(final long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId,
boolean bypassVlanOverlapCheck, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk,
final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr,
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, final Boolean isPrivateNetwork) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, final Boolean isPrivateNetwork, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
final DataCenterVO zone = _dcDao.findById(zoneId);
@ -2577,6 +2588,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
userNetwork.setExternalId(externalId);
}
if (isNotBlank(routerIp)) {
userNetwork.setRouterIp(routerIp);
}
if (isNotBlank(routerIpv6)) {
userNetwork.setRouterIpv6(routerIpv6);
}
if (vlanIdFinal != null) {
if (isolatedPvlan == null) {
URI uri = null;
@ -2616,7 +2635,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
final List<? extends Network> networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId,
isDisplayNetworkEnabled);
Network network = null;
if (networks == null || networks.isEmpty()) {
throw new CloudRuntimeException("Fail to create a network");

View File

@ -175,6 +175,12 @@ public class NetworkVO implements Network {
@Column(name = "external_id")
String externalId;
@Transient
String routerIp;
@Transient
String routerIpv6;
@Transient
transient String vlanIdAsUUID;
@ -672,4 +678,20 @@ public class NetworkVO implements Network {
public void setPvlanType(PVlanType pvlanType) {
this.pVlanType = pvlanType;
}
public String getRouterIp() {
return routerIp;
}
public void setRouterIp(String routerIp) {
this.routerIp = routerIp;
}
public String getRouterIpv6() {
return routerIpv6;
}
public void setRouterIpv6(String routerIpv6) {
this.routerIpv6 = routerIpv6;
}
}

View File

@ -797,7 +797,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne
try {
network = networkMgr.createGuestNetwork(networkOffering.getId(), clusterName + "-network", owner.getAccountName() + "-network",
null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ControlledEntity.ACLType.Account, null, null, null, null, true, null, null, null);
null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ControlledEntity.ACLType.Account, null, null, null, null, true, null, null, null, null, null);
} catch (ConcurrentOperationException | InsufficientCapacityException | ResourceAllocationException e) {
logAndThrow(Level.ERROR, String.format("Unable to create network for the Kubernetes cluster: %s", clusterName));
}

View File

@ -32,6 +32,7 @@ import javax.inject.Inject;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
@ -850,10 +851,16 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
errorMessage.append(", network id=" + guestNetworkId);
}
sc.setJoinParameters("vlan", "type", vlanUse);
String routerIpAddress = null;
if (network != null) {
NetworkDetailVO routerIpDetail = _networkDetailsDao.findDetail(network.getId(), ApiConstants.ROUTER_IP);
routerIpAddress = routerIpDetail != null ? routerIpDetail.getValue() : null;
}
if (requestedIp != null) {
sc.addAnd("address", SearchCriteria.Op.EQ, requestedIp);
errorMessage.append(": requested ip " + requestedIp + " is not available");
} else if (routerIpAddress != null) {
sc.addAnd("address", Op.NEQ, routerIpAddress);
}
boolean ascOrder = ! forSystemVms;
@ -1729,7 +1736,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId()
+ " as a part of createVlanIpRange process");
guestNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName()
+ "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null, null);
+ "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null, null, null, null);
if (guestNetwork == null) {
s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId);
throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT "
@ -2104,7 +2111,6 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
public void doInTransactionWithoutResult(TransactionStatus status) throws InsufficientAddressCapacityException {
//This method allocates direct ip for the Shared network in Advance zones
boolean ipv4 = false;
if (network.getGateway() != null) {
if (nic.getIPv4Address() == null) {
PublicIp ip = null;

View File

@ -1029,6 +1029,46 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
private void validateRouterIps(String routerIp, String routerIpv6, String startIp, String endIp, String gateway,
String netmask, String startIpv6, String endIpv6, String ip6Cidr) {
if (isNotBlank(routerIp)) {
if (startIp != null && endIp == null) {
endIp = startIp;
}
if (!NetUtils.isValidIp4(routerIp)) {
throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
}
if (isNotBlank(startIp) && isNotBlank(endIp)) {
if (!NetUtils.isIpInRange(routerIp, startIp, endIp)) {
throw new CloudRuntimeException("Router IPv4 IP provided is not within the specified range: " + startIp + " - " + endIp);
}
} else {
String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
if (!NetUtils.isIpWithInCidrRange(routerIp, cidr)) {
throw new CloudRuntimeException("Router IP provided in not within the network range");
}
}
}
if (isNotBlank(routerIpv6)) {
if (startIpv6 != null && endIpv6 == null) {
endIpv6 = startIpv6;
}
if (!NetUtils.isValidIp6(routerIpv6)) {
throw new CloudRuntimeException("Router IPv6 address provided is of incorrect format");
}
if (isNotBlank(startIpv6) && isNotBlank(endIpv6)) {
String ipv6Range = startIpv6 + "-" + endIpv6;
if (!NetUtils.isIp6InRange(routerIpv6, ipv6Range)) {
throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIpv6 + " - " + endIpv6);
}
} else {
if (!NetUtils.isIp6InNetwork(routerIpv6, ip6Cidr)) {
throw new CloudRuntimeException("Router IPv6 address provided is not with the network range");
}
}
}
}
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network")
@ -1042,10 +1082,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
String vlanId = null;
boolean bypassVlanOverlapCheck = false;
boolean hideIpAddressUsage = false;
String routerIp = null;
String routerIpv6 = null;
if (cmd instanceof CreateNetworkCmdByAdmin) {
vlanId = ((CreateNetworkCmdByAdmin)cmd).getVlan();
bypassVlanOverlapCheck = ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
hideIpAddressUsage = ((CreateNetworkCmdByAdmin)cmd).getHideIpAddressUsage();
routerIp = ((CreateNetworkCmdByAdmin)cmd).getRouterIp();
routerIpv6 = ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6();
}
String name = cmd.getNetworkName();
@ -1150,6 +1194,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
throw new InvalidParameterValueException("Only Admins can create network with guest type " + GuestType.Shared);
}
if (ntwkOff.getGuestType() != GuestType.Shared && (isNotBlank(routerIp) || isNotBlank(routerIpv6))) {
throw new InvalidParameterValueException("Router IP can be specified only for Shared networks");
}
if (ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.isProviderForNetworkOffering(Provider.VirtualRouter, networkOfferingId)
&& (isNotBlank(routerIp) || isNotBlank(routerIpv6))) {
throw new InvalidParameterValueException("Virtual Router is not a supported provider for the Shared network, hence router ip should not be provided");
}
// Check if the network is domain specific
if (aclType == ACLType.Domain) {
// only Admin can create domain with aclType=Domain
@ -1279,6 +1332,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
validateRouterIps(routerIp, routerIpv6, startIP, endIP, gateway, netmask, startIPv6, endIPv6, ip6Cidr);
if (isNotBlank(isolatedPvlan) && (zone.getNetworkType() != NetworkType.Advanced || ntwkOff.getGuestType() == GuestType.Isolated)) {
throw new InvalidParameterValueException("Can only support create Private VLAN network with advanced shared or L2 network!");
}
@ -1365,7 +1420,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zoneId,
domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId, privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan,
externalId);
externalId, routerIp, routerIpv6);
if (hideIpAddressUsage) {
_networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false));
@ -1445,7 +1500,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
final Boolean bypassVlanOverlapCheck, final String name, final String displayText, final Account caller, final Long physicalNetworkId, final Long zoneId, final Long domainId,
final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway, final String ip6Cidr,
final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal,
final String cidr, final boolean createVlan, final String externalId) throws InsufficientCapacityException, ResourceAllocationException {
final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6) throws InsufficientCapacityException, ResourceAllocationException {
try {
Network network = Transaction.execute(new TransactionCallbackWithException<Network, Exception>() {
@Override
@ -1500,7 +1555,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, sharedDomainId, pNtwk,
zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId);
zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId, routerIp, routerIpv6);
}
if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) {

View File

@ -205,6 +205,13 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
if (userSpecified.getPvlanType() != null) {
config.setPvlanType(userSpecified.getPvlanType());
}
if (userSpecified.getRouterIp() != null) {
config.setRouterIp(userSpecified.getRouterIp());
}
if (userSpecified.getRouterIpv6() != null) {
config.setRouterIpv6(userSpecified.getRouterIpv6());
}
}
boolean isSecurityGroupEnabled = _networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Service.SecurityGroup);

View File

@ -27,6 +27,7 @@ import java.util.Map;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.log4j.Logger;
import org.cloud.network.router.deployment.RouterDeploymentDefinition;
@ -71,6 +72,8 @@ import com.cloud.network.Networks.IsolationType;
import com.cloud.network.addr.PublicIp;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkDetailVO;
import com.cloud.network.dao.NetworkDetailsDao;
import com.cloud.network.dao.UserIpv6AddressDao;
import com.cloud.network.lb.LoadBalancingRule;
import com.cloud.network.router.VirtualRouter.RedundantState;
@ -156,6 +159,8 @@ public class NetworkHelperImpl implements NetworkHelper {
ConfigurationDao _configDao;
@Inject
VpcVirtualNetworkApplianceManager _vpcRouterMgr;
@Inject
NetworkDetailsDao networkDetailsDao;
protected final Map<HypervisorType, ConfigKey<String>> hypervisorsMap = new HashMap<>();
@ -717,6 +722,11 @@ public class NetworkHelperImpl implements NetworkHelper {
s_logger.debug("Requesting ipv4 address " + placeholder.getIPv4Address() + " stored in placeholder nic for the network "
+ guestNetwork);
defaultNetworkStartIp = placeholder.getIPv4Address();
} else {
NetworkDetailVO routerIpDetail = networkDetailsDao.findDetail(guestNetwork.getId(), ApiConstants.ROUTER_IP);
String routerIp = routerIpDetail != null ? routerIpDetail.getValue() : null;
if (routerIp != null) {
defaultNetworkStartIp = routerIp;
} else {
final String startIp = _networkModel.getStartIpAddress(guestNetwork.getId());
if (startIp != null
@ -728,12 +738,18 @@ public class NetworkHelperImpl implements NetworkHelper {
}
}
}
}
if (guestNetwork.getIp6Cidr() != null) {
if (placeholder != null && placeholder.getIPv6Address() != null) {
s_logger.debug("Requesting ipv6 address " + placeholder.getIPv6Address() + " stored in placeholder nic for the network "
+ guestNetwork);
defaultNetworkStartIpv6 = placeholder.getIPv6Address();
} else {
NetworkDetailVO routerIpDetail = networkDetailsDao.findDetail(guestNetwork.getId(), ApiConstants.ROUTER_IPV6);
String routerIpv6 = routerIpDetail != null ? routerIpDetail.getValue() : null;
if (routerIpv6 != null) {
defaultNetworkStartIpv6 = routerIpv6;
} else {
final String startIpv6 = _networkModel.getStartIpv6Address(guestNetwork.getId());
if (startIpv6 != null && _ipv6Dao.findByNetworkIdAndIp(guestNetwork.getId(), startIpv6) == null) {
@ -744,6 +760,7 @@ public class NetworkHelperImpl implements NetworkHelper {
}
}
}
}
} else if (placeholder != null) {
// Remove placeholder nic if router has public network
_nicDao.remove(placeholder.getId());

View File

@ -2596,7 +2596,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
// 2) Create network
final Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, false, networkDomain, owner, domainId, pNtwk, zoneId, aclType,
subdomainAccess, vpcId, null, null, isDisplayNetworkEnabled, null, null, externalId);
subdomainAccess, vpcId, null, null, isDisplayNetworkEnabled, null, null, externalId, null, null);
if (guestNetwork != null) {
guestNetwork.setNetworkACLId(aclId);

View File

@ -3488,7 +3488,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process");
Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network",
null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null, null,
null);
null, null, null);
if (newNetwork != null) {
defaultNetwork = _networkDao.findById(newNetwork.getId());
}
@ -6775,7 +6775,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network",
newAccount.getAccountName() + "-network", null, null, null, false, null, newAccount,
null, physicalNetwork, zone.getId(), ACLType.Account, null, null,
null, null, true, null, null, null);
null, null, true, null, null, null, null, null);
// if the network offering has persistent set to true, implement the network
if (requiredOfferings.get(0).isPersistent()) {
DeployDestination dest = new DeployDestination(zone, null, null, null);

View File

@ -123,7 +123,7 @@ public class CreatePrivateNetworkTest {
ACLType.Account, false, 1L, false);
when(networkService._networkMgr.createGuestNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), nullable(String.class), nullable(Boolean.class), nullable(String.class),
eq(account), nullable(Long.class), eq(physicalNetwork), eq(physicalNetwork.getDataCenterId()), eq(ACLType.Account), nullable(Boolean.class), eq(1L), nullable(String.class), nullable(String.class),
nullable(Boolean.class), nullable(String.class), nullable(Network.PVlanType.class), nullable(String.class))).thenReturn(net);
nullable(Boolean.class), nullable(String.class), nullable(Network.PVlanType.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenReturn(net);
when(
networkService._networkMgr.createPrivateNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), anyString(), anyBoolean(), eq(account), eq(physicalNetwork), eq(1L))).thenReturn(net);

View File

@ -646,7 +646,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
@Override
public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, boolean bypassVlanOverlapCheck, String networkDomain,
Account owner, Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String gatewayv6,
String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId) throws ConcurrentOperationException, InsufficientCapacityException,
String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException,
ResourceAllocationException {
// TODO Auto-generated method stub
return null;

View File

@ -23,7 +23,7 @@ from marvin.cloudstackTestCase import cloudstackTestCase
from marvin.cloudstackException import CloudstackAPIException
from marvin.cloudstackAPI import rebootRouter
from marvin.sshClient import SshClient
from marvin.lib.utils import cleanup_resources, get_process_status
from marvin.lib.utils import cleanup_resources, get_process_status, get_host_credentials
from marvin.lib.base import (Account,
VirtualMachine,
ServiceOffering,
@ -38,7 +38,9 @@ from marvin.lib.base import (Account,
NIC,
Cluster)
from marvin.lib.common import (get_domain,
get_free_vlan,
get_zone,
get_template,
get_test_template,
list_hosts,
list_publicIP,
@ -54,6 +56,7 @@ from ddt import ddt, data
# Import System modules
import time
import logging
import random
_multiprocess_shared_ = True
@ -1838,3 +1841,195 @@ class TestPrivateVlansL2Networks(cloudstackTestCase):
self.fail("Failing test. Error: %s" % e)
return
class TestSharedNetwork(cloudstackTestCase):
@classmethod
def setUpClass(cls):
cls.testClient = super(TestSharedNetwork, cls).getClsTestClient()
cls.apiclient = cls.testClient.getApiClient()
cls.services = cls.testClient.getParsedTestDataConfig()
# Get Zone, Domain and templates
cls.domain = get_domain(cls.apiclient)
cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests())
cls.template = get_template(cls.apiclient, cls.zone.id,
cls.services["ostype"])
cls.services["virtual_machine"]["zoneid"] = cls.zone.id
cls.services["virtual_machine"]["template"] = cls.template.id
# Create Network Offering
cls.services["shared_network_offering"]["specifyVlan"] = "True"
cls.services["shared_network_offering"]["specifyIpRanges"] = "True"
cls.shared_network_offering = NetworkOffering.create(cls.apiclient, cls.services["shared_network_offering"],
conservemode=False)
# Update network offering state from disabled to enabled.
NetworkOffering.update(cls.shared_network_offering, cls.apiclient, state="enabled")
cls.service_offering = ServiceOffering.create(cls.apiclient, cls.services["service_offering"])
physical_network, vlan = get_free_vlan(cls.apiclient, cls.zone.id)
# create network using the shared network offering created
cls.services["shared_network"]["acltype"] = "domain"
cls.services["shared_network"]["vlan"] = vlan
cls.services["shared_network"]["networkofferingid"] = cls.shared_network_offering.id
cls.services["shared_network"]["physicalnetworkid"] = physical_network.id
cls.setSharedNetworkParams("shared_network")
cls.shared_network = Network.create(cls.apiclient,
cls.services["shared_network"],
networkofferingid=cls.shared_network_offering.id,
zoneid=cls.zone.id)
cls._cleanup = [
cls.service_offering,
cls.shared_network_offering
]
return
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
@classmethod
def tearDownClass(cls):
try:
# Cleanup resources used
cleanup_resources(cls.apiclient, cls._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
def tearDown(self):
cleanup_resources(self.apiclient, self.cleanup)
return
@classmethod
def setSharedNetworkParams(cls, network, range=20):
# @range: range decides the endip. Pass the range as "x" if you want the difference between the startip
# and endip as "x"
# Set the subnet number of shared networks randomly prior to execution
# of each test case to avoid overlapping of ip addresses
shared_network_subnet_number = random.randrange(1, 254)
cls.services[network]["routerip"] = "172.16." + str(shared_network_subnet_number) + "." + str(15)
cls.services[network]["gateway"] = "172.16." + str(shared_network_subnet_number) + ".1"
cls.services[network]["startip"] = "172.16." + str(shared_network_subnet_number) + ".2"
cls.services[network]["endip"] = "172.16." + str(shared_network_subnet_number) + "." + str(range + 1)
cls.services[network]["netmask"] = "255.255.255.0"
logger.debug("Executing command '%s'" % cls.services[network])
def get_router_host(self, router):
self.assertEqual(
router.state,
'Running',
"Check list router response for router state"
)
hosts = list_hosts(
self.apiclient,
id=router.hostid)
self.assertEqual(
isinstance(hosts, list),
True,
"Check for list hosts response return valid data")
host = hosts[0]
if host.hypervisor.lower() in ("vmware", "hyperv"):
host.ipaddress = self.apiclient.connection.mgtSvr
host.user = self.apiclient.connection.user
host.password = self.apiclient.connection.passwd
host.port = 22
else:
host.user, host.password = get_host_credentials(self.config, host.ipaddress)
host.port = 22
return host
def verify_ip_address_in_router(self, router, host, ipaddress, device, isExist=True):
command = 'ip addr show %s |grep "inet "|cut -d " " -f6 |cut -d "/" -f1 |grep -w %s' % (device, ipaddress)
logger.debug("Executing command '%s'" % command)
result = get_process_status(
host.ipaddress,
host.port,
host.user,
host.password,
router.linklocalip,
command,
host.hypervisor.lower())
self.assertEqual(len(result) > 0 and result[0] == ipaddress, isExist, "ip %s verification failed" % ipaddress)
@attr(tags=["advanced", "shared"])
def test_01_deployVMInSharedNetwork(self):
try:
self.virtual_machine = VirtualMachine.create(self.apiclient, self.services["virtual_machine"],
networkids=[self.shared_network.id],
serviceofferingid=self.service_offering.id
)
except Exception as e:
self.fail("Exception while deploying virtual machine: %s" % e)
routerIp = self.services["shared_network"]["routerip"]
nic_ip_address = self.dbclient.execute(
"select ip4_address from nics where strategy='Placeholder' and ip4_address = '%s';" % routerIp);
self.assertNotEqual(
len(nic_ip_address),
0,
"Placeholder ip for the VR in shared network isn't the same as what was passed"
)
routers = Router.list(
self.apiclient,
networkid=self.shared_network.id,
listall=True
)
for router in routers:
host = self.get_router_host(router)
self.verify_ip_address_in_router(router, host, routerIp, "eth0", True)
# expunge VM
VirtualMachine.delete(self.virtual_machine, self.apiclient, expunge=True)
@attr(tags=["advanced", "shared"])
def test_02_verifyRouterIpAfterNetworkRestart(self):
routerIp = self.services["shared_network"]["routerip"]
self.debug("restarting network with cleanup")
try:
self.shared_network.restart(self.apiclient, cleanup=True)
except Exception as e:
self.fail("Failed to cleanup network - %s" % e)
self.debug("Listing routers for network: %s" % self.shared_network.name)
routers = Router.list(
self.apiclient,
networkid=self.shared_network.id,
listall=True
)
self.assertEqual(
len(routers),
1,
"Router for the shared network wasn't found)"
)
for router in routers:
host = self.get_router_host(router)
self.verify_ip_address_in_router(router, host, routerIp, "eth0", True)
@attr(tags=["advanced", "shared"])
def test_03_destroySharedNetwork(self):
routerIp = self.services["shared_network"]["routerip"]
try:
self.shared_network.delete(self.apiclient)
except Exception as e:
self.fail("Failed to destroy the shared network")
self.debug("Fetch the placeholder record for the router")
nic_ip_address = self.dbclient.execute(
"select ip4_address from nics where strategy='Placeholder' and ip4_address = '%s' and removed is NOT NULL;" % routerIp);
self.assertNotEqual(
len(nic_ip_address),
0,
"Failed to find the placeholder IP"
)

View File

@ -3158,6 +3158,18 @@ class Network:
cmd.isolatedpvlan = services["isolatedpvlan"]
if "isolatedpvlantype" in services:
cmd.isolatedpvlantype = services["isolatedpvlantype"]
if "routerip" in services:
cmd.routerip = services["routerip"]
if "ip6gateway" in services:
cmd.ip6gateway = services["ip6gateway"]
if "ip6cidr" in services:
cmd.ip6cidr = services["ip6cidr"]
if "startipv6" in services:
cmd.startipv6 = services["startipv6"]
if "endipv6" in services:
cmd.endipv6 = services["endipv6"]
if "routeripv6" in services:
cmd.routeripv6 = services["routeripv6"]
if accountid:
cmd.account = accountid

View File

@ -1815,6 +1815,8 @@
"label.root.disk.size": "Root disk size (GB)",
"label.rootdiskcontrollertype": "Root disk controller",
"label.rootdiskcontrollertypekvm": "Root disk controller",
"label.routerip": "IPv4 address for Router in Shared Network",
"label.routeripv6": "IPv6 address for Router in Shared Network",
"label.router.health.check.last.updated": "Last updated",
"label.router.health.check.name": "Check name",
"label.router.health.check.success": "Success",

View File

@ -332,6 +332,17 @@
v-decorator="['endipv4', {}]"
:placeholder="this.$t('label.endipv4')"/>
</a-form-item>
<a-form-item v-if="isVirtualRouterForAtLeastOneService">
<span slot="label">
{{ $t('label.routerip') }}
<a-tooltip :title="apiParams.routerip.description" v-if="'routerip' in apiParams">
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
</a-tooltip>
</span>
<a-input
v-decorator="['routerip', {}]"
:placeholder="this.$t('label.routerip')"/>
</a-form-item>
<a-form-item>
<span slot="label">
{{ $t('label.ip6gateway') }}
@ -376,6 +387,17 @@
v-decorator="['endipv6', {}]"
:placeholder="this.$t('label.endipv6')"/>
</a-form-item>
<a-form-item v-if="isVirtualRouterForAtLeastOneService">
<span slot="label">
{{ $t('label.routeripv6') }}
<a-tooltip :title="apiParams.routeripv6.description" v-if="'routeripv6' in apiParams">
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
</a-tooltip>
</span>
<a-input
v-decorator="['routeripv6', {}]"
:placeholder="this.$t('label.routeripv6')"/>
</a-form-item>
<a-form-item>
<span slot="label">
{{ $t('label.networkdomain') }}
@ -457,7 +479,9 @@ export default {
selectedNetworkOffering: {},
projects: [],
projectLoading: false,
selectedProject: {}
selectedProject: {},
isVirtualRouterForAtLeastOneService: false,
selectedServiceProviderMap: {}
}
},
watch: {
@ -654,6 +678,7 @@ export default {
this.networkOfferings = []
api('listNetworkOfferings', params).then(json => {
this.networkOfferings = json.listnetworkofferingsresponse.networkoffering
this.handleNetworkOfferingChange(this.networkOfferings[0])
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
@ -662,7 +687,6 @@ export default {
this.form.setFieldsValue({
networkofferingid: 0
})
this.handleNetworkOfferingChange(this.networkOfferings[0])
} else {
this.form.setFieldsValue({
networkofferingid: null
@ -672,6 +696,27 @@ export default {
},
handleNetworkOfferingChange (networkOffering) {
this.selectedNetworkOffering = networkOffering
if (networkOffering) {
this.networkServiceProviderMap(this.selectedNetworkOffering.id)
}
},
networkServiceProviderMap (id) {
api('listNetworkOfferings', { id: id }).then(json => {
var networkOffering = json.listnetworkofferingsresponse.networkoffering[0]
const services = networkOffering.service
this.selectedServiceProviderMap = {}
for (const svc of services) {
this.selectedServiceProviderMap[svc.name] = svc.provider[0].name
}
var providers = Object.values(this.selectedServiceProviderMap)
this.isVirtualRouterForAtLeastOneService = false
var self = this
providers.forEach(function (prvdr, idx) {
if (prvdr === 'VirtualRouter') {
self.isVirtualRouterForAtLeastOneService = true
}
})
})
},
fetchDomainData () {
const params = {}
@ -791,6 +836,9 @@ export default {
if (this.isValidTextValueForKey(values, 'ip4gateway')) {
params.ip6gateway = values.ip6gateway
}
if (this.isValidTextValueForKey(values, 'routerip')) {
params.routerip = values.routerip
}
if (this.isValidTextValueForKey(values, 'ip6cidr')) {
params.ip6cidr = values.ip6cidr
}
@ -800,6 +848,9 @@ export default {
if (this.isValidTextValueForKey(values, 'endipv6')) {
params.endipv6 = values.endipv6
}
if (this.isValidTextValueForKey(values, 'routeripv6')) {
params.routeripv6 = values.routeripv6
}
// IPv6 (end)
if (this.isValidTextValueForKey(values, 'networkdomain')) {