diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java index 0861a424ebb6..fdca6e43f00f 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDao.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; @@ -47,6 +48,12 @@ public interface NetworkDao extends GenericDao, StateDao listByNetworkDomains(Set uniqueNtwkDomains); + + List listByNetworkDomainsAndAccountIds(Set uniqueNtwkDomains, Set accountIds); + + List listByNetworkDomainsAndDomainIds(Set uniqueNtwkDomains, Set domainIds); + /** * Retrieves the next available mac address in this network configuration. * diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java index 0aae532eac57..4b299085612c 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkDaoImpl.java @@ -86,6 +86,7 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne GenericSearchBuilder GarbageCollectedSearch; SearchBuilder PrivateNetworkSearch; + SearchBuilder NetworkDomainSearch; @Inject ResourceTagDao _tagsDao; @@ -198,6 +199,12 @@ protected void init() { PersistentNetworkSearch.join("persistent", persistentNtwkOffJoin, PersistentNetworkSearch.entity().getNetworkOfferingId(), persistentNtwkOffJoin.entity().getId(), JoinType.INNER); PersistentNetworkSearch.done(); + NetworkDomainSearch = createSearchBuilder(); + NetworkDomainSearch.and("networkDomains", NetworkDomainSearch.entity().getNetworkDomain(), Op.IN); + NetworkDomainSearch.and("accounts", NetworkDomainSearch.entity().getAccountId(), Op.IN); + NetworkDomainSearch.and("domains", NetworkDomainSearch.entity().getDomainId(), Op.IN); + NetworkDomainSearch.done(); + PhysicalNetworkSearch = createSearchBuilder(); PhysicalNetworkSearch.and("physicalNetworkId", PhysicalNetworkSearch.entity().getPhysicalNetworkId(), Op.EQ); PhysicalNetworkSearch.done(); @@ -428,6 +435,29 @@ public List getAllPersistentNetworksFromZone(long dataCenterId) { return search(sc, null); } + @Override + public List listByNetworkDomains(Set uniqueNtwkDomains) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + return search(sc, null); + } + + @Override + public List listByNetworkDomainsAndAccountIds(Set uniqueNtwkDomains, Set accountIds) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + sc.setParameters("accounts", accountIds.toArray()); + return search(sc, null); + } + + @Override + public List listByNetworkDomainsAndDomainIds(Set uniqueNtwkDomains, Set domainIds) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + sc.setParameters("domains", domainIds.toArray()); + return search(sc, null); + } + @Override public String getNextAvailableMacAddress(final long networkConfigId, Integer zoneMacIdentifier) { final SequenceFetcher fetch = SequenceFetcher.getInstance(); diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index 21ac6e3eb36e..972c6cbea89d 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -83,6 +83,12 @@ public interface UserVmManager extends UserVmService { "If set to true, tags specified in `resource.limit.host.tags` are also included in vm.strict.host.tags.", true); + ConfigKey VmDistinctHostNameScope = new ConfigKey<>(String.class, "vm.distinct.hostname.scope", ConfigKey.CATEGORY_ADVANCED, + "network", + "Defines the scope for enforcing unique VM hostnames which determines the resource boundary within which VM hostnames must be unique. Possible values: global, domain, subdomain, account, network.", + true, ConfigKey.Scope.Global, null, "VM distinct hostname scope", null, null, null, ConfigKey.Kind.Select, + "global,domain,subdomain,account,network"); + ConfigKey EnableAdditionalVmConfig = new ConfigKey<>( "Advanced", Boolean.class, @@ -92,6 +98,7 @@ public interface UserVmManager extends UserVmService { true, ConfigKey.Scope.Account); + static final int MAX_USER_DATA_LENGTH_BYTES = 2048; public static final String CKS_NODE = "cksnode"; diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index f1bbfa072928..b19e110cf3e0 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -4690,23 +4690,75 @@ protected void verifyIfHypervisorSupportsRootdiskSizeOverride(HypervisorType hyp } } - private void checkIfHostNameUniqueInNtwkDomain(String hostName, List networkList) { - // Check that hostName is unique in the network domain - Map> ntwkDomains = new HashMap>(); + private List getNetworksWithSameNetworkDomainInDomains(List networkList, boolean checkSubDomains) { + Set uniqueNtwkDomains = networkList.stream().map(NetworkVO::getNetworkDomain).collect(Collectors.toSet()); + Set domainIdList = new HashSet<>(); for (Network network : networkList) { + domainIdList.add(network.getDomainId()); + } + Set finalDomainIdSet = new HashSet<>(domainIdList); + if (checkSubDomains) { + for (Long domainId : domainIdList) { + DomainVO domain = _domainDao.findById(domainId); + List childDomainIds = _domainDao.getDomainChildrenIds(domain.getPath()); + finalDomainIdSet.addAll(childDomainIds); + } + } + return _networkDao.listByNetworkDomainsAndDomainIds(uniqueNtwkDomains, finalDomainIdSet); + } + + private List getNetworksForCheckUniqueHostName(List networkList) { + List finalNetworkList; + Set uniqueNtwkDomains; + switch (VmDistinctHostNameScope.value()) { + case "global": + uniqueNtwkDomains = networkList.stream().map(NetworkVO::getNetworkDomain).collect(Collectors.toSet()); + finalNetworkList = _networkDao.listByNetworkDomains(uniqueNtwkDomains); + break; + case "domain": + finalNetworkList = getNetworksWithSameNetworkDomainInDomains(networkList, false); + break; + case "subdomain": + finalNetworkList = getNetworksWithSameNetworkDomainInDomains(networkList, true); + break; + case "account": + uniqueNtwkDomains = networkList.stream().map(NetworkVO::getNetworkDomain).collect(Collectors.toSet()); + Set accountIds = networkList.stream().map(Network::getAccountId).collect(Collectors.toSet()); + finalNetworkList = _networkDao.listByNetworkDomainsAndAccountIds(uniqueNtwkDomains, accountIds); + break; + default: + Set vpcIds = networkList.stream().map(Network::getVpcId).filter(Objects::nonNull).collect(Collectors.toSet()); + finalNetworkList = new ArrayList<>(networkList); + for (Long vpcId : vpcIds) { + finalNetworkList.addAll(_networkDao.listByVpc(vpcId)); + } + break; + } + return finalNetworkList; + } + + private Map> getNetworkIdPerNetworkDomain(List networkList) { + Map> ntwkDomains = new HashMap<>(); + + List updatedNetworkList = getNetworksForCheckUniqueHostName(networkList); + for (Network network : updatedNetworkList) { String ntwkDomain = network.getNetworkDomain(); + Set ntwkIds; if (!ntwkDomains.containsKey(ntwkDomain)) { - List ntwkIds = new ArrayList(); - ntwkIds.add(network.getId()); - ntwkDomains.put(ntwkDomain, ntwkIds); + ntwkIds = new HashSet<>(); } else { - List ntwkIds = ntwkDomains.get(ntwkDomain); - ntwkIds.add(network.getId()); - ntwkDomains.put(ntwkDomain, ntwkIds); + ntwkIds = ntwkDomains.get(ntwkDomain); } + ntwkIds.add(network.getId()); + ntwkDomains.put(ntwkDomain, ntwkIds); } + return ntwkDomains; + } - for (Entry> ntwkDomain : ntwkDomains.entrySet()) { + private void checkIfHostNameUniqueInNtwkDomain(String hostName, List networkList) { + // Check that hostName is unique + Map> ntwkDomains = getNetworkIdPerNetworkDomain(networkList); + for (Entry> ntwkDomain : ntwkDomains.entrySet()) { for (Long ntwkId : ntwkDomain.getValue()) { // * get all vms hostNames in the network List hostNames = _vmInstanceDao.listDistinctHostNames(ntwkId); @@ -9243,7 +9295,7 @@ public ConfigKey[] getConfigKeys() { return new ConfigKey[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties, KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction, - EnforceStrictResourceLimitHostTagCheck, StrictHostTags, AllowUserForceStopVm}; + EnforceStrictResourceLimitHostTagCheck, StrictHostTags, AllowUserForceStopVm, VmDistinctHostNameScope}; } @Override diff --git a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java index 490f2ccba46b..8a0bec56df7b 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; @DB() public class MockNetworkDaoImpl extends GenericDaoBase implements NetworkDao { @@ -265,4 +266,18 @@ public List getAllPersistentNetworksFromZone(long dataCenterId) { return null; } + @Override + public List listByNetworkDomains(Set uniqueNtwkDomains) { + return List.of(); + } + + @Override + public List listByNetworkDomainsAndAccountIds(Set uniqueNtwkDomains, Set accountIds) { + return List.of(); + } + + @Override + public List listByNetworkDomainsAndDomainIds(Set uniqueNtwkDomains, Set domainIds) { + return List.of(); + } }