diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index b451974851b..c4db7dce8a6 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -231,27 +231,27 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Inject EntityManager _entityMgr; @Inject - DataCenterDao _dcDao = null; + DataCenterDao _dcDao; @Inject - VlanDao _vlanDao = null; + VlanDao _vlanDao; @Inject - IPAddressDao _ipAddressDao = null; + IPAddressDao _ipAddressDao; @Inject - AccountDao _accountDao = null; + AccountDao _accountDao; @Inject ConfigurationDao _configDao; @Inject - UserVmDao _userVmDao = null; + UserVmDao _userVmDao; @Inject AlertManager _alertMgr; @Inject ConfigurationManager _configMgr; @Inject - NetworkOfferingDao _networkOfferingDao = null; + NetworkOfferingDao _networkOfferingDao; @Inject - NetworkDao _networksDao = null; + NetworkDao _networksDao; @Inject - NicDao _nicDao = null; + NicDao _nicDao; @Inject RulesManager _rulesMgr; @Inject @@ -860,6 +860,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NicVO vo = new NicVO(guru.getName(), vm.getId(), network.getId(), vm.getType()); + DataCenterVO dcVo = _dcDao.findById(network.getDataCenterId()); + if (dcVo.getNetworkType() == NetworkType.Basic) { + configureNicProfileBasedOnRequestedIp(requested, profile, network); + } + deviceId = applyProfileToNic(vo, profile, deviceId); vo = _nicDao.persist(vo); @@ -871,6 +876,85 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return new Pair(vmNic, Integer.valueOf(deviceId)); } + /** + * If the requested IPv4 address from the NicProfile was configured then it configures the IPv4 address, Netmask and Gateway to deploy the VM with the requested IP. + */ + protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProfile, NicProfile nicProfile, Network network) { + if (requestedNicProfile == null) { + return; + } + String requestedIpv4Address = requestedNicProfile.getRequestedIPv4(); + if (requestedIpv4Address == null) { + return; + } + if (!NetUtils.isValidIp4(requestedIpv4Address)) { + throw new InvalidParameterValueException(String.format("The requested [IPv4 address='%s'] is not a valid IP address", requestedIpv4Address)); + } + + VlanVO vlanVo = _vlanDao.findByNetworkIdAndIpv4(network.getId(), requestedIpv4Address); + if (vlanVo == null) { + throw new InvalidParameterValueException(String.format("Trying to configure a Nic with the requested [IPv4='%s'] but cannot find a Vlan for the [network id='%s']", + requestedIpv4Address, network.getId())); + } + + String ipv4Gateway = vlanVo.getVlanGateway(); + String ipv4Netmask = vlanVo.getVlanNetmask(); + + if (!NetUtils.isValidIp4(ipv4Gateway)) { + throw new InvalidParameterValueException(String.format("The [IPv4Gateway='%s'] from [VlanId='%s'] is not valid", ipv4Gateway, vlanVo.getId())); + } + if (!NetUtils.isValidIp4Netmask(ipv4Netmask)) { + throw new InvalidParameterValueException(String.format("The [IPv4Netmask='%s'] from [VlanId='%s'] is not valid", ipv4Netmask, vlanVo.getId())); + } + + acquireLockAndCheckIfIpv4IsFree(network, requestedIpv4Address); + + nicProfile.setIPv4Address(requestedIpv4Address); + nicProfile.setIPv4Gateway(ipv4Gateway); + nicProfile.setIPv4Netmask(ipv4Netmask); + + if (nicProfile.getMacAddress() == null) { + try { + String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId()); + nicProfile.setMacAddress(macAddress); + } catch (InsufficientAddressCapacityException e) { + throw new CloudRuntimeException(String.format("Cannot get next available mac address in [network id='%s']", network.getId()), e); + } + } + } + + /** + * Acquires lock in "user_ip_address" and checks if the requested IPv4 address is Free. + */ + protected void acquireLockAndCheckIfIpv4IsFree(Network network, String requestedIpv4Address) { + IPAddressVO ipVO = _ipAddressDao.findByIpAndSourceNetworkId(network.getId(), requestedIpv4Address); + if (ipVO == null) { + throw new InvalidParameterValueException( + String.format("Cannot find IPAddressVO for guest [IPv4 address='%s'] and [network id='%s']", requestedIpv4Address, network.getId())); + } + try { + IPAddressVO lockedIpVO = _ipAddressDao.acquireInLockTable(ipVO.getId()); + validateLockedRequestedIp(ipVO, lockedIpVO); + lockedIpVO.setState(IPAddressVO.State.Allocated); + _ipAddressDao.update(lockedIpVO.getId(), lockedIpVO); + } finally { + _ipAddressDao.releaseFromLockTable(ipVO.getId()); + } + } + + /** + * Validates the locked IP, throwing an exeption if the locked IP is null or the locked IP is not in 'Free' state. + */ + protected void validateLockedRequestedIp(IPAddressVO ipVO, IPAddressVO lockedIpVO) { + if (lockedIpVO == null) { + throw new InvalidParameterValueException(String.format("Cannot acquire guest [IPv4 address='%s'] as it was removed while acquiring lock", ipVO.getAddress())); + } + if (lockedIpVO.getState() != IPAddressVO.State.Free) { + throw new InvalidParameterValueException( + String.format("Cannot acquire guest [IPv4 address='%s']; The Ip address is in [state='%s']", ipVO.getAddress(), lockedIpVO.getState().toString())); + } + } + protected Integer applyProfileToNic(final NicVO vo, final NicProfile profile, Integer deviceId) { if (profile.getDeviceId() != null) { vo.setDeviceId(profile.getDeviceId()); diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index b0283f35c1b..3450c09b263 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -23,31 +23,42 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Arrays; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.offerings.NetworkOfferingVO; import org.apache.log4j.Logger; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Matchers; +import org.mockito.Mockito; +import com.cloud.dc.Vlan; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.VlanDao; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; import com.cloud.network.Network.Service; import com.cloud.network.NetworkModel; +import com.cloud.network.IpAddress.State; import com.cloud.network.Networks.TrafficType; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.guru.NetworkGuru; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.utils.net.Ip; import com.cloud.vm.Nic; +import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; @@ -65,11 +76,11 @@ import junit.framework.TestCase; public class NetworkOrchestratorTest extends TestCase { static final Logger s_logger = Logger.getLogger(NetworkOrchestratorTest.class); - NetworkOrchestrator testOrchastrator = new NetworkOrchestrator(); + NetworkOrchestrator testOrchastrator = Mockito.spy(new NetworkOrchestrator()); - String guruName = "GuestNetworkGuru"; - String dhcpProvider = "VirtualRouter"; - NetworkGuru guru = mock(NetworkGuru.class); + private String guruName = "GuestNetworkGuru"; + private String dhcpProvider = "VirtualRouter"; + private NetworkGuru guru = mock(NetworkGuru.class); NetworkOfferingVO networkOffering = mock(NetworkOfferingVO.class); @@ -85,11 +96,13 @@ public class NetworkOrchestratorTest extends TestCase { testOrchastrator._nicSecondaryIpDao = mock(NicSecondaryIpDao.class); testOrchastrator._ntwkSrvcDao = mock(NetworkServiceMapDao.class); testOrchastrator._nicIpAliasDao = mock(NicIpAliasDao.class); + testOrchastrator._ipAddressDao = mock(IPAddressDao.class); + testOrchastrator._vlanDao = mock(VlanDao.class); DhcpServiceProvider provider = mock(DhcpServiceProvider.class); Map capabilities = new HashMap(); - Map> services = new HashMap>(); - services.put(Network.Service.Dhcp,capabilities); + Map> services = new HashMap>(); + services.put(Network.Service.Dhcp, capabilities); when(provider.getCapabilities()).thenReturn(services); capabilities.put(Network.Capability.DhcpAccrossMultipleSubnets, "true"); @@ -108,7 +121,7 @@ public class NetworkOrchestratorTest extends TestCase { @Test public void testRemoveDhcpServiceWithNic() { // make local mocks - VirtualMachineProfile vm = mock(VirtualMachineProfile.class); + VirtualMachineProfile vm = mock(VirtualMachineProfile.class); NicVO nic = mock(NicVO.class); NetworkVO network = mock(NetworkVO.class); @@ -117,9 +130,8 @@ public class NetworkOrchestratorTest extends TestCase { when(testOrchastrator._networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)).thenReturn(true); when(network.getTrafficType()).thenReturn(TrafficType.Guest); when(network.getGuestType()).thenReturn(GuestType.Shared); - when(testOrchastrator._nicDao.listByNetworkIdTypeAndGatewayAndBroadcastUri(nic.getNetworkId(), VirtualMachine.Type.User, nic.getIPv4Gateway(), nic.getBroadcastUri())).thenReturn(new ArrayList()); - - + when(testOrchastrator._nicDao.listByNetworkIdTypeAndGatewayAndBroadcastUri(nic.getNetworkId(), VirtualMachine.Type.User, nic.getIPv4Gateway(), nic.getBroadcastUri())) + .thenReturn(new ArrayList()); when(network.getGuruName()).thenReturn(guruName); when(testOrchastrator._networksDao.findById(nic.getNetworkId())).thenReturn(network); @@ -134,7 +146,7 @@ public class NetworkOrchestratorTest extends TestCase { @Test public void testDontRemoveDhcpServiceFromDomainRouter() { // make local mocks - VirtualMachineProfile vm = mock(VirtualMachineProfile.class); + VirtualMachineProfile vm = mock(VirtualMachineProfile.class); NicVO nic = mock(NicVO.class); NetworkVO network = mock(NetworkVO.class); @@ -154,7 +166,7 @@ public class NetworkOrchestratorTest extends TestCase { @Test public void testDontRemoveDhcpServiceWhenNotProvided() { // make local mocks - VirtualMachineProfile vm = mock(VirtualMachineProfile.class); + VirtualMachineProfile vm = mock(VirtualMachineProfile.class); NicVO nic = mock(NicVO.class); NetworkVO network = mock(NetworkVO.class); @@ -200,4 +212,255 @@ public class NetworkOrchestratorTest extends TestCase { when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(false); testOrchastrator.checkL2OfferingServices(networkOffering); } + + @Test + public void testConfigureNicProfileBasedOnRequestedIpTestMacNull() { + Network network = mock(Network.class); + NicProfile requestedNicProfile = new NicProfile(); + NicProfile nicProfile = Mockito.spy(new NicProfile()); + + configureTestConfigureNicProfileBasedOnRequestedIpTests(nicProfile, 0l, false, IPAddressVO.State.Free, "192.168.100.1", "255.255.255.0", "00-88-14-4D-4C-FB", + requestedNicProfile, null, "192.168.100.150"); + + testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); + + verifyAndAssert("192.168.100.150", "192.168.100.1", "255.255.255.0", nicProfile, 1, 1); + } + + @Test + public void testConfigureNicProfileBasedOnRequestedIpTestNicProfileMacNotNull() { + Network network = mock(Network.class); + NicProfile requestedNicProfile = new NicProfile(); + NicProfile nicProfile = Mockito.spy(new NicProfile()); + + configureTestConfigureNicProfileBasedOnRequestedIpTests(nicProfile, 0l, false, IPAddressVO.State.Free, "192.168.100.1", "255.255.255.0", "00-88-14-4D-4C-FB", + requestedNicProfile, "00-88-14-4D-4C-FB", "192.168.100.150"); + + testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); + + verifyAndAssert("192.168.100.150", "192.168.100.1", "255.255.255.0", nicProfile, 1, 0); + } + + @Test + public void testConfigureNicProfileBasedOnRequestedIpTestRequestedIpNull() { + testConfigureNicProfileBasedOnRequestedIpTestRequestedIp(null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestRequestedIpIsBlank() { + testConfigureNicProfileBasedOnRequestedIpTestRequestedIp(""); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestRequestedIpIsNotValid() { + testConfigureNicProfileBasedOnRequestedIpTestRequestedIp("123"); + } + + private void testConfigureNicProfileBasedOnRequestedIpTestRequestedIp(String requestedIpv4Address) { + Network network = mock(Network.class); + NicProfile requestedNicProfile = new NicProfile(); + NicProfile nicProfile = Mockito.spy(new NicProfile()); + + configureTestConfigureNicProfileBasedOnRequestedIpTests(nicProfile, 0l, false, IPAddressVO.State.Free, "192.168.100.1", "255.255.255.0", "00-88-14-4D-4C-FB", + requestedNicProfile, null, requestedIpv4Address); + testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); + + verifyAndAssert(null, null, null, nicProfile, 0, 0); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestGatewayIsBlank() { + testConfigureNicProfileBasedOnRequestedIpTestGateway(""); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestGatewayIsNotValid() { + testConfigureNicProfileBasedOnRequestedIpTestGateway("123"); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestGatewayIsNull() { + testConfigureNicProfileBasedOnRequestedIpTestGateway(null); + } + + private void testConfigureNicProfileBasedOnRequestedIpTestGateway(String ipv4Gateway) { + Network network = mock(Network.class); + NicProfile requestedNicProfile = new NicProfile(); + NicProfile nicProfile = Mockito.spy(new NicProfile()); + + configureTestConfigureNicProfileBasedOnRequestedIpTests(nicProfile, 0l, false, IPAddressVO.State.Free, ipv4Gateway, "255.255.255.0", "00-88-14-4D-4C-FB", + requestedNicProfile, "00-88-14-4D-4C-FB", "192.168.100.150"); + testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); + verifyAndAssert(null, null, null, nicProfile, 1, 0); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestNetmaskIsNull() { + testConfigureNicProfileBasedOnRequestedIpTestNetmask(null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestNetmaskIsBlank() { + testConfigureNicProfileBasedOnRequestedIpTestNetmask(""); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestNetmaskIsNotValid() { + testConfigureNicProfileBasedOnRequestedIpTestNetmask("123"); + } + + private void testConfigureNicProfileBasedOnRequestedIpTestNetmask(String ipv4Netmask) { + Network network = mock(Network.class); + NicProfile requestedNicProfile = new NicProfile(); + NicProfile nicProfile = Mockito.spy(new NicProfile()); + + configureTestConfigureNicProfileBasedOnRequestedIpTests(nicProfile, 0l, false, IPAddressVO.State.Free, "192.168.100.1", ipv4Netmask, "00-88-14-4D-4C-FB", + requestedNicProfile, "00-88-14-4D-4C-FB", "192.168.100.150"); + testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); + verifyAndAssert(null, null, null, nicProfile, 1, 0); + } + + @Test(expected = InvalidParameterValueException.class) + public void testConfigureNicProfileBasedOnRequestedIpTestIPAddressVONull() { + Network network = mock(Network.class); + NicProfile requestedNicProfile = new NicProfile(); + NicProfile nicProfile = Mockito.spy(new NicProfile()); + + configureTestConfigureNicProfileBasedOnRequestedIpTests(nicProfile, 0l, false, IPAddressVO.State.Free, "192.168.100.1", "255.255.255.0", "00-88-14-4D-4C-FB", + requestedNicProfile, "00-88-14-4D-4C-FB", "192.168.100.150"); + when(testOrchastrator._vlanDao.findByNetworkIdAndIpv4(Mockito.anyLong(), Mockito.anyString())).thenReturn(null); + + testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); + verifyAndAssert(null, null, null, nicProfile, 0, 0); + } + + private void configureTestConfigureNicProfileBasedOnRequestedIpTests(NicProfile nicProfile, long ipvoId, boolean ipVoIsNull, IPAddressVO.State state, String vlanGateway, + String vlanNetmask, String macAddress, NicProfile requestedNicProfile, String nicProfileMacAddress, String requestedIpv4Address) { + IPAddressVO ipVoSpy = Mockito.spy(new IPAddressVO(new Ip("192.168.100.100"), 0l, 0l, 0l, true)); + ipVoSpy.setState(state); + + requestedNicProfile.setRequestedIPv4(requestedIpv4Address); + nicProfile.setMacAddress(nicProfileMacAddress); + + when(ipVoSpy.getId()).thenReturn(ipvoId); + when(ipVoSpy.getState()).thenReturn(state); + + if (ipVoIsNull) { + when(testOrchastrator._ipAddressDao.findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString())).thenReturn(ipVoSpy); + } else { + when(testOrchastrator._ipAddressDao.findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString())).thenReturn(ipVoSpy); + } + + VlanVO vlanSpy = Mockito.spy(new VlanVO(Vlan.VlanType.DirectAttached, "vlanTag", vlanGateway, vlanNetmask, 0l, "192.168.100.100 - 192.168.100.200", 0l, new Long(0l), + "ip6Gateway", "ip6Cidr", "ip6Range")); + + Mockito.doReturn(0l).when(vlanSpy).getId(); + when(testOrchastrator._vlanDao.findByNetworkIdAndIpv4(Mockito.anyLong(), Mockito.anyString())).thenReturn(vlanSpy); + when(testOrchastrator._ipAddressDao.acquireInLockTable(Mockito.anyLong())).thenReturn(ipVoSpy); + when(testOrchastrator._ipAddressDao.update(Mockito.anyLong(), Mockito.any(IPAddressVO.class))).thenReturn(true); + when(testOrchastrator._ipAddressDao.releaseFromLockTable(Mockito.anyLong())).thenReturn(true); + try { + when(testOrchastrator._networkModel.getNextAvailableMacAddressInNetwork(Mockito.anyLong())).thenReturn(macAddress); + } catch (InsufficientAddressCapacityException e) { + e.printStackTrace(); + } + } + + private void verifyAndAssert(String requestedIpv4Address, String ipv4Gateway, String ipv4Netmask, NicProfile nicProfile, int acquireLockAndCheckIfIpv4IsFreeTimes, + int nextMacAddressTimes) { + verify(testOrchastrator, times(acquireLockAndCheckIfIpv4IsFreeTimes)).acquireLockAndCheckIfIpv4IsFree(Mockito.any(Network.class), Mockito.anyString()); + try { + verify(testOrchastrator._networkModel, times(nextMacAddressTimes)).getNextAvailableMacAddressInNetwork(Mockito.anyLong()); + } catch (InsufficientAddressCapacityException e) { + e.printStackTrace(); + } + + assertEquals(requestedIpv4Address, nicProfile.getIPv4Address()); + assertEquals(ipv4Gateway, nicProfile.getIPv4Gateway()); + assertEquals(ipv4Netmask, nicProfile.getIPv4Netmask()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testAcquireLockAndCheckIfIpv4IsFreeTestIpvoNull() { + executeTestAcquireLockAndCheckIfIpv4IsFree(IPAddressVO.State.Free, true, 1, 0, 0, 0, 0); + } + + @Test + public void testAcquireLockAndCheckIfIpv4IsFreeTestExpectedFlow() { + executeTestAcquireLockAndCheckIfIpv4IsFree(IPAddressVO.State.Free, false, 1, 1, 1, 1, 1); + } + + @Test(expected = InvalidParameterValueException.class) + public void testAcquireLockAndCheckIfIpv4IsFreeTestAllocatedIp() { + executeTestAcquireLockAndCheckIfIpv4IsFree(IPAddressVO.State.Allocated, false, 1, 1, 1, 0, 1); + } + + @Test(expected = InvalidParameterValueException.class) + public void testAcquireLockAndCheckIfIpv4IsFreeTestAllocatingIp() { + executeTestAcquireLockAndCheckIfIpv4IsFree(IPAddressVO.State.Allocating, false, 1, 1, 1, 0, 1); + } + + @Test(expected = InvalidParameterValueException.class) + public void testAcquireLockAndCheckIfIpv4IsFreeTestReleasingIp() { + executeTestAcquireLockAndCheckIfIpv4IsFree(IPAddressVO.State.Releasing, false, 1, 1, 1, 0, 1); + } + + private void executeTestAcquireLockAndCheckIfIpv4IsFree(IPAddressVO.State state, boolean isIPAddressVONull, int findByIpTimes, int acquireLockTimes, int releaseFromLockTimes, + int updateTimes, int validateTimes) { + Network network = Mockito.spy(new NetworkVO()); + IPAddressVO ipVoSpy = Mockito.spy(new IPAddressVO(new Ip("192.168.100.100"), 0l, 0l, 0l, true)); + ipVoSpy.setState(state); + ipVoSpy.setState(state); + if (isIPAddressVONull) { + when(testOrchastrator._ipAddressDao.findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString())).thenReturn(null); + } else { + when(testOrchastrator._ipAddressDao.findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString())).thenReturn(ipVoSpy); + } + when(testOrchastrator._ipAddressDao.acquireInLockTable(Mockito.anyLong())).thenReturn(ipVoSpy); + when(testOrchastrator._ipAddressDao.releaseFromLockTable(Mockito.anyLong())).thenReturn(true); + when(testOrchastrator._ipAddressDao.update(Mockito.anyLong(), Mockito.any(IPAddressVO.class))).thenReturn(true); + + testOrchastrator.acquireLockAndCheckIfIpv4IsFree(network, "192.168.100.150"); + + verify(testOrchastrator._ipAddressDao, Mockito.times(findByIpTimes)).findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString()); + verify(testOrchastrator._ipAddressDao, Mockito.times(acquireLockTimes)).acquireInLockTable(Mockito.anyLong()); + verify(testOrchastrator._ipAddressDao, Mockito.times(releaseFromLockTimes)).releaseFromLockTable(Mockito.anyLong()); + verify(testOrchastrator._ipAddressDao, Mockito.times(updateTimes)).update(Mockito.anyLong(), Mockito.any(IPAddressVO.class)); + verify(testOrchastrator, Mockito.times(validateTimes)).validateLockedRequestedIp(Mockito.any(IPAddressVO.class), Mockito.any(IPAddressVO.class)); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateLockedRequestedIpTestNullLockedIp() { + IPAddressVO ipVoSpy = Mockito.spy(new IPAddressVO(new Ip("192.168.100.100"), 0l, 0l, 0l, true)); + testOrchastrator.validateLockedRequestedIp(ipVoSpy, null); + } + + @Test + public void validateLockedRequestedIpTestNotFreeLockedIp() { + IPAddressVO ipVoSpy = Mockito.spy(new IPAddressVO(new Ip("192.168.100.100"), 0l, 0l, 0l, true)); + State[] states = State.values(); + for(int i=0; i < states.length;i++) { + boolean expectedException = false; + if (states[i] == State.Free) { + continue; + } + IPAddressVO lockedIp = ipVoSpy; + lockedIp.setState(states[i]); + try { + testOrchastrator.validateLockedRequestedIp(ipVoSpy, lockedIp); + } catch (InvalidParameterValueException e) { + expectedException = true; + } + Assert.assertTrue(expectedException); + } + } + + @Test + public void validateLockedRequestedIpTestFreeAndNotNullIp() { + IPAddressVO ipVoSpy = Mockito.spy(new IPAddressVO(new Ip("192.168.100.100"), 0l, 0l, 0l, true)); + IPAddressVO lockedIp = ipVoSpy; + lockedIp.setState(State.Free); + testOrchastrator.validateLockedRequestedIp(ipVoSpy, lockedIp); + } + } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java index e92a5cc6f1c..a3e3c60210c 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java @@ -27,6 +27,8 @@ public interface VlanDao extends GenericDao { VlanVO findByZoneAndVlanId(long zoneId, String vlanId); + VlanVO findByNetworkIdAndIpv4(long networkId, String ipv4Address); + List listByZone(long zoneId); List listByType(Vlan.VlanType vlanType); diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java index 0c5914824f4..2737beb03b4 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java @@ -43,6 +43,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; @Component public class VlanDaoImpl extends GenericDaoBase implements VlanDao { @@ -82,6 +83,24 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao return findOneBy(sc); } + /** + * Returns a vlan by the network id and if the given IPv4 is in the network IP range. + */ + @Override + public VlanVO findByNetworkIdAndIpv4(long networkId, String ipv4Address) { + List vlanVoList = listVlansByNetworkId(networkId); + for (VlanVO vlan : vlanVoList) { + String ipRange = vlan.getIpRange(); + String[] ipRangeParts = ipRange.split("-"); + String startIP = ipRangeParts[0]; + String endIP = ipRangeParts[1]; + if (NetUtils.isIpInRange(ipv4Address, startIP, endIP)) { + return vlan; + } + } + return null; + } + @Override public List listByZone(long zoneId) { SearchCriteria sc = ZoneSearch.create(); diff --git a/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java index a797b24471a..01b33893b5c 100644 --- a/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java @@ -30,8 +30,8 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; -import com.cloud.dc.VlanVO; import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.VlanVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; @@ -44,10 +44,10 @@ import com.cloud.network.Network; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; +import com.cloud.network.PhysicalNetwork; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; -import com.cloud.network.PhysicalNetwork; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.utils.db.DB; @@ -55,7 +55,6 @@ import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionStatus; -import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; import com.cloud.vm.Nic.ReservationStrategy; @@ -106,10 +105,6 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru { rsStrategy = ReservationStrategy.Create; } - if (nic != null && nic.getRequestedIPv4() != null) { - throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic); - } - if (nic == null) { nic = new NicProfile(rsStrategy, null, null, null, null); } else if (nic.getIPv4Address() == null) { diff --git a/utils/src/main/java/com/cloud/utils/net/NetUtils.java b/utils/src/main/java/com/cloud/utils/net/NetUtils.java index b9762fdae63..958dfc6619d 100644 --- a/utils/src/main/java/com/cloud/utils/net/NetUtils.java +++ b/utils/src/main/java/com/cloud/utils/net/NetUtils.java @@ -479,9 +479,23 @@ public class NetUtils { return validator.isValidInet4Address(ip); } + /** + * Returns true if the given IPv4 address is in the specific Ipv4 range + */ + public static boolean isIpInRange(final String ipInRange, final String startIP, final String endIP) { + if (ipInRange == null || !validIpRange(startIP, endIP)) + return false; + + final long ipInRangeLong = NetUtils.ip2Long(ipInRange); + final long startIPLong = NetUtils.ip2Long(startIP); + final long endIPLong = NetUtils.ip2Long(endIP); + + return startIPLong <= ipInRangeLong && ipInRangeLong <= endIPLong; + } + public static boolean is31PrefixCidr(final String cidr) { final boolean isValidCird = isValidIp4Cidr(cidr); - if (isValidCird){ + if (isValidCird) { final String[] cidrPair = cidr.split("\\/"); final String cidrSize = cidrPair[1];