mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
[Vmware] Enable PVLAN support on L2 networks (#3732)
* Enable PVLAN support on L2 networks * Fix prevent null pointer on details * Add marvin tests * Fixes from comments * Fix: missing pvlan type on plugniccommand * Fix checks on network creation for vlans overlap * Fix remove prefix from secondary vlan id * Improve checks on physical network for pvlans * Fix compatibility with previous pvlan creation * Fix shared networks backwards pvlan compatibility * Add ui fix for pvlan type not passed to api * Add check for isolated vlan id overlap * Include check for dynamic vlan reserved for secondary vlan * Fix marvin tests errors * Fix redundant imports * Skip marvin test for pvlan if dvswitch is not present * spelling Co-authored-by: Andrija Panic <45762285+andrijapanicsb@users.noreply.github.com>
This commit is contained in:
parent
70d1535df4
commit
ce896a477d
@ -21,6 +21,8 @@ import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
|
||||
@ -44,6 +46,24 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
|
||||
Shared, Isolated, L2
|
||||
}
|
||||
|
||||
enum PVlanType {
|
||||
Community, Isolated, Promiscuous;
|
||||
|
||||
static PVlanType fromValue(String type) {
|
||||
if (StringUtils.isBlank(type)) {
|
||||
return null;
|
||||
} else if (type.equalsIgnoreCase("promiscuous") || type.equalsIgnoreCase("p")) {
|
||||
return Promiscuous;
|
||||
} else if (type.equalsIgnoreCase("community") || type.equalsIgnoreCase("c")) {
|
||||
return Community;
|
||||
} else if (type.equalsIgnoreCase("isolated") || type.equalsIgnoreCase("i")) {
|
||||
return Isolated;
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Unexpected Private VLAN type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String updatingInSequence = "updatingInSequence";
|
||||
String hideIpAddressUsage = "hideIpAddressUsage";
|
||||
|
||||
@ -416,4 +436,6 @@ public interface Network extends ControlledEntity, StateObject<Network.State>, I
|
||||
boolean isStrechedL2Network();
|
||||
|
||||
String getExternalId();
|
||||
|
||||
PVlanType getPvlanType();
|
||||
}
|
||||
|
||||
@ -314,4 +314,9 @@ public class NetworkProfile implements Network {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PVlanType getPvlanType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity,
|
||||
}
|
||||
|
||||
public enum Detail {
|
||||
InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, RelatedNetworkOffering, domainid, zoneid
|
||||
InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, RelatedNetworkOffering, domainid, zoneid, pvlanType
|
||||
}
|
||||
|
||||
public final static String SystemPublicNetwork = "System-Public-Network";
|
||||
|
||||
@ -97,6 +97,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd {
|
||||
@Parameter(name = ApiConstants.ISOLATED_PVLAN, type = CommandType.STRING, description = "the isolated private VLAN for this network")
|
||||
private String isolatedPvlan;
|
||||
|
||||
@Parameter(name = ApiConstants.ISOLATED_PVLAN_TYPE, type = CommandType.STRING,
|
||||
description = "the isolated private VLAN type for this network")
|
||||
private String isolatedPvlanType;
|
||||
|
||||
@Parameter(name = ApiConstants.NETWORK_DOMAIN, type = CommandType.STRING, description = "network domain")
|
||||
private String networkDomain;
|
||||
|
||||
@ -217,6 +221,10 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd {
|
||||
return externalId;
|
||||
}
|
||||
|
||||
public String getIsolatedPvlanType() {
|
||||
return isolatedPvlanType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisplay() {
|
||||
if(displayNetwork == null)
|
||||
|
||||
@ -180,7 +180,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, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
|
||||
Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
|
||||
|
||||
UserDataServiceProvider getPasswordResetProvider(Network network);
|
||||
|
||||
|
||||
@ -39,6 +39,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.network.dao.NetworkDetailVO;
|
||||
import com.cloud.network.dao.NetworkDetailsDao;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
|
||||
@ -329,6 +331,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
||||
private StorageManager storageMgr;
|
||||
@Inject
|
||||
private NetworkOfferingDetailsDao networkOfferingDetailsDao;
|
||||
@Inject
|
||||
private NetworkDetailsDao networkDetailsDao;
|
||||
|
||||
VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
|
||||
|
||||
@ -4059,6 +4063,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
||||
final VMInstanceVO router = _vmDao.findById(vm.getId());
|
||||
if (router.getState() == State.Running) {
|
||||
try {
|
||||
NetworkDetailVO pvlanTypeDetail = networkDetailsDao.findDetail(network.getId(), ApiConstants.ISOLATED_PVLAN_TYPE);
|
||||
if (pvlanTypeDetail != null) {
|
||||
Map<NetworkOffering.Detail, String> nicDetails = nic.getDetails() == null ? new HashMap<>() : nic.getDetails();
|
||||
s_logger.debug("Found PVLAN type: " + pvlanTypeDetail.getValue() + " on network details, adding it as part of the PlugNicCommand");
|
||||
nicDetails.putIfAbsent(NetworkOffering.Detail.pvlanType, pvlanTypeDetail.getValue());
|
||||
nic.setDetails(nicDetails);
|
||||
}
|
||||
final PlugNicCommand plugNicCmd = new PlugNicCommand(nic, vm.getName(), vm.getType(), vm.getDetails());
|
||||
final Commands cmds = new Commands(Command.OnError.Stop);
|
||||
cmds.addCommand("plugnic", plugNicCmd);
|
||||
|
||||
@ -38,7 +38,10 @@ import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.network.dao.NetworkDetailVO;
|
||||
import com.cloud.network.dao.NetworkDetailsDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO;
|
||||
import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
|
||||
@ -222,6 +225,8 @@ import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
import static org.apache.commons.lang.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* NetworkManagerImpl implements NetworkManager.
|
||||
*/
|
||||
@ -251,6 +256,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
@Inject
|
||||
NetworkDao _networksDao;
|
||||
@Inject
|
||||
NetworkDetailsDao networkDetailsDao;
|
||||
@Inject
|
||||
NicDao _nicDao;
|
||||
@Inject
|
||||
RulesManager _rulesMgr;
|
||||
@ -698,6 +705,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
finalizeServicesAndProvidersForNetwork(offering, plan.getPhysicalNetworkId()));
|
||||
networks.add(networkPersisted);
|
||||
|
||||
if (network.getPvlanType() != null) {
|
||||
NetworkDetailVO detailVO = new NetworkDetailVO(networkPersisted.getId(), ApiConstants.ISOLATED_PVLAN_TYPE, network.getPvlanType().toString(), true);
|
||||
networkDetailsDao.persist(detailVO);
|
||||
}
|
||||
|
||||
if (predefined instanceof NetworkVO && guru instanceof NetworkGuruAdditionalFunctions){
|
||||
final NetworkGuruAdditionalFunctions functions = (NetworkGuruAdditionalFunctions) guru;
|
||||
functions.finalizeNetworkDesign(networkPersisted.getId(), ((NetworkVO)predefined).getVlanIdAsUUID());
|
||||
@ -2168,7 +2180,7 @@ 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, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
|
||||
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
|
||||
|
||||
final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
|
||||
final DataCenterVO zone = _dcDao.findById(zoneId);
|
||||
@ -2280,16 +2292,25 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
|
||||
if (vlanSpecified) {
|
||||
URI uri = BroadcastDomainType.fromString(vlanId);
|
||||
// Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks
|
||||
URI secondaryUri = isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null;
|
||||
//don't allow to specify vlan tag used by physical network for dynamic vlan allocation
|
||||
if (!(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) {
|
||||
throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone "
|
||||
+ zone.getName());
|
||||
}
|
||||
if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) &&
|
||||
_dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) {
|
||||
throw new InvalidParameterValueException("The VLAN tag " + isolatedPvlan + " is already being used for dynamic vlan allocation for the guest network in zone "
|
||||
+ zone.getName());
|
||||
}
|
||||
if (! UuidUtils.validateUUID(vlanId)){
|
||||
// For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone
|
||||
if (ntwkOff.getGuestType() == GuestType.Isolated || !hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff)) {
|
||||
if (ntwkOff.getGuestType() == GuestType.Isolated || ntwkOff.getGuestType() == GuestType.L2 || !hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff)) {
|
||||
if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) {
|
||||
throw new InvalidParameterValueException("Network with vlan " + vlanId + " already exists or overlaps with other network vlans in zone " + zoneId);
|
||||
} else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) {
|
||||
throw new InvalidParameterValueException("Network with vlan " + isolatedPvlan + " already exists or overlaps with other network vlans in zone " + zoneId);
|
||||
} else {
|
||||
final List<DataCenterVnetVO> dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri));
|
||||
//for the network that is created as part of private gateway,
|
||||
@ -2436,8 +2457,15 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
if (vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) {
|
||||
throw new InvalidParameterValueException("Cannot support pvlan with untagged primary vlan!");
|
||||
}
|
||||
userNetwork.setBroadcastUri(NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan));
|
||||
URI uri = NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan);
|
||||
if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString(), isolatedPvlanType).size() > 0) {
|
||||
throw new InvalidParameterValueException("Network with primary vlan " + vlanIdFinal +
|
||||
" and secondary vlan " + isolatedPvlan + " type " + isolatedPvlanType +
|
||||
" already exists or overlaps with other network pvlans in zone " + zoneId);
|
||||
}
|
||||
userNetwork.setBroadcastUri(uri);
|
||||
userNetwork.setBroadcastDomainType(BroadcastDomainType.Pvlan);
|
||||
userNetwork.setPvlanType(isolatedPvlanType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -122,4 +122,6 @@ public interface NetworkDao extends GenericDao<NetworkVO, Long>, StateDao<State,
|
||||
List<NetworkVO> listNetworkVO(List<Long> idset);
|
||||
|
||||
List<NetworkVO> listByAccountIdNetworkName(long accountId, String name);
|
||||
|
||||
List<NetworkVO> listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType);
|
||||
}
|
||||
|
||||
@ -26,7 +26,9 @@ import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.TableGenerator;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
@ -92,6 +94,8 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
|
||||
NetworkOfferingDao _ntwkOffDao;
|
||||
@Inject
|
||||
NetworkOpDao _ntwkOpDao;
|
||||
@Inject
|
||||
NetworkDetailsDao networkDetailsDao;
|
||||
|
||||
TableGenerator _tgMacAddress;
|
||||
|
||||
@ -707,4 +711,72 @@ public class NetworkDaoImpl extends GenericDaoBase<NetworkVO, Long>implements Ne
|
||||
|
||||
return listBy(sc, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a requested PVLAN pair overlaps with any existing PVLAN pair within the same physical network, i.e when:
|
||||
* - The requested exact PVLAN pair exists
|
||||
* - The requested secondary VLAN ID is secondary VLAN ID of an existing PVLAN pair
|
||||
* - The requested secondary VLAN ID is primary VLAN ID of an existing PVLAN pair
|
||||
*/
|
||||
protected boolean isNetworkOverlappingRequestedPvlan(Integer existingPrimaryVlan, Integer existingSecondaryVlan, Network.PVlanType existingPvlanType,
|
||||
Integer requestedPrimaryVlan, Integer requestedSecondaryVlan, Network.PVlanType requestedPvlanType) {
|
||||
if (existingPrimaryVlan == null || existingSecondaryVlan == null || requestedPrimaryVlan == null || requestedSecondaryVlan == null) {
|
||||
throw new CloudRuntimeException(String.format("Missing VLAN ID while checking PVLAN pair (%s, %s)" +
|
||||
" against existing pair (%s, %s)", existingPrimaryVlan, existingSecondaryVlan, requestedPrimaryVlan, requestedSecondaryVlan));
|
||||
}
|
||||
boolean exactMatch = existingPrimaryVlan.equals(requestedPrimaryVlan) && existingSecondaryVlan.equals(requestedSecondaryVlan);
|
||||
boolean secondaryVlanUsed = requestedPvlanType != Network.PVlanType.Promiscuous && requestedSecondaryVlan.equals(existingPrimaryVlan) || requestedSecondaryVlan.equals(existingSecondaryVlan);
|
||||
boolean isolatedMax = false;
|
||||
boolean promiscuousMax = false;
|
||||
if (requestedPvlanType == Network.PVlanType.Isolated && existingPrimaryVlan.equals(requestedPrimaryVlan) && existingPvlanType.equals(Network.PVlanType.Isolated)) {
|
||||
isolatedMax = true;
|
||||
} else if (requestedPvlanType == Network.PVlanType.Promiscuous && existingPrimaryVlan.equals(requestedPrimaryVlan) && existingPvlanType == Network.PVlanType.Promiscuous) {
|
||||
promiscuousMax = true;
|
||||
}
|
||||
return exactMatch || secondaryVlanUsed || isolatedMax || promiscuousMax;
|
||||
}
|
||||
|
||||
protected Network.PVlanType getNetworkPvlanType(long networkId, List<Integer> existingPvlan) {
|
||||
Network.PVlanType existingPvlanType = null;
|
||||
NetworkDetailVO pvlanTypeDetail = networkDetailsDao.findDetail(networkId, ApiConstants.ISOLATED_PVLAN_TYPE);
|
||||
if (pvlanTypeDetail != null) {
|
||||
existingPvlanType = Network.PVlanType.valueOf(pvlanTypeDetail.getValue());
|
||||
} else {
|
||||
existingPvlanType = existingPvlan.get(0).equals(existingPvlan.get(1)) ? Network.PVlanType.Promiscuous : Network.PVlanType.Isolated;
|
||||
}
|
||||
return existingPvlanType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NetworkVO> listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType) {
|
||||
final URI searchUri = BroadcastDomainType.fromString(broadcastUri);
|
||||
if (!searchUri.getScheme().equalsIgnoreCase("pvlan")) {
|
||||
throw new CloudRuntimeException("PVLAN requested but URI is not in the expected format: " + searchUri.toString());
|
||||
}
|
||||
final String searchRange = BroadcastDomainType.getValue(searchUri);
|
||||
final List<Integer> searchVlans = UriUtils.expandPvlanUri(searchRange);
|
||||
final List<NetworkVO> overlappingNetworks = new ArrayList<>();
|
||||
|
||||
final SearchCriteria<NetworkVO> sc = PhysicalNetworkSearch.create();
|
||||
sc.setParameters("physicalNetworkId", physicalNetworkId);
|
||||
|
||||
for (final NetworkVO network : listBy(sc)) {
|
||||
if (network.getBroadcastUri() == null || !network.getBroadcastUri().getScheme().equalsIgnoreCase("pvlan")) {
|
||||
continue;
|
||||
}
|
||||
final String networkVlanRange = BroadcastDomainType.getValue(network.getBroadcastUri());
|
||||
if (networkVlanRange == null || networkVlanRange.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<Integer> existingPvlan = UriUtils.expandPvlanUri(networkVlanRange);
|
||||
Network.PVlanType existingPvlanType = getNetworkPvlanType(network.getId(), existingPvlan);
|
||||
if (isNetworkOverlappingRequestedPvlan(existingPvlan.get(0), existingPvlan.get(1), existingPvlanType,
|
||||
searchVlans.get(0), searchVlans.get(1), pVlanType)) {
|
||||
overlappingNetworks.add(network);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return overlappingNetworks;
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,6 +181,9 @@ public class NetworkVO implements Network {
|
||||
@Transient
|
||||
boolean rollingRestart = false;
|
||||
|
||||
@Transient
|
||||
PVlanType pVlanType;
|
||||
|
||||
public NetworkVO() {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
@ -661,4 +664,12 @@ public class NetworkVO implements Network {
|
||||
public void setRollingRestart(boolean rollingRestart) {
|
||||
this.rollingRestart = rollingRestart;
|
||||
}
|
||||
|
||||
public PVlanType getPvlanType() {
|
||||
return pVlanType;
|
||||
}
|
||||
|
||||
public void setPvlanType(PVlanType pvlanType) {
|
||||
this.pVlanType = pvlanType;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,8 @@ import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.network.dao.NetworkDetailVO;
|
||||
import com.cloud.network.dao.NetworkDetailsDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
@ -77,6 +79,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
|
||||
protected ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
|
||||
@Inject
|
||||
private ServiceOfferingDao _serviceOfferingDao;
|
||||
@Inject
|
||||
private NetworkDetailsDao networkDetailsDao;
|
||||
|
||||
@Override
|
||||
public NicTO toNicTO(NicProfile profile) {
|
||||
@ -182,6 +186,10 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis
|
||||
details.putIfAbsent(NetworkOffering.Detail.MacAddressChanges, NetworkOrchestrationService.MacAddressChanges.value().toString());
|
||||
details.putIfAbsent(NetworkOffering.Detail.ForgedTransmits, NetworkOrchestrationService.ForgedTransmits.value().toString());
|
||||
}
|
||||
NetworkDetailVO pvlantypeDetail = networkDetailsDao.findDetail(network.getId(), ApiConstants.ISOLATED_PVLAN_TYPE);
|
||||
if (pvlantypeDetail != null) {
|
||||
details.putIfAbsent(NetworkOffering.Detail.pvlanType, pvlantypeDetail.getValue());
|
||||
}
|
||||
nicTo.setDetails(details);
|
||||
}
|
||||
nics[i++] = nicTo;
|
||||
|
||||
@ -1693,7 +1693,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);
|
||||
+ "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, 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 "
|
||||
|
||||
@ -38,6 +38,7 @@ import java.util.UUID;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.network.Network.PVlanType;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -200,6 +201,9 @@ import com.cloud.vm.dao.NicSecondaryIpVO;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
||||
import static org.apache.commons.lang.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* NetworkServiceImpl implements NetworkService.
|
||||
*/
|
||||
@ -1062,6 +1066,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
||||
Long aclId = cmd.getAclId();
|
||||
String isolatedPvlan = cmd.getIsolatedPvlan();
|
||||
String externalId = cmd.getExternalId();
|
||||
String isolatedPvlanType = cmd.getIsolatedPvlanType();
|
||||
|
||||
// Validate network offering
|
||||
NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
|
||||
@ -1270,14 +1275,24 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
||||
}
|
||||
}
|
||||
|
||||
if (isolatedPvlan != null && (zone.getNetworkType() != NetworkType.Advanced || ntwkOff.getGuestType() != Network.GuestType.Shared)) {
|
||||
throw new InvalidParameterValueException("Can only support create Private VLAN network with advance shared network!");
|
||||
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!");
|
||||
}
|
||||
|
||||
if (isolatedPvlan != null && ipv6) {
|
||||
if (isNotBlank(isolatedPvlan) && ipv6) {
|
||||
throw new InvalidParameterValueException("Can only support create Private VLAN network with IPv4!");
|
||||
}
|
||||
|
||||
Pair<String, PVlanType> pvlanPair = getPrivateVlanPair(isolatedPvlan, isolatedPvlanType, vlanId);
|
||||
String secondaryVlanId = pvlanPair.first();
|
||||
PVlanType privateVlanType = pvlanPair.second();
|
||||
|
||||
if ((isNotBlank(secondaryVlanId) || privateVlanType != null) && isBlank(vlanId)) {
|
||||
throw new InvalidParameterValueException("VLAN ID has to be set in order to configure a Private VLAN");
|
||||
}
|
||||
|
||||
performBasicPrivateVlanChecks(vlanId, secondaryVlanId, privateVlanType);
|
||||
|
||||
// Regular user can create Guest Isolated Source Nat enabled network only
|
||||
if (_accountMgr.isNormalUser(caller.getId()) && (ntwkOff.getTrafficType() != TrafficType.Guest
|
||||
|| ntwkOff.getGuestType() != Network.GuestType.Isolated && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) {
|
||||
@ -1309,7 +1324,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
||||
throw new InvalidParameterValueException("Cannot support IPv6 on network offering with external devices!");
|
||||
}
|
||||
|
||||
if (isolatedPvlan != null && providersConfiguredForExternalNetworking(ntwkProviders)) {
|
||||
if (isNotBlank(secondaryVlanId) && providersConfiguredForExternalNetworking(ntwkProviders)) {
|
||||
throw new InvalidParameterValueException("Cannot support private vlan on network offering with external devices!");
|
||||
}
|
||||
|
||||
@ -1345,7 +1360,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, isolatedPvlan, 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);
|
||||
|
||||
if (hideIpAddressUsage) {
|
||||
@ -1379,11 +1394,54 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
||||
return network;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information (if set) for private VLAN when creating the network
|
||||
*/
|
||||
protected Pair<String, PVlanType> getPrivateVlanPair(String pvlanId, String pvlanTypeStr, String vlanId) {
|
||||
String secondaryVlanId = pvlanId;
|
||||
PVlanType type = null;
|
||||
|
||||
if (isNotBlank(pvlanTypeStr)) {
|
||||
PVlanType providedType = PVlanType.fromValue(pvlanTypeStr);
|
||||
type = providedType;
|
||||
} else if (isNotBlank(vlanId) && isNotBlank(secondaryVlanId)) {
|
||||
// Preserve the existing functionality
|
||||
type = vlanId.equals(secondaryVlanId) ? PVlanType.Promiscuous : PVlanType.Isolated;
|
||||
}
|
||||
|
||||
if (isBlank(secondaryVlanId) && type == PVlanType.Promiscuous) {
|
||||
secondaryVlanId = vlanId;
|
||||
}
|
||||
|
||||
if (isNotBlank(secondaryVlanId)) {
|
||||
try {
|
||||
Integer.parseInt(secondaryVlanId);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new CloudRuntimeException("The secondary VLAN ID: " + secondaryVlanId + " is not in numeric format", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(secondaryVlanId, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic checks for setting up private VLANs, considering the VLAN ID, secondary VLAN ID and private VLAN type
|
||||
*/
|
||||
protected void performBasicPrivateVlanChecks(String vlanId, String secondaryVlanId, PVlanType privateVlanType) {
|
||||
if (isNotBlank(vlanId) && isBlank(secondaryVlanId) && privateVlanType != null && privateVlanType != PVlanType.Promiscuous) {
|
||||
throw new InvalidParameterValueException("Private VLAN ID has not been set, therefore Promiscuous type is expected");
|
||||
} else if (isNotBlank(vlanId) && isNotBlank(secondaryVlanId) && !vlanId.equalsIgnoreCase(secondaryVlanId) && privateVlanType == PVlanType.Promiscuous) {
|
||||
throw new InvalidParameterValueException("Private VLAN type is set to Promiscuous, but VLAN ID and Secondary VLAN ID differ");
|
||||
} else if (isNotBlank(vlanId) && isNotBlank(secondaryVlanId) && privateVlanType != null && privateVlanType != PVlanType.Promiscuous && vlanId.equalsIgnoreCase(secondaryVlanId)) {
|
||||
throw new InvalidParameterValueException("Private VLAN type is set to " + privateVlanType + ", but VLAN ID and Secondary VLAN ID are equal");
|
||||
}
|
||||
}
|
||||
|
||||
private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, final String vlanId,
|
||||
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 NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal,
|
||||
final String cidr, final boolean createVlan, final String externalId) throws InsufficientCapacityException, ResourceAllocationException {
|
||||
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 {
|
||||
try {
|
||||
Network network = Transaction.execute(new TransactionCallbackWithException<Network, Exception>() {
|
||||
@Override
|
||||
@ -1438,7 +1496,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, externalId);
|
||||
zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId);
|
||||
}
|
||||
|
||||
if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) {
|
||||
@ -4415,7 +4473,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
||||
if (privateNetwork == null) {
|
||||
//create Guest network
|
||||
privateNetwork = _networkMgr.createGuestNetwork(ntwkOffFinal.getId(), networkName, displayText, gateway, cidr, uriString, false, null, owner, null, pNtwk,
|
||||
pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null, null);
|
||||
pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null, null,null);
|
||||
if (privateNetwork != null) {
|
||||
s_logger.debug("Successfully created guest network " + privateNetwork);
|
||||
} else {
|
||||
|
||||
@ -201,6 +201,10 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
|
||||
if (userSpecified.getBroadcastDomainType() != null) {
|
||||
config.setBroadcastDomainType(userSpecified.getBroadcastDomainType());
|
||||
}
|
||||
|
||||
if (userSpecified.getPvlanType() != null) {
|
||||
config.setPvlanType(userSpecified.getPvlanType());
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSecurityGroupEnabled = _networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Service.SecurityGroup);
|
||||
|
||||
@ -214,6 +214,10 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
|
||||
if (offering.isSpecifyVlan()) {
|
||||
network.setBroadcastUri(userSpecified.getBroadcastUri());
|
||||
network.setState(State.Setup);
|
||||
if (userSpecified.getPvlanType() != null) {
|
||||
network.setBroadcastDomainType(BroadcastDomainType.Pvlan);
|
||||
network.setPvlanType(userSpecified.getPvlanType());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final String guestNetworkCidr = dc.getGuestNetworkCidr();
|
||||
|
||||
@ -2569,7 +2569,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, externalId);
|
||||
subdomainAccess, vpcId, null, null, isDisplayNetworkEnabled, null, null, externalId);
|
||||
|
||||
if (guestNetwork != null) {
|
||||
guestNetwork.setNetworkACLId(aclId);
|
||||
|
||||
@ -3354,7 +3354,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, false, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null, null,
|
||||
null);
|
||||
if (newNetwork != null) {
|
||||
defaultNetwork = _networkDao.findById(newNetwork.getId());
|
||||
@ -6511,7 +6511,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, true, 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);
|
||||
|
||||
@ -26,6 +26,7 @@ import java.util.UUID;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@ -123,7 +124,7 @@ public class CreatePrivateNetworkTest {
|
||||
when(
|
||||
networkService._networkMgr.createGuestNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), anyString(), anyBoolean(), anyString(),
|
||||
eq(account), anyLong(), eq(physicalNetwork), eq(physicalNetwork.getDataCenterId()), eq(ACLType.Account), anyBoolean(), eq(1L), anyString(), anyString(),
|
||||
anyBoolean(), anyString(), anyString())).thenReturn(net);
|
||||
anyBoolean(), anyString(), Matchers.any(), anyString())).thenReturn(net);
|
||||
|
||||
when(networkService._privateIpDao.findByIpAndSourceNetworkId(net.getId(), "10.1.1.2")).thenReturn(null);
|
||||
when(networkService._privateIpDao.findByIpAndSourceNetworkIdAndVpcId(eq(1L), anyString(), eq(1L))).thenReturn(null);
|
||||
|
||||
@ -0,0 +1,120 @@
|
||||
// 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.
|
||||
package com.cloud.network;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NetworkServiceImplTest {
|
||||
|
||||
private NetworkServiceImpl service = new NetworkServiceImpl();
|
||||
|
||||
private static final String VLAN_ID_900 = "900";
|
||||
private static final String VLAN_ID_901 = "901";
|
||||
private static final String VLAN_ID_902 = "902";
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairNoVlans() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(null, null, null);
|
||||
Assert.assertNull(pair.first());
|
||||
Assert.assertNull(pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairVlanPrimaryOnly() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(null, null, VLAN_ID_900);
|
||||
Assert.assertNull(pair.first());
|
||||
Assert.assertNull(pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairVlanPrimaryPromiscuousType() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(null, Network.PVlanType.Promiscuous.toString(), VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_900, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Promiscuous, pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairPromiscuousType() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(VLAN_ID_900, Network.PVlanType.Promiscuous.toString(), VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_900, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Promiscuous, pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairPromiscuousTypeOnSecondaryVlanId() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(VLAN_ID_900, "promiscuous", VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_900, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Promiscuous, pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairIsolatedType() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(VLAN_ID_901, Network.PVlanType.Isolated.toString(), VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_901, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Isolated, pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairIsolatedTypeOnSecondaryVlanId() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(VLAN_ID_901, "isolated", VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_901, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Isolated, pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairCommunityType() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(VLAN_ID_902, Network.PVlanType.Community.toString(), VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_902, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Community, pair.second());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPrivateVlanPairCommunityTypeOnSecondaryVlanId() {
|
||||
Pair<String, Network.PVlanType> pair = service.getPrivateVlanPair(VLAN_ID_902, "community", VLAN_ID_900);
|
||||
Assert.assertEquals(VLAN_ID_902, pair.first());
|
||||
Assert.assertEquals(Network.PVlanType.Community, pair.second());
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testPerformBasicChecksPromiscuousTypeExpectedIsolatedSet() {
|
||||
service.performBasicPrivateVlanChecks(VLAN_ID_900, VLAN_ID_900, Network.PVlanType.Isolated);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testPerformBasicChecksPromiscuousTypeExpectedCommunitySet() {
|
||||
service.performBasicPrivateVlanChecks(VLAN_ID_900, VLAN_ID_900, Network.PVlanType.Community);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testPerformBasicChecksPromiscuousTypeExpectedSecondaryVlanNullIsolatedSet() {
|
||||
service.performBasicPrivateVlanChecks(VLAN_ID_900, null, Network.PVlanType.Isolated);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testPerformBasicChecksPromiscuousTypeExpectedSecondaryVlanNullCommunitySet() {
|
||||
service.performBasicPrivateVlanChecks(VLAN_ID_900, null, Network.PVlanType.Community);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testPerformBasicChecksPromiscuousTypeExpectedDifferentVlanIds() {
|
||||
service.performBasicPrivateVlanChecks(VLAN_ID_900, VLAN_ID_901, Network.PVlanType.Promiscuous);
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,46 +16,40 @@
|
||||
// under the License.
|
||||
package com.cloud.network.dao;
|
||||
|
||||
import com.cloud.network.Network;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Assert;
|
||||
import org.mockito.Spy;
|
||||
|
||||
public class NetworkDaoTest extends TestCase {
|
||||
public void testTags() {
|
||||
// NetworkDaoImpl dao = ComponentLocator.inject(NetworkDaoImpl.class);
|
||||
//
|
||||
// dao.expunge(1001l);
|
||||
// NetworkVO network = new NetworkVO(1001, TrafficType.Control, GuestType.Shared, Mode.Dhcp, BroadcastDomainType.Native, 1, 1, 1, 1, 1001, "Name", "DisplayText", false, true, true, null, null);
|
||||
// network.setGuruName("guru_name");
|
||||
// List<String> tags = new ArrayList<String>();
|
||||
//
|
||||
// tags.add("a");
|
||||
// tags.add("b");
|
||||
// network.setTags(tags);
|
||||
//
|
||||
// network = dao.persist(network);
|
||||
// List<String> saveTags = network.getTags();
|
||||
// Assert.assertTrue(saveTags.size() == 2 && saveTags.contains("a") && saveTags.contains("b"));
|
||||
//
|
||||
// NetworkVO retrieved = dao.findById(1001l);
|
||||
// List<String> retrievedTags = retrieved.getTags();
|
||||
// Assert.assertTrue(retrievedTags.size() == 2 && retrievedTags.contains("a") && retrievedTags.contains("b"));
|
||||
//
|
||||
// List<String> updateTags = new ArrayList<String>();
|
||||
// updateTags.add("e");
|
||||
// updateTags.add("f");
|
||||
// retrieved.setTags(updateTags);
|
||||
// dao.update(retrieved.getId(), retrieved);
|
||||
//
|
||||
// retrieved = dao.findById(1001l);
|
||||
// retrievedTags = retrieved.getTags();
|
||||
// Assert.assertTrue("Unable to retrieve back the data updated", retrievedTags.size() == 2 && retrievedTags.contains("e") && retrievedTags.contains("f"));
|
||||
//
|
||||
// dao.expunge(1001l);
|
||||
|
||||
@Spy
|
||||
private NetworkDaoImpl dao = new NetworkDaoImpl();
|
||||
|
||||
private static final Integer existingPrimaryVlan = 900;
|
||||
private static final Integer existingSecondaryVlan = 901;
|
||||
|
||||
private static final Integer requestedVlan = 902;
|
||||
|
||||
public void testNetworkOverlappingExactPair() {
|
||||
Assert.assertTrue(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Isolated,
|
||||
existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Isolated));
|
||||
Assert.assertTrue(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Isolated,
|
||||
existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Community));
|
||||
}
|
||||
|
||||
public void testListBy() {
|
||||
// NetworkDaoImpl dao = ComponentLocator.inject(NetworkDaoImpl.class);
|
||||
//
|
||||
// dao.listBy(1l, 1l, 1l, "192.168.192.0/24");
|
||||
public void testNetworkOverlappingPromiscuous() {
|
||||
Assert.assertTrue(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingPrimaryVlan, Network.PVlanType.Promiscuous,
|
||||
existingPrimaryVlan, existingPrimaryVlan, Network.PVlanType.Promiscuous));
|
||||
}
|
||||
|
||||
public void testNetworkOverlappingIsolated() {
|
||||
Assert.assertTrue(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Isolated,
|
||||
existingPrimaryVlan, requestedVlan, Network.PVlanType.Isolated));
|
||||
}
|
||||
|
||||
public void testNetworkOverlappingMultipleCommunityAllowed() {
|
||||
Assert.assertFalse(dao.isNetworkOverlappingRequestedPvlan(existingPrimaryVlan, existingSecondaryVlan, Network.PVlanType.Community,
|
||||
existingPrimaryVlan, requestedVlan, Network.PVlanType.Community));
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,7 +641,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, String externalId) throws ConcurrentOperationException, InsufficientCapacityException,
|
||||
String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId) throws ConcurrentOperationException, InsufficientCapacityException,
|
||||
ResourceAllocationException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
|
||||
@ -240,4 +240,9 @@ public class MockNetworkDaoImpl extends GenericDaoBase<NetworkVO, Long> implemen
|
||||
public List<NetworkVO> listByAccountIdNetworkName(final long accountId, final String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NetworkVO> listByPhysicalNetworkPvlan(long physicalNetworkId, String broadcastUri, Network.PVlanType pVlanType) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,9 @@ from marvin.lib.base import (Account,
|
||||
Network,
|
||||
NetworkOffering,
|
||||
LoadBalancerRule,
|
||||
Router)
|
||||
Router,
|
||||
NIC,
|
||||
Cluster)
|
||||
from marvin.lib.common import (get_domain,
|
||||
get_zone,
|
||||
get_test_template,
|
||||
@ -47,6 +49,7 @@ from marvin.lib.common import (get_domain,
|
||||
list_configurations,
|
||||
verifyGuestTrafficPortGroups)
|
||||
from nose.plugins.attrib import attr
|
||||
from marvin.lib.decoratorGenerators import skipTestIf
|
||||
from ddt import ddt, data
|
||||
# Import System modules
|
||||
import time
|
||||
@ -1532,3 +1535,287 @@ class TestL2Networks(cloudstackTestCase):
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
class TestPrivateVlansL2Networks(cloudstackTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.apiclient = self.testClient.getApiClient()
|
||||
|
||||
self.cleanup = [
|
||||
]
|
||||
|
||||
def tearDown(self):
|
||||
cleanup_resources(self.apiclient, self.cleanup)
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
testClient = super(TestPrivateVlansL2Networks, cls).getClsTestClient()
|
||||
cls.apiclient = testClient.getApiClient()
|
||||
cls.services = testClient.getParsedTestDataConfig()
|
||||
|
||||
cls.domain = get_domain(cls.apiclient)
|
||||
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
|
||||
cls.hypervisor = testClient.getHypervisorInfo()
|
||||
cls.services['mode'] = cls.zone.networktype
|
||||
|
||||
# Supported hypervisor = Vmware using dvSwitches for guest traffic
|
||||
isVmware = False
|
||||
isDvSwitch = False
|
||||
if cls.hypervisor.lower() in ["vmware"]:
|
||||
isVmware = True
|
||||
clusters = Cluster.list(cls.apiclient, zoneid=cls.zone.id, hypervisor=cls.hypervisor)
|
||||
for cluster in clusters:
|
||||
if cluster.resourcedetails.guestvswitchtype == "vmwaredvs":
|
||||
# Test only if cluster uses dvSwitch
|
||||
isDvSwitch = True
|
||||
break
|
||||
|
||||
supported = isVmware and isDvSwitch
|
||||
cls.vmwareHypervisorDvSwitchesForGuestTrafficNotPresent = not supported
|
||||
|
||||
cls._cleanup = []
|
||||
|
||||
if supported:
|
||||
|
||||
cls.account = Account.create(
|
||||
cls.apiclient,
|
||||
cls.services["account"],
|
||||
admin=True,
|
||||
domainid=cls.domain.id
|
||||
)
|
||||
cls.template = get_test_template(
|
||||
cls.apiclient,
|
||||
cls.zone.id,
|
||||
cls.hypervisor
|
||||
)
|
||||
cls.service_offering = ServiceOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["service_offerings"]["tiny"]
|
||||
)
|
||||
cls.services["network"]["zoneid"] = cls.zone.id
|
||||
cls.services['mode'] = cls.zone.networktype
|
||||
cls.services["small"]["zoneid"] = cls.zone.id
|
||||
cls.services["small"]["template"] = cls.template.id
|
||||
cls.services["l2-network-pvlan-community-1"] = {
|
||||
"name": "Test Network L2 PVLAN Community 1",
|
||||
"displaytext": "Test Network L2 PVLAN Community 1",
|
||||
"vlan": 900,
|
||||
"isolatedpvlan": "901",
|
||||
"isolatedpvlantype": "community"
|
||||
}
|
||||
cls.services["l2-network-pvlan-community-2"] = {
|
||||
"name": "Test Network L2 PVLAN Community 2",
|
||||
"displaytext": "Test Network L2 PVLAN Community 2",
|
||||
"vlan": 900,
|
||||
"isolatedpvlan": "902",
|
||||
"isolatedpvlantype": "community"
|
||||
}
|
||||
cls.services["l2-network-pvlan-promiscuous"] = {
|
||||
"name": "Test Network L2 PVLAN Promiscuous",
|
||||
"displaytext": "Test Network L2 PVLAN Promiscuous",
|
||||
"vlan": 900,
|
||||
"isolatedpvlan" : "900",
|
||||
"isolatedpvlantype": "promiscuous"
|
||||
}
|
||||
cls.services["l2-network-pvlan-isolated"] = {
|
||||
"name": "Test Network L2 PVLAN Isolated",
|
||||
"displaytext": "Test Network L2 PVLAN Isolated",
|
||||
"vlan": 900,
|
||||
"isolatedpvlan": "903",
|
||||
"isolatedpvlantype": "isolated"
|
||||
}
|
||||
|
||||
cls.l2_network_offering = NetworkOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["l2-network_offering"],
|
||||
specifyvlan=True
|
||||
)
|
||||
cls.isolated_network_offering = NetworkOffering.create(
|
||||
cls.apiclient,
|
||||
cls.services["network_offering"]
|
||||
)
|
||||
cls.l2_network_offering.update(cls.apiclient, state='Enabled')
|
||||
cls.isolated_network_offering.update(cls.apiclient, state='Enabled')
|
||||
|
||||
cls.l2_pvlan_community1 = Network.create(
|
||||
cls.apiclient,
|
||||
cls.services["l2-network-pvlan-community-1"],
|
||||
zoneid=cls.zone.id,
|
||||
networkofferingid=cls.l2_network_offering.id
|
||||
)
|
||||
cls.l2_pvlan_community2 = Network.create(
|
||||
cls.apiclient,
|
||||
cls.services["l2-network-pvlan-community-2"],
|
||||
zoneid=cls.zone.id,
|
||||
networkofferingid=cls.l2_network_offering.id
|
||||
)
|
||||
cls.l2_pvlan_isolated = Network.create(
|
||||
cls.apiclient,
|
||||
cls.services["l2-network-pvlan-isolated"],
|
||||
zoneid=cls.zone.id,
|
||||
networkofferingid=cls.l2_network_offering.id
|
||||
)
|
||||
cls.l2_pvlan_promiscuous = Network.create(
|
||||
cls.apiclient,
|
||||
cls.services["l2-network-pvlan-promiscuous"],
|
||||
zoneid=cls.zone.id,
|
||||
networkofferingid=cls.l2_network_offering.id
|
||||
)
|
||||
cls.isolated_network = Network.create(
|
||||
cls.apiclient,
|
||||
cls.services["isolated_network"],
|
||||
zoneid=cls.zone.id,
|
||||
networkofferingid=cls.isolated_network_offering.id,
|
||||
accountid=cls.account.name,
|
||||
domainid=cls.account.domainid
|
||||
)
|
||||
|
||||
cls._cleanup = [
|
||||
cls.l2_pvlan_promiscuous,
|
||||
cls.l2_pvlan_isolated,
|
||||
cls.l2_pvlan_community1,
|
||||
cls.l2_pvlan_community2,
|
||||
cls.isolated_network,
|
||||
cls.l2_network_offering,
|
||||
cls.isolated_network_offering,
|
||||
cls.service_offering,
|
||||
cls.account,
|
||||
]
|
||||
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
try:
|
||||
cleanup_resources(cls.apiclient, cls._cleanup)
|
||||
except Exception as e:
|
||||
raise Exception("Warning: Exception during cleanup : %s" % e)
|
||||
return
|
||||
|
||||
def deploy_vm_multiple_nics(self, name, l2net):
|
||||
"""
|
||||
Deploy VM on L2 network and isolated network so VM can get an IP, to use with arping command for isolation test
|
||||
"""
|
||||
self.services["small"]["name"] = name
|
||||
|
||||
vm = VirtualMachine.create(
|
||||
self.apiclient,
|
||||
self.services["small"],
|
||||
accountid=self.account.name,
|
||||
domainid=self.account.domainid,
|
||||
serviceofferingid=self.service_offering.id,
|
||||
networkids=[self.isolated_network.id, l2net.id],
|
||||
mode=self.services["mode"]
|
||||
)
|
||||
|
||||
return vm
|
||||
|
||||
def is_vm_l2_isolated_from_dest(self, vm, eth_device, dest_ip):
|
||||
"""
|
||||
True if VM is isolated from dest IP - using arping through the NIC on L2 network:
|
||||
If arping can reach destination, then response is greater than 3 (no reply)
|
||||
"""
|
||||
ssh_client = vm.get_ssh_client()
|
||||
response = ssh_client.execute("/usr/sbin/arping -c 5 -I %s %s" % (eth_device, str(dest_ip)))
|
||||
return len(response) == 3
|
||||
|
||||
def enable_l2_nic(self, vm):
|
||||
vm_ip = list(filter(lambda x: x['networkid'] == self.isolated_network.id, vm.nic))[0]['ipaddress']
|
||||
ssh_client = vm.get_ssh_client()
|
||||
eth_device = "eth0"
|
||||
if len(ssh_client.execute("/sbin/ifconfig %s | grep %s" % (eth_device, vm_ip))) > 0:
|
||||
eth_device = "eth1"
|
||||
ssh_client.execute("/sbin/ifconfig %s up" % eth_device)
|
||||
return vm_ip, eth_device
|
||||
|
||||
@attr(tags=["advanced", "advancedns", "smoke", "pvlan"], required_hardware="true")
|
||||
@skipTestIf("vmwareHypervisorDvSwitchesForGuestTrafficNotPresent")
|
||||
def test_l2_network_pvlan_connectivity(self):
|
||||
try:
|
||||
vm_community1_one = self.deploy_vm_multiple_nics("vmcommunity1one", self.l2_pvlan_community1)
|
||||
vm_community1_two = self.deploy_vm_multiple_nics("vmcommunity1two", self.l2_pvlan_community1)
|
||||
vm_community2 = self.deploy_vm_multiple_nics("vmcommunity2", self.l2_pvlan_community2)
|
||||
|
||||
vm_isolated1 = self.deploy_vm_multiple_nics("vmisolated1", self.l2_pvlan_isolated)
|
||||
vm_isolated2 = self.deploy_vm_multiple_nics("vmisolated2", self.l2_pvlan_isolated)
|
||||
|
||||
vm_promiscuous1 = self.deploy_vm_multiple_nics("vmpromiscuous1", self.l2_pvlan_promiscuous)
|
||||
vm_promiscuous2 = self.deploy_vm_multiple_nics("vmpromiscuous2", self.l2_pvlan_promiscuous)
|
||||
|
||||
self.cleanup.append(vm_community1_one)
|
||||
self.cleanup.append(vm_community1_two)
|
||||
self.cleanup.append(vm_community2)
|
||||
self.cleanup.append(vm_isolated1)
|
||||
self.cleanup.append(vm_isolated2)
|
||||
self.cleanup.append(vm_promiscuous1)
|
||||
self.cleanup.append(vm_promiscuous2)
|
||||
|
||||
vm_community1_one_ip, vm_community1_one_eth = self.enable_l2_nic(vm_community1_one)
|
||||
vm_community1_two_ip, vm_community1_two_eth = self.enable_l2_nic(vm_community1_two)
|
||||
vm_community2_ip, vm_community2_eth = self.enable_l2_nic(vm_community2)
|
||||
vm_isolated1_ip, vm_isolated1_eth = self.enable_l2_nic(vm_isolated1)
|
||||
vm_isolated2_ip, vm_isolated2_eth = self.enable_l2_nic(vm_isolated2)
|
||||
vm_promiscuous1_ip, vm_promiscuous1_eth = self.enable_l2_nic(vm_promiscuous1)
|
||||
vm_promiscuous2_ip, vm_promiscuous2_eth = self.enable_l2_nic(vm_promiscuous2)
|
||||
|
||||
# Community PVLAN checks
|
||||
different_community_isolated = self.is_vm_l2_isolated_from_dest(vm_community1_one, vm_community1_one_eth, vm_community2_ip)
|
||||
same_community_isolated = self.is_vm_l2_isolated_from_dest(vm_community1_one, vm_community1_one_eth, vm_community1_two_ip)
|
||||
community_to_promiscuous_isolated = self.is_vm_l2_isolated_from_dest(vm_community1_one, vm_community1_one_eth, vm_promiscuous1_ip)
|
||||
community_to_isolated = self.is_vm_l2_isolated_from_dest(vm_community1_one, vm_community1_one_eth, vm_isolated1_ip)
|
||||
|
||||
self.assertTrue(
|
||||
different_community_isolated,
|
||||
"VMs on different community PVLANs must be isolated on layer 2"
|
||||
)
|
||||
|
||||
self.assertFalse(
|
||||
same_community_isolated,
|
||||
"VMs on the same community PVLAN must not be isolated on layer 2"
|
||||
)
|
||||
|
||||
self.assertFalse(
|
||||
community_to_promiscuous_isolated,
|
||||
"VMs on community PVLANs must not be isolated on layer 2 to VMs on promiscuous PVLAN"
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
community_to_isolated,
|
||||
"VMs on community PVLANs must be isolated on layer 2 to Vms on isolated PVLAN"
|
||||
)
|
||||
|
||||
# Isolated PVLAN checks
|
||||
same_isolated = self.is_vm_l2_isolated_from_dest(vm_isolated1, vm_isolated1_eth, vm_isolated2_ip)
|
||||
isolated_to_community_isolated = self.is_vm_l2_isolated_from_dest(vm_isolated1, vm_isolated1_eth, vm_community1_one_ip)
|
||||
|
||||
self.assertTrue(
|
||||
same_isolated,
|
||||
"VMs on isolated PVLANs must be isolated on layer 2"
|
||||
)
|
||||
self.assertTrue(
|
||||
isolated_to_community_isolated,
|
||||
"VMs on isolated PVLANs must be isolated on layer 2 to Vms on community PVLAN"
|
||||
)
|
||||
|
||||
# Promiscuous PVLAN checks
|
||||
same_promiscuous = self.is_vm_l2_isolated_from_dest(vm_promiscuous1, vm_promiscuous1_eth, vm_promiscuous2_ip)
|
||||
prom_to_community_isolated = self.is_vm_l2_isolated_from_dest(vm_promiscuous1, vm_promiscuous1_eth, vm_community1_one_ip)
|
||||
prom_to_isolated = self.is_vm_l2_isolated_from_dest(vm_promiscuous1, vm_promiscuous1_eth, vm_isolated1_ip)
|
||||
|
||||
self.assertFalse(
|
||||
same_promiscuous,
|
||||
"VMs on promiscuous PVLANs must not be isolated on layer 2"
|
||||
)
|
||||
self.assertFalse(
|
||||
prom_to_community_isolated,
|
||||
"VMs on promiscuous PVLANs must not be isolated on layer 2 to Vms on isolated PVLAN"
|
||||
)
|
||||
self.assertFalse(
|
||||
prom_to_isolated,
|
||||
"VMs on promiscuous PVLANs must not be isolated on layer 2 to Vms on community PVLAN"
|
||||
)
|
||||
except Exception as e:
|
||||
self.fail("Failing test. Error: %s" % e)
|
||||
|
||||
return
|
||||
|
||||
@ -3067,6 +3067,10 @@ class Network:
|
||||
cmd.vlan = services["vlan"]
|
||||
if "acltype" in services:
|
||||
cmd.acltype = services["acltype"]
|
||||
if "isolatedpvlan" in services:
|
||||
cmd.isolatedpvlan = services["isolatedpvlan"]
|
||||
if "isolatedpvlantype" in services:
|
||||
cmd.isolatedpvlantype = services["isolatedpvlantype"]
|
||||
|
||||
if accountid:
|
||||
cmd.account = accountid
|
||||
|
||||
@ -1550,6 +1550,11 @@ var dictionary = {
|
||||
"label.search":"Search",
|
||||
"label.secondary.ips":"Secondary IPs",
|
||||
"label.secondary.isolated.vlan.id":"Secondary Isolated VLAN ID",
|
||||
"label.secondary.isolated.vlan.type":"Secondary Isolated VLAN Type",
|
||||
"label.secondary.isolated.vlan.type.community":"Community",
|
||||
"label.secondary.isolated.vlan.type.isolated":"Isolated",
|
||||
"label.secondary.isolated.vlan.type.none":"None",
|
||||
"label.secondary.isolated.vlan.type.promiscuous":"Promiscuous",
|
||||
"label.secondary.staging.store":"Secondary Staging Store",
|
||||
"label.secondary.staging.store.details":"Secondary Staging Store details",
|
||||
"label.secondary.storage":"Secondary Storage",
|
||||
|
||||
@ -358,6 +358,41 @@ var addGuestNetworkDialog = {
|
||||
isolatedpvlanId: {
|
||||
label: 'label.secondary.isolated.vlan.id'
|
||||
},
|
||||
pvlanType: {
|
||||
label: 'label.secondary.isolated.vlan.type',
|
||||
isHidden: true,
|
||||
select: function (args) {
|
||||
var type = [{
|
||||
id: 'none',
|
||||
description: _l('label.secondary.isolated.vlan.type.none')
|
||||
}, {
|
||||
id: 'community',
|
||||
description: _l('label.secondary.isolated.vlan.type.community')
|
||||
}, {
|
||||
id: 'isolated',
|
||||
description: _l('label.secondary.isolated.vlan.type.isolated')
|
||||
}, {
|
||||
id: 'promiscuous',
|
||||
description: _l('label.secondary.isolated.vlan.type.promiscuous')
|
||||
}
|
||||
];
|
||||
|
||||
args.response.success({
|
||||
data: type
|
||||
});
|
||||
|
||||
args.$select.change(function () {
|
||||
var $form = $(this).closest('form');
|
||||
var pvlanType = $(this).val();
|
||||
|
||||
if (pvlanType === 'none' || pvlanType === 'promiscuous') {
|
||||
$form.find('.form-item[rel=isolatedpvlanId]').hide();
|
||||
} else if (pvlanType === 'isolated' || pvlanType === 'community') {
|
||||
$form.find('.form-item[rel=isolatedpvlanId]').css('display', 'inline-block');
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
scope: {
|
||||
label: 'label.scope',
|
||||
@ -635,12 +670,12 @@ var addGuestNetworkDialog = {
|
||||
$form.find('.form-item[rel=vlanId]').hide();
|
||||
cloudStack.dialog.createFormField.validation.required.remove($form.find('.form-item[rel=vlanId]')); //make vlanId optional
|
||||
|
||||
$form.find('.form-item[rel=isolatedpvlanId]').hide();
|
||||
$form.find('.form-item[rel=pvlanType]').hide();
|
||||
} else {
|
||||
$form.find('.form-item[rel=vlanId]').css('display', 'inline-block');
|
||||
cloudStack.dialog.createFormField.validation.required.add($form.find('.form-item[rel=vlanId]')); //make vlanId required
|
||||
|
||||
$form.find('.form-item[rel=isolatedpvlanId]').css('display', 'inline-block');
|
||||
$form.find('.form-item[rel=pvlanType]').css('display', 'inline-block');
|
||||
}
|
||||
return false; //break each loop
|
||||
}
|
||||
@ -816,6 +851,9 @@ var addGuestNetworkDialog = {
|
||||
if (args.data.hideipaddressusage != null && args.data.hideipaddressusage) {
|
||||
array1.push("&hideipaddressusage=true")
|
||||
}
|
||||
if (args.$form.find('.form-item[rel=pvlanType]').css('display') != 'none' && args.data.pvlanType != 'none') {
|
||||
array1.push("&isolatedpvlantype=" + args.data.pvlanType);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: createURL("createNetwork" + array1.join("")),
|
||||
@ -1006,6 +1044,7 @@ var addL2GuestNetwork = {
|
||||
args.$select.change(function() {
|
||||
var $vlan = args.$select.closest('form').find('[rel=vlan]');
|
||||
var $bypassVlanOverlapCheck = args.$select.closest('form').find('[rel=bypassVlanOverlapCheck]');
|
||||
var $pvlanType = args.$select.closest('form').find('[rel=pvlanType]');
|
||||
var networkOffering = $.grep(
|
||||
networkOfferingObjs, function(netoffer) {
|
||||
return netoffer.id == args.$select.val();
|
||||
@ -1015,9 +1054,11 @@ var addL2GuestNetwork = {
|
||||
if (networkOffering.specifyvlan) {
|
||||
$vlan.css('display', 'inline-block');
|
||||
$bypassVlanOverlapCheck.css('display', 'inline-block');
|
||||
$pvlanType.css('display', 'inline-block');
|
||||
} else {
|
||||
$vlan.hide();
|
||||
$bypassVlanOverlapCheck.hide();
|
||||
$pvlanType.hide();
|
||||
}
|
||||
});
|
||||
|
||||
@ -1046,6 +1087,45 @@ var addL2GuestNetwork = {
|
||||
isBoolean: true,
|
||||
isHidden: true
|
||||
},
|
||||
pvlanId: {
|
||||
label: 'label.secondary.isolated.vlan.id',
|
||||
isHidden: true
|
||||
},
|
||||
pvlanType: {
|
||||
label: 'label.secondary.isolated.vlan.type',
|
||||
isHidden: true,
|
||||
select: function (args) {
|
||||
var type = [{
|
||||
id: 'none',
|
||||
description: _l('label.secondary.isolated.vlan.type.none')
|
||||
}, {
|
||||
id: 'community',
|
||||
description: _l('label.secondary.isolated.vlan.type.community')
|
||||
}, {
|
||||
id: 'isolated',
|
||||
description: _l('label.secondary.isolated.vlan.type.isolated')
|
||||
}, {
|
||||
id: 'promiscuous',
|
||||
description: _l('label.secondary.isolated.vlan.type.promiscuous')
|
||||
}
|
||||
];
|
||||
|
||||
args.response.success({
|
||||
data: type
|
||||
});
|
||||
|
||||
args.$select.change(function () {
|
||||
var $form = $(this).closest('form');
|
||||
var pvlanType = $(this).val();
|
||||
|
||||
if (pvlanType === 'none' || pvlanType === 'promiscuous') {
|
||||
$form.find('.form-item[rel=pvlanId]').hide();
|
||||
} else if (pvlanType === 'isolated' || pvlanType === 'community') {
|
||||
$form.find('.form-item[rel=pvlanId]').css('display', 'inline-block');
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
account: {
|
||||
label: 'label.account',
|
||||
validation: {
|
||||
@ -1076,6 +1156,18 @@ var addL2GuestNetwork = {
|
||||
});
|
||||
}
|
||||
|
||||
if (args.$form.find('.form-item[rel=pvlanId]').css('display') != 'none') {
|
||||
$.extend(dataObj, {
|
||||
isolatedpvlan: args.data.pvlanId
|
||||
});
|
||||
}
|
||||
|
||||
if (args.$form.find('.form-item[rel=pvlanType]').css('display') != 'none' && args.data.pvlanType != 'none') {
|
||||
$.extend(dataObj, {
|
||||
isolatedpvlantype: args.data.pvlanType
|
||||
});
|
||||
}
|
||||
|
||||
if (args.data.domain != null && args.data.domain.length > 0) {
|
||||
$.extend(dataObj, {
|
||||
domainid: args.data.domain
|
||||
|
||||
@ -624,4 +624,15 @@ public class UriUtils {
|
||||
}
|
||||
return !Collections.disjoint(vlans1, vlans2);
|
||||
}
|
||||
|
||||
public static List<Integer> expandPvlanUri(String pvlanRange) {
|
||||
final List<Integer> expandedVlans = new ArrayList<>();
|
||||
if (Strings.isNullOrEmpty(pvlanRange)) {
|
||||
return expandedVlans;
|
||||
}
|
||||
String[] parts = pvlanRange.split("-i");
|
||||
expandedVlans.add(Integer.parseInt(parts[0]));
|
||||
expandedVlans.add(Integer.parseInt(parts[1]));
|
||||
return expandedVlans;
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
|
||||
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.w3c.dom.Document;
|
||||
@ -579,8 +580,9 @@ public class HypervisorHostHelper {
|
||||
// First, if both vlan id and pvlan id are provided, we need to
|
||||
// reconfigure the DVSwitch to have a tuple <vlan id, pvlan id> of
|
||||
// type isolated.
|
||||
String pvlanType = MapUtils.isNotEmpty(details) ? details.get(NetworkOffering.Detail.pvlanType) : null;
|
||||
if (vid != null && spvlanid != null) {
|
||||
setupPVlanPair(dvSwitchMo, morDvSwitch, vid, spvlanid);
|
||||
setupPVlanPair(dvSwitchMo, morDvSwitch, vid, spvlanid, pvlanType);
|
||||
}
|
||||
|
||||
VMwareDVSPortgroupPolicy portGroupPolicy = null;
|
||||
@ -660,7 +662,8 @@ public class HypervisorHostHelper {
|
||||
return vCenterApiVersion.compareTo(minVcenterApiVersionForFeature) >= 0 ? true : false;
|
||||
}
|
||||
|
||||
private static void setupPVlanPair(DistributedVirtualSwitchMO dvSwitchMo, ManagedObjectReference morDvSwitch, Integer vid, Integer spvlanid) throws Exception {
|
||||
private static void setupPVlanPair(DistributedVirtualSwitchMO dvSwitchMo, ManagedObjectReference morDvSwitch, Integer vid, Integer spvlanid, String pvlanType) throws Exception {
|
||||
s_logger.debug(String.format("Setting up PVLAN on dvSwitch %s with the following information: %s %s %s", dvSwitchMo.getName(), vid, spvlanid, pvlanType));
|
||||
Map<Integer, HypervisorHostHelper.PvlanType> vlanmap = dvSwitchMo.retrieveVlanPvlan(vid, spvlanid, morDvSwitch);
|
||||
if (!vlanmap.isEmpty()) {
|
||||
// Then either vid or pvlanid or both are already being used. Check how.
|
||||
@ -678,25 +681,20 @@ public class HypervisorHostHelper {
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
} else {
|
||||
if (vlanmap.containsKey(spvlanid) && !vlanmap.get(spvlanid).equals(HypervisorHostHelper.PvlanType.isolated)) {
|
||||
// This PVLAN ID is already setup as a non-isolated vlan id on the DVS. Throw an exception.
|
||||
String msg = "Specified secondary PVLAN ID " + spvlanid + " is already in use as a " + vlanmap.get(spvlanid).toString() + " VLAN in the DVSwitch";
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First create a DVSconfig spec.
|
||||
VMwareDVSConfigSpec dvsSpec = new VMwareDVSConfigSpec();
|
||||
// Next, add the required primary and secondary vlan config specs to the dvs config spec.
|
||||
|
||||
if (!vlanmap.containsKey(vid)) {
|
||||
VMwareDVSPvlanConfigSpec ppvlanConfigSpec = createDVPortPvlanConfigSpec(vid, vid, PvlanType.promiscuous, PvlanOperation.add);
|
||||
dvsSpec.getPvlanConfigSpec().add(ppvlanConfigSpec);
|
||||
}
|
||||
if (!vid.equals(spvlanid) && !vlanmap.containsKey(spvlanid)) {
|
||||
VMwareDVSPvlanConfigSpec spvlanConfigSpec = createDVPortPvlanConfigSpec(vid, spvlanid, PvlanType.isolated, PvlanOperation.add);
|
||||
PvlanType selectedType = StringUtils.isNotBlank(pvlanType) ? PvlanType.fromStr(pvlanType) : PvlanType.isolated;
|
||||
VMwareDVSPvlanConfigSpec spvlanConfigSpec = createDVPortPvlanConfigSpec(vid, spvlanid, selectedType, PvlanOperation.add);
|
||||
dvsSpec.getPvlanConfigSpec().add(spvlanConfigSpec);
|
||||
}
|
||||
|
||||
@ -1046,7 +1044,20 @@ public class HypervisorHostHelper {
|
||||
}
|
||||
|
||||
public enum PvlanType {
|
||||
promiscuous, isolated, community, // We don't use Community
|
||||
promiscuous, isolated, community;
|
||||
|
||||
public static PvlanType fromStr(String val) {
|
||||
if (StringUtils.isBlank(val)) {
|
||||
return null;
|
||||
} else if (val.equalsIgnoreCase("promiscuous")) {
|
||||
return promiscuous;
|
||||
} else if (val.equalsIgnoreCase("community")) {
|
||||
return community;
|
||||
} else if (val.equalsIgnoreCase("isolated")) {
|
||||
return isolated;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static VMwareDVSPvlanConfigSpec createDVPortPvlanConfigSpec(int vlanId, int secondaryVlanId, PvlanType pvlantype, PvlanOperation operation) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user