diff --git a/api/src/com/cloud/agent/api/to/NicTO.java b/api/src/com/cloud/agent/api/to/NicTO.java index bd681f282cd5..3863e1bafe72 100644 --- a/api/src/com/cloud/agent/api/to/NicTO.java +++ b/api/src/com/cloud/agent/api/to/NicTO.java @@ -16,7 +16,10 @@ // under the License. package com.cloud.agent.api.to; +import com.cloud.offering.NetworkOffering; + import java.util.List; +import java.util.Map; public class NicTO extends NetworkTO { int deviceId; @@ -26,6 +29,7 @@ public class NicTO extends NetworkTO { boolean pxeDisable; String nicUuid; List nicSecIps; + Map details; public NicTO() { super(); @@ -97,4 +101,12 @@ public String getNetworkUuid() { public void setNetworkUuid(String uuid) { super.setUuid(uuid); } + + public Map getDetails() { + return details; + } + + public void setDetails(final Map details) { + this.details = details; + } } diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java index f027cf9fe995..37746f0d0f7e 100644 --- a/api/src/com/cloud/network/Networks.java +++ b/api/src/com/cloud/network/Networks.java @@ -75,6 +75,10 @@ public URI toUri(T value) { throw new CloudRuntimeException("Unable to convert to broadcast URI: " + value); } } + @Override + public String getValueFrom(URI uri) { + return uri.getAuthority(); + } }, Vswitch("vs", String.class), LinkLocal(null, null), Vnet("vnet", Long.class), Storage("storage", Integer.class), Lswitch("lswitch", String.class) { @Override diff --git a/api/src/com/cloud/offering/NetworkOffering.java b/api/src/com/cloud/offering/NetworkOffering.java index 59045dccd97b..402846202e7e 100644 --- a/api/src/com/cloud/offering/NetworkOffering.java +++ b/api/src/com/cloud/offering/NetworkOffering.java @@ -38,7 +38,7 @@ public enum State { } public enum Detail { - InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription + InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits } public final static String SystemPublicNetwork = "System-Public-Network"; diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 582ead6693d6..1ec340df1e89 100644 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -38,6 +38,7 @@ public class ApiConstants { public static final String BIND_PASSWORD = "bindpass"; public static final String BYTES_READ_RATE = "bytesreadrate"; public static final String BYTES_WRITE_RATE = "byteswriterate"; + public static final String BYPASS_VLAN_OVERLAP_CHECK = "bypassvlanoverlapcheck"; public static final String CATEGORY = "category"; public static final String CAN_REVERT = "canrevert"; public static final String CA_CERTIFICATES = "cacertificates"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java b/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java index 6cf9e2308dcd..6d346e9a4dbe 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java @@ -40,6 +40,9 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd { @Parameter(name=ApiConstants.VLAN, type=CommandType.STRING, description="the ID or VID of the network") private String vlan; + @Parameter(name=ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type=CommandType.BOOLEAN, description="when true bypasses VLAN id/range overlap check during network creation for shared networks") + private Boolean bypassVlanOverlapCheck; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -48,6 +51,13 @@ public String getVlan() { return vlan; } + public Boolean getBypassVlanOverlapCheck() { + if (bypassVlanOverlapCheck != null) { + return bypassVlanOverlapCheck; + } + return false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java index d1a34185eede..259c4905ed16 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java @@ -113,7 +113,8 @@ public class CreateNetworkOfferingCmd extends BaseCmd { private Boolean isPersistent; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.2.0", description = "Network offering details in key/value pairs." - + " Supported keys are internallbprovider/publiclbprovider with service provider as a value") + + " Supported keys are internallbprovider/publiclbprovider with service provider as a value, and" + + " promiscuousmode/macaddresschanges/forgedtransmits with true/false as value to accept/reject the security settings if available for a nic/portgroup") protected Map details; @Parameter(name = ApiConstants.EGRESS_DEFAULT_POLICY, diff --git a/api/test/com/cloud/network/NetworksTest.java b/api/test/com/cloud/network/NetworksTest.java index c9102d3a02aa..ef5829243421 100644 --- a/api/test/com/cloud/network/NetworksTest.java +++ b/api/test/com/cloud/network/NetworksTest.java @@ -16,16 +16,16 @@ // under the License. package com.cloud.network; -import java.net.URISyntaxException; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - import com.cloud.dc.Vlan; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; import com.cloud.utils.exception.CloudRuntimeException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.net.URI; +import java.net.URISyntaxException; /** * @author dhoogland @@ -43,6 +43,13 @@ public void emptyBroadcastDomainTypeTest() throws URISyntaxException { Assert.assertEquals("an empty uri should mean a broadcasttype of undecided", BroadcastDomainType.UnDecided, type); } + @Test + public void vlanCommaSeparatedTest() throws URISyntaxException { + Assert.assertEquals(BroadcastDomainType.getValue(new URI("vlan://100")), "100"); + Assert.assertEquals(BroadcastDomainType.getValue(new URI("vlan://100-200")), "100-200"); + Assert.assertEquals(BroadcastDomainType.getValue(new URI("vlan://10-50,12,11,112-170")), "10-50,12,11,112-170"); + } + @Test public void vlanBroadcastDomainTypeTest() throws URISyntaxException { String uri1 = "vlan://1"; diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index 68531e3ab3d5..8a7a516f3d73 100644 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -77,6 +77,15 @@ public interface NetworkOrchestrationService { ConfigKey NetworkThrottlingRate = new ConfigKey("Network", Integer.class, NetworkThrottlingRateCK, "200", "Default data transfer rate in megabits per second allowed in network.", true, ConfigKey.Scope.Zone); + ConfigKey PromiscuousMode = new ConfigKey("Advanced", Boolean.class, "network.promiscuous.mode", "false", + "Whether to allow or deny promiscuous mode on nics for applicable network elements such as for vswitch/dvswitch portgroups.", true); + + ConfigKey MacAddressChanges = new ConfigKey("Advanced", Boolean.class, "network.mac.address.changes", "true", + "Whether to allow or deny mac address changes on nics for applicable network elements such as for vswitch/dvswitch porgroups.", true); + + ConfigKey ForgedTransmits = new ConfigKey("Advanced", Boolean.class, "network.forged.transmits", "true", + "Whether to allow or deny forged transmits on nics for applicable network elements such as for vswitch/dvswitch portgroups.", true); + List setupNetwork(Account owner, NetworkOffering offering, DeploymentPlan plan, String name, String displayText, boolean isDefault) throws ConcurrentOperationException; @@ -136,9 +145,9 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC boolean destroyNetwork(long networkId, ReservationContext context, boolean forced); - Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, - Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr, - Boolean displayNetworkEnabled, String isolatedPvlan) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; + 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) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; UserDataServiceProvider getPasswordResetProvider(Network network); diff --git a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java index 46be654f5819..eee159523cb2 100644 --- a/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/com/cloud/configuration/ConfigurationManager.java @@ -214,7 +214,7 @@ NetworkOfferingVO createNetworkOffering(String name, String displayText, Traffic Map details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive); Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) + String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException; void createDefaultSystemNetworks(long zoneId) throws ConcurrentOperationException; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 3956617859c1..755fba2f74c1 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -32,18 +32,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; + import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.network.dao.NetworkDetailsDao; -import com.cloud.network.dao.RemoteAccessVpnDao; -import com.cloud.network.dao.RemoteAccessVpnVO; -import com.cloud.network.dao.VpnUserDao; -import com.cloud.network.element.RedundantResource; -import com.cloud.network.router.VirtualRouter; -import com.cloud.vm.DomainRouterVO; -import com.cloud.vm.dao.DomainRouterDao; -import org.apache.log4j.Logger; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO; @@ -58,6 +50,8 @@ import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.region.PortableIpDao; +import org.apache.log4j.Logger; + import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; @@ -129,6 +123,7 @@ import com.cloud.network.dao.NetworkAccountDao; import com.cloud.network.dao.NetworkAccountVO; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.NetworkDomainDao; import com.cloud.network.dao.NetworkDomainVO; import com.cloud.network.dao.NetworkServiceMapDao; @@ -139,17 +134,22 @@ import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.RemoteAccessVpnDao; +import com.cloud.network.dao.RemoteAccessVpnVO; +import com.cloud.network.dao.VpnUserDao; import com.cloud.network.element.AggregatedCommandExecutor; import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.DnsServiceProvider; import com.cloud.network.element.IpDeployer; import com.cloud.network.element.LoadBalancingServiceProvider; import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.RedundantResource; import com.cloud.network.element.StaticNatServiceProvider; import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.guru.NetworkGuruAdditionalFunctions; import com.cloud.network.lb.LoadBalancingRulesManager; +import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; @@ -197,6 +197,7 @@ import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.NetUtils; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.Nic; import com.cloud.vm.Nic.ReservationStrategy; import com.cloud.vm.NicIpAlias; @@ -209,6 +210,7 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.NicIpAliasVO; @@ -2017,9 +2019,9 @@ public void expungeNics(final VirtualMachineProfile vm) { @Override @DB public Network createGuestNetwork(final long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId, - 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) - throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + 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) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); // this method supports only guest network creation @@ -2136,19 +2138,19 @@ public Network createGuestNetwork(final long networkOfferingId, final String nam } if (vlanSpecified) { + URI uri = BroadcastDomainType.fromString(vlanId); //don't allow to specify vlan tag used by physical network for dynamic vlan allocation - if (_dcDao.findVnet(zoneId, pNtwk.getId(), vlanId).size() > 0) { + 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 (! UuidUtils.validateUUID(vlanId)){ - final String uri = BroadcastDomainType.fromString(vlanId).toString(); // For Isolated networks, don't allow to create network with vlan that already exists in the zone if (ntwkOff.getGuestType() == GuestType.Isolated) { - if (_networksDao.countByZoneAndUri(zoneId, uri) > 0) { - throw new InvalidParameterValueException("Network with vlan " + vlanId + " already exists in zone " + zoneId); + 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 { - final List dcVnets = _datacenterVnetDao.findVnet(zoneId, vlanId.toString()); + final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); //for the network that is created as part of private gateway, //the vnet is not coming from the data center vnet table, so the list can be empty if (!dcVnets.isEmpty()) { @@ -2177,8 +2179,8 @@ public Network createGuestNetwork(final long networkOfferingId, final String nam } else { // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or // shared network with same Vlan ID in the zone - if (_networksDao.countByZoneUriAndGuestType(zoneId, uri, GuestType.Isolated) > 0 ) { - throw new InvalidParameterValueException("There is a isolated/shared network with vlan id: " + vlanId + " already exists " + "in zone " + zoneId); + if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0 ) { + throw new InvalidParameterValueException("There is an existing isolated/shared network that overlaps with vlan id:" + vlanId + " in zone " + zoneId); } } } @@ -3663,6 +3665,8 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion}; + return new ConfigKey[] {NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, + GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion, + PromiscuousMode, MacAddressChanges, ForgedTransmits}; } } diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDao.java b/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDao.java index 1783231f4fbb..556ab4513911 100644 --- a/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDao.java +++ b/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDao.java @@ -27,10 +27,10 @@ public interface DataCenterVnetDao extends GenericDao { public List listAllocatedVnetsInRange(long dcId, long physicalNetworkId, Integer start, Integer end); - public List findVnet(long dcId, String vnet); - public int countZoneVlans(long dcId, boolean onlyCountAllocated); + public List findVnet(long dcId, String vnet); + public List findVnet(long dcId, long physicalNetworkId, String vnet); public void add(long dcId, long physicalNetworkId, List vnets); diff --git a/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDaoImpl.java b/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDaoImpl.java index 3205fcd6af64..1c29e6a944c6 100644 --- a/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDaoImpl.java +++ b/engine/schema/src/com/cloud/dc/dao/DataCenterVnetDaoImpl.java @@ -18,6 +18,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @@ -30,6 +31,8 @@ import com.cloud.dc.DataCenterVnetVO; import com.cloud.network.dao.AccountGuestVlanMapDao; import com.cloud.network.dao.AccountGuestVlanMapVO; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.UriUtils; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -99,15 +102,6 @@ public void lockRange(long dcId, long physicalNetworkId, Integer start, Integer lockRows(sc, null, true); } - @Override - public List findVnet(long dcId, String vnet) { - SearchCriteria sc = VnetDcSearch.create(); - ; - sc.setParameters("dc", dcId); - sc.setParameters("vnet", vnet); - return listBy(sc); - } - @Override public int countZoneVlans(long dcId, boolean onlyCountAllocated) { SearchCriteria sc = onlyCountAllocated ? countAllocatedZoneVlans.create() : countZoneVlans.create(); @@ -115,14 +109,37 @@ public int countZoneVlans(long dcId, boolean onlyCountAllocated) { return customSearch(sc, null).get(0); } - @Override - public List findVnet(long dcId, long physicalNetworkId, String vnet) { + private List findOverlappingVnets(final long dcId, final Long physicalNetworkId, final String vnet) { + final List searchVnets = UriUtils.expandVlanUri(vnet); + final List overlappingVnets = new ArrayList<>(); + if (searchVnets == null || searchVnets.isEmpty()) { + return overlappingVnets; + } SearchCriteria sc = VnetDcSearch.create(); sc.setParameters("dc", dcId); - sc.setParameters("physicalNetworkId", physicalNetworkId); - sc.setParameters("vnet", vnet); + if (physicalNetworkId != null) { + sc.setParameters("physicalNetworkId", physicalNetworkId); + } + for (final DataCenterVnetVO dcVNet : listBy(sc)) { + if (dcVNet == null || dcVNet.getVnet() == null) { + continue; + } + final Integer vnetValue = NumbersUtil.parseInt(dcVNet.getVnet(), -1); + if (vnetValue != -1 && searchVnets.contains(vnetValue)) { + overlappingVnets.add(dcVNet); + } + } + return overlappingVnets; + } - return listBy(sc); + @Override + public List findVnet(long dcId, String vnet) { + return findOverlappingVnets(dcId, null, vnet); + } + + @Override + public List findVnet(long dcId, long physicalNetworkId, String vnet) { + return findOverlappingVnets(dcId, physicalNetworkId, vnet); } @Override diff --git a/engine/schema/src/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/com/cloud/network/dao/NetworkDao.java index 85544e78901a..5091ebd75df0 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkDao.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkDao.java @@ -61,9 +61,7 @@ public interface NetworkDao extends GenericDao, StateDao listBy(long accountId, long networkId); - long countByZoneAndUri(long zoneId, String broadcastUri); - - long countByZoneUriAndGuestType(long zoneId, String broadcastUri, GuestType guestType); + List listByZoneAndUriAndGuestType(long zoneId, String broadcastUri, GuestType guestType); List listByZone(long zoneId); diff --git a/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java index 49a5944f838b..11444b0d008a 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.network.dao; +import java.net.URI; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; @@ -24,9 +26,8 @@ import javax.inject.Inject; import javax.persistence.TableGenerator; -import org.springframework.stereotype.Component; - import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.springframework.stereotype.Component; import com.cloud.network.Network; import com.cloud.network.Network.Event; @@ -42,6 +43,7 @@ import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.utils.UriUtils; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; @@ -71,7 +73,6 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne GenericSearchBuilder NetworksRegularUserCanCreateSearch; GenericSearchBuilder NetworksCount; SearchBuilder SourceNATSearch; - GenericSearchBuilder CountByZoneAndURI; GenericSearchBuilder VpcNetworksCount; SearchBuilder OfferingAccountNetworkSearch; @@ -152,14 +153,6 @@ protected void init() { ZoneBroadcastUriSearch.and("guestType", ZoneBroadcastUriSearch.entity().getGuestType(), Op.EQ); ZoneBroadcastUriSearch.done(); - CountByZoneAndURI = createSearchBuilder(Long.class); - CountByZoneAndURI.select(null, Func.COUNT, null); - CountByZoneAndURI.and("dataCenterId", CountByZoneAndURI.entity().getDataCenterId(), Op.EQ); - CountByZoneAndURI.and("broadcastUri", CountByZoneAndURI.entity().getBroadcastUri(), Op.EQ); - CountByZoneAndURI.and("guestType", CountByZoneAndURI.entity().getGuestType(), Op.EQ); - - CountByZoneAndURI.done(); - ZoneSecurityGroupSearch = createSearchBuilder(); ZoneSecurityGroupSearch.and("dataCenterId", ZoneSecurityGroupSearch.entity().getDataCenterId(), Op.EQ); final SearchBuilder offJoin = _ntwkSvcMap.createSearchBuilder(); @@ -399,13 +392,35 @@ public List listBy(final long accountId, final long networkId) { } @Override - public long countByZoneAndUri(final long zoneId, final String broadcastUri) { + public List listByZoneAndUriAndGuestType(long zoneId, String broadcastUri, GuestType guestType) { + final URI searchUri = BroadcastDomainType.fromString(broadcastUri); + final String searchRange = BroadcastDomainType.getValue(searchUri); + final List searchVlans = UriUtils.expandVlanUri(searchRange); + final List overlappingNetworks = new ArrayList<>(); - final SearchCriteria sc = CountByZoneAndURI.create(); + final SearchCriteria sc = ZoneBroadcastUriSearch.create(); sc.setParameters("dataCenterId", zoneId); - sc.setParameters("broadcastUri", broadcastUri); + if (guestType != null) { + sc.setParameters("guestType", guestType); + } - return customSearch(sc, null).get(0); + for (final NetworkVO network : listBy(sc)) { + if (network.getBroadcastUri() == null || !network.getBroadcastUri().getScheme().equalsIgnoreCase(searchUri.getScheme())) { + continue; + } + final String networkVlanRange = BroadcastDomainType.getValue(network.getBroadcastUri()); + if (networkVlanRange == null || networkVlanRange.isEmpty()) { + continue; + } + for (final Integer networkVlan : UriUtils.expandVlanUri(networkVlanRange)) { + if (searchVlans.contains(networkVlan)) { + overlappingNetworks.add(network); + break; + } + } + } + + return overlappingNetworks; } @Override @@ -415,15 +430,6 @@ public List listByZone(final long zoneId) { return search(sc, null); } - @Override - public long countByZoneUriAndGuestType(final long zoneId, final String broadcastUri, final GuestType guestType) { - final SearchCriteria sc = CountByZoneAndURI.create(); - sc.setParameters("dataCenterId", zoneId); - sc.setParameters("broadcastUri", broadcastUri); - sc.setParameters("guestType", guestType); - return customSearch(sc, null).get(0); - } - @Override public List listByZoneSecurityGroup(final Long zoneId) { final SearchCriteria sc = ZoneSecurityGroupSearch.create(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 53e3239113e2..b7149ab2bf9e 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -392,11 +392,11 @@ private void prepareHost(HostMO hostMo, String privateTrafficLabel) throws Excep VirtualSwitchType vsType = VirtualSwitchType.getType(vSwitchType); //The management network is probably always going to be a physical network with islation type of vlans, so assume BroadcastDomainType VLAN if (VirtualSwitchType.StandardVirtualSwitch == vsType) { - HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, 180000, false, BroadcastDomainType.Vlan, null); + HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, 180000, false, BroadcastDomainType.Vlan, null, null); } else { HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, null, 180000, - vsType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, null); + vsType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, null, null); } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 17064ff0d1e7..9d32f3424dee 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1178,11 +1178,11 @@ private void plugPublicNic(VirtualMachineMO vmMo, final String vlanId, final Str if (VirtualSwitchType.StandardVirtualSwitch == vSwitchType) { networkInfo = HypervisorHostHelper.prepareNetwork(_publicTrafficInfo.getVirtualSwitchName(), "cloud.public", vmMo.getRunningHost(), vlanId, null, null, - _opsTimeout, true, BroadcastDomainType.Vlan, null); + _opsTimeout, true, BroadcastDomainType.Vlan, null, null); } else { networkInfo = HypervisorHostHelper.prepareNetwork(_publicTrafficInfo.getVirtualSwitchName(), "cloud.public", vmMo.getRunningHost(), vlanId, null, null, null, - _opsTimeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, _vsmCredentials); + _opsTimeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, _vsmCredentials, null); } int nicIndex = allocPublicNicIndex(vmMo); @@ -3032,7 +3032,7 @@ private Pair prepareNetworkFromNicInfo(HostMO ho if (VirtualSwitchType.StandardVirtualSwitch == switchType) { networkInfo = HypervisorHostHelper.prepareNetwork(switchName, namePrefix, hostMo, getVlanInfo(nicTo, vlanToken), nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), - _opsTimeout, !namePrefix.startsWith("cloud.private"), nicTo.getBroadcastType(), nicTo.getUuid()); + _opsTimeout, true, nicTo.getBroadcastType(), nicTo.getUuid(), nicTo.getDetails()); } else { String vlanId = getVlanInfo(nicTo, vlanToken); @@ -3047,7 +3047,7 @@ private Pair prepareNetworkFromNicInfo(HostMO ho } networkInfo = HypervisorHostHelper.prepareNetwork(switchName, namePrefix, hostMo, vlanId, svlanId, nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), _opsTimeout, switchType, - _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType(), _vsmCredentials); + _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType(), _vsmCredentials, nicTo.getDetails()); } return networkInfo; diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 3d2c236c39c4..4dd526323383 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -199,6 +199,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; +import com.cloud.utils.UriUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.EntityManager; @@ -2893,7 +2894,7 @@ public Vlan doInTransaction(final TransactionStatus status) { newVlanNetmask = sameSubnet.second().second(); } final Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, - domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + false, domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); // create an entry in the nic_secondary table. This will be the new // gateway that will be configured on the corresponding routervm. return vlan; @@ -3010,7 +3011,7 @@ public boolean hasSameSubnet(boolean ipv4, String vlanGateway, String vlanNetmas @Override @DB public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final boolean forVirtualNetwork, final Long podId, final String startIP, final String endIP, - final String vlanGateway, final String vlanNetmask, String vlanId, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr) { + final String vlanGateway, final String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr) { final Network network = _networkModel.getNetwork(networkId); boolean ipv4 = false, ipv6 = false; @@ -3077,8 +3078,10 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final String[] vlan = uri.toString().split("vlan:\\/\\/"); networkVlanId = vlan[1]; // For pvlan - networkVlanId = networkVlanId.split("-")[0]; - } + if (network.getBroadcastDomainType() != BroadcastDomainType.Vlan) { + networkVlanId = networkVlanId.split("-")[0]; + } + } } if (vlanId != null && !connectivityWithoutVlan) { @@ -3168,7 +3171,9 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, continue; } // from here, subnet overlaps - if ( !vlanId.equals(vlan.getVlanTag()) ) { + if (!UriUtils.checkVlanUriOverlap( + BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId)), + BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlan.getVlanTag())))) { boolean overlapped = false; if( network.getTrafficType() == TrafficType.Public ) { overlapped = true; @@ -3239,7 +3244,7 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, } // Check if the vlan is being used - if (_zoneDao.findVnet(zoneId, physicalNetworkId, vlanId).size() > 0) { + if (!bypassVlanOverlapCheck && _zoneDao.findVnet(zoneId, physicalNetworkId, BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId))).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()); } diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index d84c104f76be..0bf1c7057599 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -22,6 +22,7 @@ import javax.inject.Inject; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.log4j.Logger; import com.cloud.agent.api.Command; @@ -29,9 +30,12 @@ import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.gpu.GPU; +import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; +import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; +import com.cloud.offerings.dao.NetworkOfferingDetailsDao; import com.cloud.resource.ResourceManager; import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.dao.ServiceOfferingDao; @@ -48,7 +52,6 @@ import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; -import com.cloud.network.Networks.BroadcastDomainType; public abstract class HypervisorGuruBase extends AdapterBase implements HypervisorGuru { public static final Logger s_logger = Logger.getLogger(HypervisorGuruBase.class); @@ -58,6 +61,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis @Inject private NetworkDao _networkDao; @Inject + private NetworkOfferingDetailsDao networkOfferingDetailsDao; + @Inject private VMInstanceDao _virtualMachineDao; @Inject private UserVmDetailsDao _userVmDetailsDao; @@ -138,7 +143,18 @@ protected VirtualMachineTO toVirtualMachineTO(VirtualMachineProfile vmProfile) { if(vm.getType() == VirtualMachine.Type.NetScalerVm) { nicProfile.setBroadcastType(BroadcastDomainType.Native); } - nics[i++] = toNicTO(nicProfile); + NicTO nicTo = toNicTO(nicProfile); + final NetworkVO network = _networkDao.findByUuid(nicTo.getNetworkUuid()); + if (network != null) { + final Map details = networkOfferingDetailsDao.getNtwkOffDetails(network.getNetworkOfferingId()); + if (details != null) { + details.putIfAbsent(NetworkOffering.Detail.PromiscuousMode, NetworkOrchestrationService.PromiscuousMode.value().toString()); + details.putIfAbsent(NetworkOffering.Detail.MacAddressChanges, NetworkOrchestrationService.MacAddressChanges.value().toString()); + details.putIfAbsent(NetworkOffering.Detail.ForgedTransmits, NetworkOrchestrationService.ForgedTransmits.value().toString()); + } + nicTo.setDetails(details); + } + nics[i++] = nicTo; } to.setNics(nics); diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index 9056ed5cdfce..e34f90861c38 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -1631,7 +1631,7 @@ public Ternary, Network> doInTransaction(Transa 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, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null); + + "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, 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 " diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 36dd53fe33e0..966c0e4475b3 100644 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -1038,9 +1038,14 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac String netmask = cmd.getNetmask(); String networkDomain = cmd.getNetworkDomain(); String vlanId = null; + boolean bypassVlanOverlapCheck = false; if (cmd instanceof CreateNetworkCmdByAdmin) { vlanId = ((CreateNetworkCmdByAdmin)cmd).getVlan(); } + if (cmd instanceof CreateNetworkCmdByAdmin) { + bypassVlanOverlapCheck = ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck(); + } + String name = cmd.getNetworkName(); String displayText = cmd.getDisplayText(); Account caller = CallContext.current().getCallingAccount(); @@ -1259,8 +1264,8 @@ && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { } // Don't allow to specify vlan if the caller is not ROOT admin - if (!_accountMgr.isRootAdmin(caller.getId()) && (ntwkOff.getSpecifyVlan() || vlanId != null)) { - throw new InvalidParameterValueException("Only ROOT admin is allowed to specify vlanId"); + if (!_accountMgr.isRootAdmin(caller.getId()) && (ntwkOff.getSpecifyVlan() || vlanId != null || bypassVlanOverlapCheck)) { + throw new InvalidParameterValueException("Only ROOT admin is allowed to specify vlanId or bypass vlan overlap check"); } if (ipv4) { @@ -1319,7 +1324,7 @@ && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { throw ex; } - Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, name, displayText, caller, physicalNetworkId, zoneId, domainId, + 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); @@ -1351,10 +1356,10 @@ && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { } 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 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) throws InsufficientCapacityException, ResourceAllocationException { + 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) throws InsufficientCapacityException, ResourceAllocationException { try { Network network = Transaction.execute(new TransactionCallbackWithException() { @Override @@ -1408,14 +1413,14 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa throw new InvalidParameterValueException("Internal Lb can be enabled on vpc networks only"); } - network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, zoneId, + network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, sharedDomainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan); } if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) { // Create vlan ip range _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, null, startIP, endIP, gateway, netmask, vlanId, - null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); } return network; } @@ -4079,7 +4084,7 @@ public Network doInTransaction(TransactionStatus status) throws ResourceAllocati Network privateNetwork = _networksDao.getPrivateNetwork(uriString, cidr, networkOwnerId, pNtwk.getDataCenterId(), networkOfferingId); if (privateNetwork == null) { //create Guest network - privateNetwork = _networkMgr.createGuestNetwork(ntwkOffFinal.getId(), networkName, displayText, gateway, cidr, uriString, null, owner, null, pNtwk, + 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); if (privateNetwork != null) { s_logger.debug("Successfully created guest network " + privateNetwork); diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 706f4c4abff6..9f5ff834c06f 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -2359,7 +2359,7 @@ public Network createVpcGuestNetwork(final long ntwkOffId, final String name, fi validateNtwkOffForNtwkInVpc(null, ntwkOffId, cidr, networkDomain, vpc, gateway, owner, aclId); // 2) Create network - final Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, domainId, pNtwk, zoneId, aclType, + final Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, false, networkDomain, owner, domainId, pNtwk, zoneId, aclType, subdomainAccess, vpcId, null, null, isDisplayNetworkEnabled, null); if (guestNetwork != null) { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index e4fc56fe6aff..07dd0ea8466d 100644 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3103,7 +3103,7 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv } 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, 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); if (newNetwork != null) { defaultNetwork = _networkDao.findById(newNetwork.getId()); } @@ -5723,7 +5723,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { s_logger.debug("Creating network for account " + newAccount + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network", - newAccount.getAccountName() + "-network", null, null, null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, + newAccount.getAccountName() + "-network", null, null, null, false, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null); // if the network offering has persistent set to true, implement the network if (requiredOfferings.get(0).getIsPersistent()) { diff --git a/server/test/com/cloud/network/CreatePrivateNetworkTest.java b/server/test/com/cloud/network/CreatePrivateNetworkTest.java index 8a7b54cb20fc..178ed84d7487 100644 --- a/server/test/com/cloud/network/CreatePrivateNetworkTest.java +++ b/server/test/com/cloud/network/CreatePrivateNetworkTest.java @@ -121,7 +121,7 @@ public void setup() throws Exception { new NetworkVO(1L, TrafficType.Guest, Mode.None, BroadcastDomainType.Vlan, 1L, 1L, 1L, 1L, "bla", "fake", "eet.net", GuestType.Isolated, 1L, 1L, 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"), anyString(), anyString(), + 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())).thenReturn(net); diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java index bcee896ca30e..9ac0648a5331 100644 --- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -440,7 +440,7 @@ public NetworkOfferingVO createNetworkOffering(String name, String displayText, */ @Override public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6) + String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException { // TODO Auto-generated method stub return null; diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index f6f818d17339..9b895c255413 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -611,9 +611,9 @@ public boolean destroyNetwork(long networkId, ReservationContext context, boolea * @see com.cloud.network.NetworkManager#createGuestNetwork(long, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, com.cloud.user.Account, java.lang.Long, com.cloud.network.PhysicalNetwork, long, org.apache.cloudstack.acl.ControlledEntity.ACLType, java.lang.Boolean, java.lang.Long) */ @Override - public Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, - Account owner, Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String gatewayv6, - String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan ) throws ConcurrentOperationException, InsufficientCapacityException, + 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) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { // TODO Auto-generated method stub return null; diff --git a/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java b/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java index 4c4a0a190262..11f3f81f357d 100644 --- a/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/test/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -85,13 +85,8 @@ public List listBy(final long accountId, final long networkId) { } @Override - public long countByZoneAndUri(final long zoneId, final String broadcastUri) { - return 0; - } - - @Override - public long countByZoneUriAndGuestType(final long zoneId, final String broadcastUri, final GuestType guestType) { - return 0; + public List listByZoneAndUriAndGuestType(long zoneId, String broadcastUri, GuestType guestType) { + return null; } @Override diff --git a/ui/l10n/en.js b/ui/l10n/en.js index 16e0b8894ef0..3727dc6a3130 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -761,6 +761,7 @@ var dictionary = {"ICMP.code":"ICMP Code", "label.firewall":"Firewall", "label.first.name":"First Name", "label.firstname.lower":"firstname", +"label.forged.transmits":"Forged Transmits", "label.format":"Format", "label.format.lower":"format", "label.friday":"Friday", @@ -991,6 +992,7 @@ var dictionary = {"ICMP.code":"ICMP Code", "label.management":"Management", "label.management.ips":"Management IP Addresses", "label.management.server":"Management Server", +"label.mac.address.changes":"MAC Address Changes", "label.max.cpus":"Max. CPU cores", "label.max.guest.limit":"Max guest limit", "label.max.instances":"Max Instances", @@ -1299,6 +1301,7 @@ var dictionary = {"ICMP.code":"ICMP Code", "label.project.name":"Project name", "label.project.view":"Project View", "label.projects":"Projects", +"label.promiscuous.mode":"Promiscuous Mode", "label.protocol":"Protocol", "label.protocol.number":"Protocol Number", "label.provider":"Provider", diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 8fb7ebefb7af..6c3ffd81687f 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -2811,6 +2811,60 @@ } }, + promiscuousMode: { + label: 'label.promiscuous.mode', + select: function(args) { + args.response.success({ + data: [{ + id: '', + description: '' + }, { + id: 'true', + description: 'Accept' + }, { + id: 'false', + description: 'Reject' + }] + }); + } + }, + + macAddressChanges: { + label: 'label.mac.address.changes', + select: function(args) { + args.response.success({ + data: [{ + id: '', + description: '' + }, { + id: 'true', + description: 'Accept' + }, { + id: 'false', + description: 'Reject' + }] + }); + } + }, + + forgedTransmits: { + label: 'label.forged.transmits', + select: function(args) { + args.response.success({ + data: [{ + id: '', + description: '' + }, { + id: 'true', + description: 'Accept' + }, { + id: 'false', + description: 'Reject' + }] + }); + } + }, + supportedServices: { label: 'label.supported.services', @@ -3341,6 +3395,22 @@ delete inputData.egressdefaultpolicy; } + if ("promiscuousMode" in inputData) { + inputData['details[0].promiscuousMode'] = inputData.promiscuousMode; + delete inputData.promiscuousMode; + } + + if ("macAddressChanges" in inputData) { + inputData['details[0].macAddressChanges'] = inputData.macAddressChanges; + delete inputData.macAddressChanges; + } + + if ("forgedTransmits" in inputData) { + inputData['details[0].forgedTransmits'] = inputData.forgedTransmits; + delete inputData.forgedTransmits; + } + + if (args.$form.find('.form-item[rel=serviceofferingid]').css("display") == "none") delete inputData.serviceofferingid; @@ -3639,6 +3709,9 @@ }, tags: { label: 'label.tags' + }, + details: { + label: 'label.details' } }], @@ -3649,9 +3722,16 @@ async: true, success: function(json) { var item = json.listnetworkofferingsresponse.networkoffering[0]; + if (!item.hasOwnProperty('details')) { + item.details = {}; + } args.response.success({ actionFilter: networkOfferingActionfilter, data: $.extend(item, { + details: $.map(item.details, function(val, key) { + return key + "=" + val; + }).join(', '), + supportedServices: $.map(item.service, function(service) { return service.name; }).join(', '), diff --git a/utils/src/main/java/com/cloud/utils/UriUtils.java b/utils/src/main/java/com/cloud/utils/UriUtils.java index 631c629aed3a..8805891cd2ac 100644 --- a/utils/src/main/java/com/cloud/utils/UriUtils.java +++ b/utils/src/main/java/com/cloud/utils/UriUtils.java @@ -30,6 +30,7 @@ import java.net.URLEncoder; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.StringTokenizer; @@ -55,6 +56,7 @@ import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.exception.CloudRuntimeException; +import com.google.common.base.Strings; public class UriUtils { @@ -391,4 +393,52 @@ public static InputStream getInputStreamFromUrl(String url, String user, String return null; } } + + /** + * Expands a given vlan URI to a list of vlan IDs + * @param vlanAuthority the URI part without the vlan:// scheme + * @return returns list of vlan integer ids + */ + public static List expandVlanUri(final String vlanAuthority) { + final List expandedVlans = new ArrayList<>(); + if (Strings.isNullOrEmpty(vlanAuthority)) { + return expandedVlans; + } + for (final String vlanPart: vlanAuthority.split(",")) { + if (Strings.isNullOrEmpty(vlanPart)) { + continue; + } + final String[] range = vlanPart.split("-"); + if (range.length == 2) { + Integer start = NumbersUtil.parseInt(range[0], -1); + Integer end = NumbersUtil.parseInt(range[1], -1); + if (start <= end && end > -1 && start > -1) { + while (start <= end) { + expandedVlans.add(start++); + } + } + } else { + final Integer value = NumbersUtil.parseInt(range[0], -1); + if (value > -1) { + expandedVlans.add(value); + } + } + } + return expandedVlans; + } + + /** + * Checks if given vlan URI authorities overlap + * @param vlanRange1 + * @param vlanRange2 + * @return true if they overlap + */ + public static boolean checkVlanUriOverlap(final String vlanRange1, final String vlanRange2) { + final List vlans1 = expandVlanUri(vlanRange1); + final List vlans2 = expandVlanUri(vlanRange2); + if (vlans1 == null || vlans2 == null) { + return true; + } + return !Collections.disjoint(vlans1, vlans2); + } } diff --git a/utils/src/test/java/com/cloud/utils/UriUtilsTest.java b/utils/src/test/java/com/cloud/utils/UriUtilsTest.java index d2fd9976558c..b8d951db340e 100644 --- a/utils/src/test/java/com/cloud/utils/UriUtilsTest.java +++ b/utils/src/test/java/com/cloud/utils/UriUtilsTest.java @@ -20,9 +20,12 @@ package com.cloud.utils; import junit.framework.Assert; - import org.junit.Test; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public class UriUtilsTest { @Test public void encodeURIComponent() { @@ -57,4 +60,45 @@ public void getUpdateUri() { //XXX: Interesting cases not covered: // * port is ignored and left out from the return value } + + @Test + public void expandVlanEmpty() { + List vlans = UriUtils.expandVlanUri(""); + Assert.assertTrue(vlans.size() == 0); + } + + @Test + public void expandVlanSingleValue() { + List vlans = UriUtils.expandVlanUri("10"); + Assert.assertTrue(vlans.size() == 1); + Assert.assertEquals(vlans, Collections.singletonList(10)); + } + + @Test + public void expandVlanValidRange() { + List vlans = UriUtils.expandVlanUri("10-12,14,17,40-43"); + Assert.assertEquals(vlans, Arrays.asList(10,11,12,14,17,40,41,42,43)); + } + + @Test + public void expandVlanInvalidRange() { + List vlans = UriUtils.expandVlanUri("10-,12-14,-4,5-2"); + Assert.assertEquals(vlans, Arrays.asList(10,12,13,14)); + } + + @Test + public void testVlanUriOverlap() { + Assert.assertTrue(UriUtils.checkVlanUriOverlap("10-30,45,50,12,31", "10")); + Assert.assertTrue(UriUtils.checkVlanUriOverlap("10-30,45,50,12,31", "32,33-44,30-31")); + Assert.assertTrue(UriUtils.checkVlanUriOverlap("10-30", "25-35")); + } + + @Test + public void testVlanUriNoOverlap() { + Assert.assertFalse(UriUtils.checkVlanUriOverlap("10-30,45,50,12,31", null)); + Assert.assertFalse(UriUtils.checkVlanUriOverlap("10-30,45,50,12,31", "")); + Assert.assertFalse(UriUtils.checkVlanUriOverlap("10-30,45,50,12,31", "32")); + Assert.assertFalse(UriUtils.checkVlanUriOverlap("10,22,111", "12")); + Assert.assertFalse(UriUtils.checkVlanUriOverlap("100-200", "30-40,50,201-250")); + } } diff --git a/vmware-base/pom.xml b/vmware-base/pom.xml index 44681e9a8b3f..01cfb3a52f14 100644 --- a/vmware-base/pom.xml +++ b/vmware-base/pom.xml @@ -42,6 +42,11 @@ cloud-api ${project.version} + + org.apache.cloudstack + cloud-engine-api + ${project.version} + com.google.code.gson gson diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index ef3f0ae327ca..bece91a98f55 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -16,11 +16,45 @@ // under the License. package com.cloud.hypervisor.vmware.mo; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.DocumentTraversal; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; +import org.xml.sax.SAXException; + import com.cloud.exception.CloudException; import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.hypervisor.vmware.util.VmwareHelper; import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.offering.NetworkOffering; import com.cloud.utils.ActionDelegate; +import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.cisco.n1kv.vsm.NetconfHelper; import com.cloud.utils.cisco.n1kv.vsm.PolicyMap; @@ -54,6 +88,7 @@ import com.vmware.vim25.LongPolicy; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.MethodFault; +import com.vmware.vim25.NumericRange; import com.vmware.vim25.ObjectContent; import com.vmware.vim25.OvfCreateImportSpecParams; import com.vmware.vim25.OvfCreateImportSpecResult; @@ -79,35 +114,9 @@ import com.vmware.vim25.VirtualSCSIController; import com.vmware.vim25.VirtualSCSISharing; import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec; +import com.vmware.vim25.VmwareDistributedVirtualSwitchTrunkVlanSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanSpec; -import org.apache.log4j.Logger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.traversal.DocumentTraversal; -import org.w3c.dom.traversal.NodeFilter; -import org.w3c.dom.traversal.NodeIterator; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.StringWriter; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; public class HypervisorHostHelper { private static final Logger s_logger = Logger.getLogger(HypervisorHostHelper.class); @@ -452,13 +461,14 @@ public static void updatePortProfile(VmwareContext context, String ethPortProfil * @param timeOutMs * @param vSwitchType * @param numPorts + * @param details * @return * @throws Exception */ public static Pair prepareNetwork(String physicalNetwork, String namePrefix, HostMO hostMo, String vlanId, String secondaryvlanId, - Integer networkRateMbps, Integer networkRateMulticastMbps, long timeOutMs, VirtualSwitchType vSwitchType, int numPorts, String gateway, - boolean configureVServiceInNexus, BroadcastDomainType broadcastDomainType, Map vsmCredentials) throws Exception { + Integer networkRateMbps, Integer networkRateMulticastMbps, long timeOutMs, VirtualSwitchType vSwitchType, int numPorts, String gateway, + boolean configureVServiceInNexus, BroadcastDomainType broadcastDomainType, Map vsmCredentials, Map details) throws Exception { ManagedObjectReference morNetwork = null; VmwareContext context = hostMo.getContext(); ManagedObjectReference dcMor = hostMo.getHyperHostDatacenter(); @@ -501,12 +511,18 @@ public static Pair prepareNetwork(String physica // No doubt about this, depending on vid=null to avoid lots of code below vid = null; } else { + if (vlanId != null) { + vlanId = vlanId.replace("vlan://", ""); + } networkName = composeCloudNetworkName(namePrefix, vlanId, secondaryvlanId, networkRateMbps, physicalNetwork); - if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) { + if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId) && !StringUtils.containsAny(vlanId, ",-")) { createGCTag = true; vid = Integer.parseInt(vlanId); } + if (vlanId != null && StringUtils.containsAny(vlanId, ",-")) { + createGCTag = true; + } if (secondaryvlanId != null) { spvlanid = Integer.parseInt(secondaryvlanId); } @@ -544,7 +560,7 @@ public static Pair prepareNetwork(String physica dvSwitchMo = new DistributedVirtualSwitchMO(context, morDvSwitch); shapingPolicy = getDVSShapingPolicy(networkRateMbps); - secPolicy = createDVSSecurityPolicy(); + secPolicy = createDVSSecurityPolicy(details); // First, if both vlan id and pvlan id are provided, we need to // reconfigure the DVSwitch to have a tuple of @@ -562,7 +578,7 @@ public static Pair prepareNetwork(String physica portGroupPolicy.setPortConfigResetAtDisconnect(true); } // Next, create the port group. For this, we need to create a VLAN spec. - createPortGroup(physicalNetwork, networkName, vid, spvlanid, dataCenterMo, shapingPolicy, secPolicy, portGroupPolicy, dvSwitchMo, numPorts, autoExpandSupported); + createPortGroup(physicalNetwork, networkName, vlanId, vid, spvlanid, dataCenterMo, shapingPolicy, secPolicy, portGroupPolicy, dvSwitchMo, numPorts, autoExpandSupported); bWaitPortGroupReady = true; } } else if (vSwitchType == VirtualSwitchType.NexusDistributedVirtualSwitch) { @@ -699,8 +715,8 @@ private static void setupPVlanPair(DistributedVirtualSwitchMO dvSwitchMo, Manage } - private static void createPortGroup(String physicalNetwork, String networkName, Integer vid, Integer spvlanid, DatacenterMO dataCenterMo, - DVSTrafficShapingPolicy shapingPolicy, DVSSecurityPolicy secPolicy, VMwareDVSPortgroupPolicy portGroupPolicy, DistributedVirtualSwitchMO dvSwitchMo, int numPorts, boolean autoExpandSupported) + private static void createPortGroup(String physicalNetwork, String networkName, String vlanRange, Integer vid, Integer spvlanid, DatacenterMO dataCenterMo, + DVSTrafficShapingPolicy shapingPolicy, DVSSecurityPolicy secPolicy, VMwareDVSPortgroupPolicy portGroupPolicy, DistributedVirtualSwitchMO dvSwitchMo, int numPorts, boolean autoExpandSupported) throws Exception { VmwareDistributedVirtualSwitchVlanSpec vlanSpec = null; VmwareDistributedVirtualSwitchPvlanSpec pvlanSpec = null; @@ -710,7 +726,7 @@ private static void createPortGroup(String physicalNetwork, String networkName, // Next, create the port group. For this, we need to create a VLAN spec. // NOTE - VmwareDistributedVirtualSwitchPvlanSpec extends VmwareDistributedVirtualSwitchVlanSpec. if (vid == null || spvlanid == null) { - vlanSpec = createDVPortVlanIdSpec(vid); + vlanSpec = createDVPortVlanSpec(vid, vlanRange); dvsPortSetting = createVmwareDVPortSettingSpec(shapingPolicy, secPolicy, vlanSpec); } else if (spvlanid != null) { // Create a pvlan spec. The pvlan spec is different from the pvlan config spec @@ -851,12 +867,57 @@ public static boolean isSpecMatch(DVPortgroupConfigInfo currentDvPortgroupInfo, } } - VmwareDistributedVirtualSwitchVlanIdSpec oldVlanSpec = (VmwareDistributedVirtualSwitchVlanIdSpec)(( - VMwareDVSPortSetting)currentDvPortgroupInfo.getDefaultPortConfig()).getVlan(); - VmwareDistributedVirtualSwitchVlanIdSpec newVlanSpec = (VmwareDistributedVirtualSwitchVlanIdSpec)(( - VMwareDVSPortSetting)newDvPortGroupSpec.getDefaultPortConfig()).getVlan(); - int oldVlanId = oldVlanSpec.getVlanId(); - int newVlanId = newVlanSpec.getVlanId(); + VMwareDVSPortSetting currentPortSetting = ((VMwareDVSPortSetting)currentDvPortgroupInfo.getDefaultPortConfig()); + VMwareDVSPortSetting newPortSetting = ((VMwareDVSPortSetting)newDvPortGroupSpec.getDefaultPortConfig()); + if ((currentPortSetting.getSecurityPolicy() == null && newPortSetting.getSecurityPolicy() != null) || + (currentPortSetting.getSecurityPolicy() != null && newPortSetting.getSecurityPolicy() == null)) { + specMatches = false; + } + if (currentPortSetting.getSecurityPolicy() != null && newPortSetting.getSecurityPolicy() != null) { + if (currentPortSetting.getSecurityPolicy().getAllowPromiscuous() != null && + newPortSetting.getSecurityPolicy().getAllowPromiscuous() != null && + newPortSetting.getSecurityPolicy().getAllowPromiscuous().isValue() != null && + !newPortSetting.getSecurityPolicy().getAllowPromiscuous().isValue().equals(currentPortSetting.getSecurityPolicy().getAllowPromiscuous().isValue())) { + specMatches = false; + } + if (currentPortSetting.getSecurityPolicy().getForgedTransmits() != null && + newPortSetting.getSecurityPolicy().getForgedTransmits() != null && + newPortSetting.getSecurityPolicy().getForgedTransmits().isValue() != null && + !newPortSetting.getSecurityPolicy().getForgedTransmits().isValue().equals(currentPortSetting.getSecurityPolicy().getForgedTransmits().isValue())) { + specMatches = false; + } + if (currentPortSetting.getSecurityPolicy().getMacChanges() != null && + newPortSetting.getSecurityPolicy().getMacChanges() != null && + newPortSetting.getSecurityPolicy().getMacChanges().isValue() != null && + !newPortSetting.getSecurityPolicy().getMacChanges().isValue().equals(currentPortSetting.getSecurityPolicy().getMacChanges().isValue())) { + specMatches = false; + } + } + + VmwareDistributedVirtualSwitchVlanSpec oldVlanSpec = currentPortSetting.getVlan(); + VmwareDistributedVirtualSwitchVlanSpec newVlanSpec = newPortSetting.getVlan(); + + int oldVlanId, newVlanId; + if (oldVlanSpec instanceof VmwareDistributedVirtualSwitchPvlanSpec && newVlanSpec instanceof VmwareDistributedVirtualSwitchPvlanSpec) { + VmwareDistributedVirtualSwitchPvlanSpec oldpVlanSpec = (VmwareDistributedVirtualSwitchPvlanSpec) oldVlanSpec; + VmwareDistributedVirtualSwitchPvlanSpec newpVlanSpec = (VmwareDistributedVirtualSwitchPvlanSpec) newVlanSpec; + oldVlanId = oldpVlanSpec.getPvlanId(); + newVlanId = newpVlanSpec.getPvlanId(); + } else if (oldVlanSpec instanceof VmwareDistributedVirtualSwitchTrunkVlanSpec && newVlanSpec instanceof VmwareDistributedVirtualSwitchTrunkVlanSpec) { + VmwareDistributedVirtualSwitchTrunkVlanSpec oldpVlanSpec = (VmwareDistributedVirtualSwitchTrunkVlanSpec) oldVlanSpec; + VmwareDistributedVirtualSwitchTrunkVlanSpec newpVlanSpec = (VmwareDistributedVirtualSwitchTrunkVlanSpec) newVlanSpec; + oldVlanId = oldpVlanSpec.getVlanId().get(0).getStart(); + newVlanId = newpVlanSpec.getVlanId().get(0).getStart(); + } else if (oldVlanSpec instanceof VmwareDistributedVirtualSwitchVlanIdSpec && newVlanSpec instanceof VmwareDistributedVirtualSwitchVlanIdSpec) { + VmwareDistributedVirtualSwitchVlanIdSpec oldVlanIdSpec = (VmwareDistributedVirtualSwitchVlanIdSpec) oldVlanSpec; + VmwareDistributedVirtualSwitchVlanIdSpec newVlanIdSpec = (VmwareDistributedVirtualSwitchVlanIdSpec) newVlanSpec; + oldVlanId = oldVlanIdSpec.getVlanId(); + newVlanId = newVlanIdSpec.getVlanId(); + } else { + s_logger.debug("Old and new vlan spec type mismatch found for [" + dvPortGroupName + "] has changed. Old spec type is: " + oldVlanSpec.getClass() + ", and new spec type is:" + newVlanSpec.getClass()); + return false; + } + if (oldVlanId != newVlanId) { s_logger.info("Detected that new VLAN [" + newVlanId + "] of dvPortGroup [" + dvPortGroupName + "] is different from current VLAN [" + oldVlanId + "]"); @@ -994,25 +1055,114 @@ public static VMwareDVSPvlanConfigSpec createDVPortPvlanConfigSpec(int vlanId, i return pvlanConfigSpec; } - public static VmwareDistributedVirtualSwitchVlanIdSpec createDVPortVlanIdSpec(Integer vlanId) { + public static VmwareDistributedVirtualSwitchVlanSpec createDVPortVlanSpec(Integer vlanId, String vlanRange) { + if (vlanId == null && vlanRange != null && !vlanRange.isEmpty()) { + s_logger.debug("Creating dvSwitch port vlan-trunk spec with range: " + vlanRange); + VmwareDistributedVirtualSwitchTrunkVlanSpec trunkVlanSpec = new VmwareDistributedVirtualSwitchTrunkVlanSpec(); + for (final String vlanRangePart : vlanRange.split(",")) { + if (vlanRangePart == null || vlanRange.isEmpty()) { + continue; + } + final NumericRange numericRange = new NumericRange(); + if (vlanRangePart.contains("-")) { + final String[] range = vlanRangePart.split("-"); + if (range.length == 2 && range[0] != null && range[1] != null) { + numericRange.setStart(NumbersUtil.parseInt(range[0], 0)); + numericRange.setEnd(NumbersUtil.parseInt(range[1], 0)); + } else { + continue; + } + } else { + numericRange.setStart(NumbersUtil.parseInt(vlanRangePart, 0)); + numericRange.setEnd(NumbersUtil.parseInt(vlanRangePart, 0)); + } + trunkVlanSpec.getVlanId().add(numericRange); + } + if (trunkVlanSpec.getVlanId().size() != 0) { + return trunkVlanSpec; + } + } VmwareDistributedVirtualSwitchVlanIdSpec vlanIdSpec = new VmwareDistributedVirtualSwitchVlanIdSpec(); - vlanIdSpec.setVlanId(vlanId == null ? 0 : vlanId.intValue()); + vlanIdSpec.setVlanId(vlanId == null ? 0 : vlanId); + s_logger.debug("Creating dvSwitch port vlan-id spec with id: " + vlanIdSpec.getVlanId()); return vlanIdSpec; } - public static DVSSecurityPolicy createDVSSecurityPolicy() { + public static Map getDefaultSecurityDetails() { + final Map details = new HashMap<>(); + details.put(NetworkOffering.Detail.PromiscuousMode, NetworkOrchestrationService.PromiscuousMode.value().toString()); + details.put(NetworkOffering.Detail.MacAddressChanges, NetworkOrchestrationService.MacAddressChanges.value().toString()); + details.put(NetworkOffering.Detail.ForgedTransmits, NetworkOrchestrationService.ForgedTransmits.value().toString()); + return details; + } + + public static DVSSecurityPolicy createDVSSecurityPolicy(Map nicDetails) { DVSSecurityPolicy secPolicy = new DVSSecurityPolicy(); BoolPolicy allow = new BoolPolicy(); allow.setValue(true); + BoolPolicy deny = new BoolPolicy(); + deny.setValue(false); + secPolicy.setAllowPromiscuous(deny); secPolicy.setForgedTransmits(allow); - secPolicy.setAllowPromiscuous(allow); secPolicy.setMacChanges(allow); + + if (nicDetails == null) { + nicDetails = getDefaultSecurityDetails(); + } + + if (nicDetails.containsKey(NetworkOffering.Detail.PromiscuousMode)) { + if (Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.PromiscuousMode))) { + secPolicy.setAllowPromiscuous(allow); + } else { + secPolicy.setAllowPromiscuous(deny); + } + } + if (nicDetails.containsKey(NetworkOffering.Detail.ForgedTransmits)) { + if (Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.ForgedTransmits))) { + secPolicy.setForgedTransmits(allow); + } else { + secPolicy.setForgedTransmits(deny); + } + } + if (nicDetails.containsKey(NetworkOffering.Detail.MacAddressChanges)) { + if (Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.MacAddressChanges))) { + secPolicy.setMacChanges(allow); + } else { + secPolicy.setMacChanges(deny); + } + } + + return secPolicy; + } + + public static HostNetworkSecurityPolicy createVSSecurityPolicy(Map nicDetails) { + HostNetworkSecurityPolicy secPolicy = new HostNetworkSecurityPolicy(); + secPolicy.setAllowPromiscuous(Boolean.FALSE); + secPolicy.setForgedTransmits(Boolean.TRUE); + secPolicy.setMacChanges(Boolean.TRUE); + + if (nicDetails == null) { + nicDetails = getDefaultSecurityDetails(); + } + + if (nicDetails.containsKey(NetworkOffering.Detail.PromiscuousMode)) { + secPolicy.setAllowPromiscuous(Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.PromiscuousMode))); + } + + if (nicDetails.containsKey(NetworkOffering.Detail.ForgedTransmits)) { + secPolicy.setForgedTransmits(Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.ForgedTransmits))); + } + + if (nicDetails.containsKey(NetworkOffering.Detail.MacAddressChanges)) { + secPolicy.setMacChanges(Boolean.valueOf(nicDetails.get(NetworkOffering.Detail.MacAddressChanges))); + } + return secPolicy; } public static Pair prepareNetwork(String vSwitchName, String namePrefix, HostMO hostMo, String vlanId, Integer networkRateMbps, - Integer networkRateMulticastMbps, long timeOutMs, boolean syncPeerHosts, BroadcastDomainType broadcastDomainType, String nicUuid) throws Exception { + Integer networkRateMulticastMbps, long timeOutMs, boolean syncPeerHosts, BroadcastDomainType broadcastDomainType, String nicUuid, Map nicDetails) throws Exception { HostVirtualSwitch vSwitch; if (vSwitchName == null) { @@ -1059,13 +1209,8 @@ public static Pair prepareNetwork(String vSwitch } } - HostNetworkSecurityPolicy secPolicy = null; - if (namePrefix.equalsIgnoreCase("cloud.private")) { - secPolicy = new HostNetworkSecurityPolicy(); - secPolicy.setAllowPromiscuous(Boolean.TRUE); - secPolicy.setForgedTransmits(Boolean.TRUE); - secPolicy.setMacChanges(Boolean.TRUE); - } + HostNetworkSecurityPolicy secPolicy = createVSSecurityPolicy(nicDetails); + HostNetworkTrafficShapingPolicy shapingPolicy = null; if (networkRateMbps != null && networkRateMbps.intValue() > 0) { shapingPolicy = new HostNetworkTrafficShapingPolicy(); @@ -1105,7 +1250,7 @@ public static Pair prepareNetwork(String vSwitch bWaitPortGroupReady = false; } else { HostPortGroupSpec spec = hostMo.getPortGroupSpec(networkName); - if (!isSpecMatch(spec, vid, shapingPolicy)) { + if (!isSpecMatch(spec, vid, secPolicy, shapingPolicy)) { hostMo.updatePortGroup(vSwitch, networkName, vid, secPolicy, shapingPolicy); bWaitPortGroupReady = true; } @@ -1149,7 +1294,7 @@ public static Pair prepareNetwork(String vSwitch if (s_logger.isDebugEnabled()) s_logger.debug("Prepare network on other host, vlan: " + vlanId + ", host: " + otherHostMo.getHostName()); prepareNetwork(vSwitchName, namePrefix, otherHostMo, vlanId, networkRateMbps, networkRateMulticastMbps, timeOutMs, false, - broadcastDomainType, nicUuid); + broadcastDomainType, nicUuid, nicDetails); } catch (Exception e) { s_logger.warn("Unable to prepare network on other host, vlan: " + vlanId + ", host: " + otherHostMo.getHostName()); } @@ -1172,7 +1317,7 @@ public static Pair prepareNetwork(String vSwitch return new Pair(morNetwork, networkName); } - private static boolean isSpecMatch(HostPortGroupSpec spec, Integer vlanId, HostNetworkTrafficShapingPolicy shapingPolicy) { + private static boolean isSpecMatch(HostPortGroupSpec spec, Integer vlanId, HostNetworkSecurityPolicy securityPolicy, HostNetworkTrafficShapingPolicy shapingPolicy) { // check VLAN configuration if (vlanId != null) { if (vlanId.intValue() != spec.getVlanId()) @@ -1182,16 +1327,36 @@ private static boolean isSpecMatch(HostPortGroupSpec spec, Integer vlanId, HostN return false; } + // check security policy for the portgroup + HostNetworkSecurityPolicy secPolicyInSpec = null; + if (spec.getPolicy() != null) { + secPolicyInSpec = spec.getPolicy().getSecurity(); + } + + if ((secPolicyInSpec != null && securityPolicy == null) || (secPolicyInSpec == null && securityPolicy != null)) { + return false; + } + + if (secPolicyInSpec != null && securityPolicy != null + && ((securityPolicy.isAllowPromiscuous() != null && !securityPolicy.isAllowPromiscuous().equals(secPolicyInSpec.isAllowPromiscuous())) + || (securityPolicy.isForgedTransmits() != null && !securityPolicy.isForgedTransmits().equals(secPolicyInSpec.isForgedTransmits())) + || (securityPolicy.isMacChanges() != null && securityPolicy.isMacChanges().equals(secPolicyInSpec.isMacChanges())))) { + return false; + } + // check traffic shaping configuration HostNetworkTrafficShapingPolicy policyInSpec = null; - if (spec.getPolicy() != null) + if (spec.getPolicy() != null) { policyInSpec = spec.getPolicy().getShapingPolicy(); + } - if (policyInSpec != null && shapingPolicy == null || policyInSpec == null && shapingPolicy != null) + if ((policyInSpec != null && shapingPolicy == null) || (policyInSpec == null && shapingPolicy != null)) { return false; + } - if (policyInSpec == null && shapingPolicy == null) + if (policyInSpec == null && shapingPolicy == null) { return true; + } // so far policyInSpec and shapingPolicy should both not be null if (policyInSpec.isEnabled() == null || !policyInSpec.isEnabled().booleanValue()) diff --git a/vmware-base/test/com/cloud/hypervisor/vmware/mo/HypervisorHostHelperTest.java b/vmware-base/test/com/cloud/hypervisor/vmware/mo/HypervisorHostHelperTest.java index 2fc9995091bd..545104d91fcb 100644 --- a/vmware-base/test/com/cloud/hypervisor/vmware/mo/HypervisorHostHelperTest.java +++ b/vmware-base/test/com/cloud/hypervisor/vmware/mo/HypervisorHostHelperTest.java @@ -16,31 +16,40 @@ // under the License. package com.cloud.hypervisor.vmware.mo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.offering.NetworkOffering; import com.vmware.vim25.AboutInfo; import com.vmware.vim25.BoolPolicy; import com.vmware.vim25.DVPortgroupConfigInfo; import com.vmware.vim25.DVPortgroupConfigSpec; +import com.vmware.vim25.DVSSecurityPolicy; import com.vmware.vim25.DVSTrafficShapingPolicy; +import com.vmware.vim25.HostNetworkSecurityPolicy; import com.vmware.vim25.LongPolicy; import com.vmware.vim25.ServiceContent; import com.vmware.vim25.VMwareDVSPortSetting; +import com.vmware.vim25.VmwareDistributedVirtualSwitchTrunkVlanSpec; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; +import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanSpec; public class HypervisorHostHelperTest { @Mock @@ -774,4 +783,104 @@ public void testOvfDomRewriter() { ""; assertEquals(expected, HypervisorHostHelper.removeOVFNetwork(ovfString)); } + + private Map getSecurityDetails() { + final Map details = new HashMap<>(); + details.put(NetworkOffering.Detail.PromiscuousMode, "false"); + details.put(NetworkOffering.Detail.ForgedTransmits, "false"); + details.put(NetworkOffering.Detail.MacAddressChanges, "false"); + return details; + } + + @Test + public void testVSSecurityPolicyDefault() { + HostNetworkSecurityPolicy secPolicy = HypervisorHostHelper.createVSSecurityPolicy(null); + assertFalse(secPolicy.isAllowPromiscuous()); + assertTrue(secPolicy.isForgedTransmits()); + assertTrue(secPolicy.isMacChanges()); + } + + @Test + public void testVSSecurityPolicyDefaultWithDetail() { + HostNetworkSecurityPolicy secPolicy = HypervisorHostHelper.createVSSecurityPolicy(getSecurityDetails()); + assertFalse(secPolicy.isAllowPromiscuous()); + assertFalse(secPolicy.isForgedTransmits()); + assertFalse(secPolicy.isMacChanges()); + } + + @Test + public void testVSSecurityPolicyWithDetail() { + Map details = getSecurityDetails(); + details.put(NetworkOffering.Detail.MacAddressChanges, "true"); + HostNetworkSecurityPolicy secPolicy = HypervisorHostHelper.createVSSecurityPolicy(details); + assertFalse(secPolicy.isAllowPromiscuous()); + assertFalse(secPolicy.isForgedTransmits()); + assertTrue(secPolicy.isMacChanges()); + } + + @Test + public void testDVSSecurityPolicyDefault() { + DVSSecurityPolicy secPolicy = HypervisorHostHelper.createDVSSecurityPolicy(null); + assertFalse(secPolicy.getAllowPromiscuous().isValue()); + assertTrue(secPolicy.getForgedTransmits().isValue()); + assertTrue(secPolicy.getMacChanges().isValue()); + } + + @Test + public void testDVSSecurityPolicyDefaultWithDetail() { + Map details = getSecurityDetails(); + details.remove(NetworkOffering.Detail.ForgedTransmits); + details.remove(NetworkOffering.Detail.PromiscuousMode); + DVSSecurityPolicy secPolicy = HypervisorHostHelper.createDVSSecurityPolicy(details); + assertFalse(secPolicy.getAllowPromiscuous().isValue()); + assertFalse(secPolicy.getMacChanges().isValue()); + assertTrue(secPolicy.getForgedTransmits().isValue()); + } + + @Test + public void testDVSSecurityPolicyWithDetail() { + Map details = getSecurityDetails(); + details.put(NetworkOffering.Detail.ForgedTransmits, "true"); + DVSSecurityPolicy secPolicy = HypervisorHostHelper.createDVSSecurityPolicy(details); + assertFalse(secPolicy.getAllowPromiscuous().isValue()); + assertTrue(secPolicy.getForgedTransmits().isValue()); + assertFalse(secPolicy.getMacChanges().isValue()); + } + + @Test + public void testCreateDVPortVlanSpecNullVlanId() { + VmwareDistributedVirtualSwitchVlanSpec spec = HypervisorHostHelper.createDVPortVlanSpec(null, null); + assertTrue(spec instanceof VmwareDistributedVirtualSwitchVlanIdSpec); + assertTrue(((VmwareDistributedVirtualSwitchVlanIdSpec) spec).getVlanId() == 0); + } + + @Test + public void testCreateDVPortVlanSpecValidVlanId() { + VmwareDistributedVirtualSwitchVlanSpec spec = HypervisorHostHelper.createDVPortVlanSpec(100, "400"); + assertTrue(spec instanceof VmwareDistributedVirtualSwitchVlanIdSpec); + assertTrue(((VmwareDistributedVirtualSwitchVlanIdSpec) spec).getVlanId() == 100); + } + + @Test + public void testCreateDVPortVlanSpecValidVlanRange() { + VmwareDistributedVirtualSwitchVlanSpec spec = HypervisorHostHelper.createDVPortVlanSpec(null, "200-250"); + assertTrue(spec instanceof VmwareDistributedVirtualSwitchTrunkVlanSpec); + assertTrue(((VmwareDistributedVirtualSwitchTrunkVlanSpec) spec).getVlanId().get(0).getStart() == 200); + assertTrue(((VmwareDistributedVirtualSwitchTrunkVlanSpec) spec).getVlanId().get(0).getEnd() == 250); + } + + @Test + public void testCreateDVPortVlanSpecInvalidMissingVlanRange() { + VmwareDistributedVirtualSwitchVlanSpec spec = HypervisorHostHelper.createDVPortVlanSpec(null, "200-"); + assertTrue(spec instanceof VmwareDistributedVirtualSwitchVlanIdSpec); + assertTrue(((VmwareDistributedVirtualSwitchVlanIdSpec) spec).getVlanId() == 0); + } + + @Test + public void testCreateDVPortVlanSpecInvalidInputVlanRange() { + VmwareDistributedVirtualSwitchVlanSpec spec = HypervisorHostHelper.createDVPortVlanSpec(null, "a-b"); + assertTrue(spec instanceof VmwareDistributedVirtualSwitchTrunkVlanSpec); + assertTrue(((VmwareDistributedVirtualSwitchTrunkVlanSpec) spec).getVlanId().get(0).getStart() == 0); + assertTrue(((VmwareDistributedVirtualSwitchTrunkVlanSpec) spec).getVlanId().get(0).getEnd() == 0); + } }