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..c50d6f899bd1 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 @@ -47,6 +47,12 @@ public interface NetworkDao extends GenericDao, StateDao listByNetworkDomains(List uniqueNtwkDomains); + + List listByNetworkDomainsAndAccountIds(List uniqueNtwkDomains, List accountIds); + + List listByNetworkDomainsAndDomainIds(List uniqueNtwkDomains, List 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 fa448b026e45..7edfdbdbcd2b 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(); @@ -429,6 +436,29 @@ public List getAllPersistentNetworksFromZone(long dataCenterId) { return search(sc, null); } + @Override + public List listByNetworkDomains(List uniqueNtwkDomains) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + return search(sc, null); + } + + @Override + public List listByNetworkDomainsAndAccountIds(List uniqueNtwkDomains, List accountIds) { + SearchCriteria sc = NetworkDomainSearch.create(); + sc.setParameters("networkDomains", uniqueNtwkDomains.toArray()); + sc.setParameters("accounts", accountIds.toArray()); + return search(sc, null); + } + + @Override + public List listByNetworkDomainsAndDomainIds(List uniqueNtwkDomains, List 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 7cd1a98fd743..1a7d1110cd6d 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -59,6 +59,12 @@ public interface UserVmManager extends UserVmService { "Destroys the VM's root volume when the VM is destroyed.", true, ConfigKey.Scope.Domain); + ConfigKey VmDistinctHostNameScope = new ConfigKey<>(String.class, "vm.distinct.hostname.scope", ConfigKey.CATEGORY_ADVANCED, + "network", + "Scope of resources to check while checking if the hostname is unique. Possible values are 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"); + 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 94034da4c8fb..daf0bf06dfc6 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -4429,23 +4429,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) { + List uniqueNtwkDomains = networkList.stream().map(NetworkVO::getNetworkDomain).collect(Collectors.toList()); + List domainIdList = new ArrayList<>(); for (Network network : networkList) { + domainIdList.add(network.getDomainId()); + } + Set finalDomainIdList = new HashSet<>(domainIdList); + if (checkSubDomains) { + for (Long domainId : domainIdList) { + DomainVO domain = _domainDao.findById(domainId); + List childDomainIds = _domainDao.getDomainChildrenIds(domain.getPath()); + finalDomainIdList.addAll(childDomainIds); + } + } + return _networkDao.listByNetworkDomainsAndDomainIds(uniqueNtwkDomains, finalDomainIdList.stream().collect(Collectors.toList())); + } + + private List getNetworksForCheckUniqueHostName(List networkList) { + List finalNetworkList; + List uniqueNtwkDomains; + switch (VmDistinctHostNameScope.value()) { + case "global": + uniqueNtwkDomains = networkList.stream().map(NetworkVO::getNetworkDomain).collect(Collectors.toList()); + 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.toList()); + List accountIds = networkList.stream().map(Network::getAccountId).collect(Collectors.toList()); + 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); @@ -8222,7 +8274,7 @@ public String getConfigComponentName() { public ConfigKey[] getConfigKeys() { return new ConfigKey[] {EnableDynamicallyScaleVm, AllowDiskOfferingChangeDuringScaleVm, AllowUserExpungeRecoverVm, VmIpFetchWaitInterval, VmIpFetchTrialMax, VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties, - KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction}; + KvmAdditionalConfigAllowList, XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, DestroyRootVolumeOnVmDestruction, 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 fe0b3ea23c13..1b74edc2acbe 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockNetworkDaoImpl.java @@ -264,4 +264,19 @@ public List listByPhysicalNetworkPvlan(long physicalNetworkId, String public List getAllPersistentNetworksFromZone(long dataCenterId) { return null; } + + @Override + public List listByNetworkDomains(List uniqueNtwkDomains) { + return List.of(); + } + + @Override + public List listByNetworkDomainsAndAccountIds(List uniqueNtwkDomains, List accountIds) { + return List.of(); + } + + @Override + public List listByNetworkDomainsAndDomainIds(List uniqueNtwkDomains, List domainIds) { + return List.of(); + } }