diff --git a/api/src/main/java/com/cloud/vm/NicProfile.java b/api/src/main/java/com/cloud/vm/NicProfile.java index d3c1daa1f5da..183c8dcb2d59 100644 --- a/api/src/main/java/com/cloud/vm/NicProfile.java +++ b/api/src/main/java/com/cloud/vm/NicProfile.java @@ -62,6 +62,7 @@ public class NicProfile implements InternalIdentity, Serializable { String iPv4Dns1; String iPv4Dns2; String requestedIPv4; + boolean ipv4AllocationRaceCheck; // IPv6 String iPv6Address; @@ -405,6 +406,13 @@ public void setMtu(Integer mtu) { this.mtu = mtu; } + public boolean getIpv4AllocationRaceCheck() { + return this.ipv4AllocationRaceCheck; + } + + public void setIpv4AllocationRaceCheck(boolean ipv4AllocationRaceCheck) { + this.ipv4AllocationRaceCheck = ipv4AllocationRaceCheck; + } // // OTHER METHODS 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 09500051df67..75ff918e2393 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 @@ -1012,42 +1012,84 @@ public void saveExtraDhcpOptions(final String networkUuid, final Long nicId, fin } } - @DB - @Override - public Pair allocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm) - throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { + private NicVO persistNicAfterRaceCheck(final NicVO nic, final Long networkId, final NicProfile profile, int deviceId) { + return Transaction.execute(new TransactionCallback() { + @Override + public NicVO doInTransaction(TransactionStatus status) { + NicVO vo = _nicDao.findByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId); + if (vo == null) { + applyProfileToNic(nic, profile, deviceId); + vo = _nicDao.persist(nic); + return vo; + } else { + return null; + } + } + }); + } + private NicVO checkForRaceAndAllocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm) + throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { final NetworkVO ntwkVO = _networksDao.findById(network.getId()); s_logger.debug("Allocating nic for vm " + vm.getVirtualMachine() + " in network " + network + " with requested profile " + requested); final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, ntwkVO.getGuruName()); - if (requested != null && requested.getMode() == null) { - requested.setMode(network.getMode()); - } - final NicProfile profile = guru.allocate(network, requested, vm); - if (profile == null) { - return null; - } + NicVO vo = null; + boolean retryIpAllocation; + do { + retryIpAllocation = false; + final NicProfile profile = guru.allocate(network, requested, vm); + if (profile == null) { + return null; + } - if (isDefaultNic != null) { - profile.setDefaultNic(isDefaultNic); - } + if (isDefaultNic != null) { + profile.setDefaultNic(isDefaultNic); + } - if (requested != null && requested.getMode() == null) { - profile.setMode(requested.getMode()); - } else { - profile.setMode(network.getMode()); - } + if (requested != null && requested.getMode() == null) { + profile.setMode(requested.getMode()); + } else { + profile.setMode(network.getMode()); + } + + 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); + } + + if (profile.getIpv4AllocationRaceCheck()) { + vo = persistNicAfterRaceCheck(vo, network.getId(), profile, deviceId); + } else { + applyProfileToNic(vo, profile, deviceId); + vo = _nicDao.persist(vo); + } + + if (vo == null) { + if (requested.getRequestedIPv4() != null) { + throw new InsufficientVirtualNetworkCapacityException("Unable to acquire requested Guest IP address " + requested.getRequestedIPv4() + " for network " + network, DataCenter.class, dcVo.getId()); + } else { + requested.setIPv4Address(null); + } + retryIpAllocation = true; + } + } while (retryIpAllocation); - NicVO vo = new NicVO(guru.getName(), vm.getId(), network.getId(), vm.getType()); + return vo; + } - DataCenterVO dcVo = _dcDao.findById(network.getDataCenterId()); - if (dcVo.getNetworkType() == NetworkType.Basic) { - configureNicProfileBasedOnRequestedIp(requested, profile, network); + @DB + @Override + public Pair allocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm) + throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { + + if (requested != null && requested.getMode() == null) { + requested.setMode(network.getMode()); } - deviceId = applyProfileToNic(vo, profile, deviceId); - vo = _nicDao.persist(vo); + NicVO vo = checkForRaceAndAllocateNic(requested, network, isDefaultNic, deviceId, vm); final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), diff --git a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java index 137d1e7268bf..f9913ade6b6d 100644 --- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java @@ -441,6 +441,7 @@ public NicProfile allocate(final Network network, NicProfile nic, final VirtualM } else { guestIp = _ipAddrMgr.acquireGuestIpAddress(network, nic.getRequestedIPv4()); } + nic.setIpv4AllocationRaceCheck(true); } if (guestIp == null && network.getGuestType() != GuestType.L2 && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) { throw new InsufficientVirtualNetworkCapacityException("Unable to acquire Guest IP" + " address for network " + network, DataCenter.class,