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 0861a424ebb..fdca6e43f00 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 @@ package com.cloud.network.dao; 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 0aae532eac5..4b299085612 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 @@ public class NetworkDaoImpl extends GenericDaoBaseimplements Ne 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 class NetworkDaoImpl extends GenericDaoBaseimplements Ne 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 21ac6e3eb36..972c6cbea89 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 6769a8066dd..bd9d2ea0cd7 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -4698,23 +4698,75 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } - 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) { - String ntwkDomain = network.getNetworkDomain(); - if (!ntwkDomains.containsKey(ntwkDomain)) { - List ntwkIds = new ArrayList(); - ntwkIds.add(network.getId()); - ntwkDomains.put(ntwkDomain, ntwkIds); - } else { - List ntwkIds = ntwkDomains.get(ntwkDomain); - ntwkIds.add(network.getId()); - ntwkDomains.put(ntwkDomain, ntwkIds); + 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); + } - for (Entry> ntwkDomain : ntwkDomains.entrySet()) { + 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)) { + ntwkIds = new HashSet<>(); + } else { + ntwkIds = ntwkDomains.get(ntwkDomain); + } + ntwkIds.add(network.getId()); + ntwkDomains.put(ntwkDomain, ntwkIds); + } + return ntwkDomains; + } + + 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); @@ -9284,7 +9336,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir 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 490f2ccba46..8a0bec56df7 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 com.cloud.utils.db.SearchBuilder; 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 class MockNetworkDaoImpl extends GenericDaoBase implemen 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(); + } }