| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| package com.cloud.user; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.Set; | |
| import java.util.UUID; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| import javax.inject.Inject; | |
| import com.cloud.api.query.dao.NetworkOfferingJoinDao; | |
| import com.cloud.api.query.dao.VpcOfferingJoinDao; | |
| import com.cloud.api.query.vo.NetworkOfferingJoinVO; | |
| import com.cloud.api.query.vo.VpcOfferingJoinVO; | |
| import com.cloud.configuration.Resource; | |
| import com.cloud.domain.dao.DomainDetailsDao; | |
| import com.cloud.network.vpc.dao.VpcOfferingDao; | |
| import com.cloud.network.vpc.dao.VpcOfferingDetailsDao; | |
| import com.cloud.offerings.dao.NetworkOfferingDao; | |
| import com.cloud.offerings.dao.NetworkOfferingDetailsDao; | |
| import com.cloud.exception.ResourceAllocationException; | |
| import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; | |
| import org.apache.cloudstack.annotation.AnnotationService; | |
| import org.apache.cloudstack.annotation.dao.AnnotationDao; | |
| import org.apache.cloudstack.api.ApiConstants; | |
| import org.apache.cloudstack.api.command.admin.domain.ListDomainChildrenCmd; | |
| import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd; | |
| import org.apache.cloudstack.api.command.admin.domain.MoveDomainCmd; | |
| import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; | |
| import org.apache.cloudstack.context.CallContext; | |
| import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; | |
| import org.apache.cloudstack.framework.messagebus.MessageBus; | |
| import org.apache.cloudstack.framework.messagebus.PublishScope; | |
| import org.apache.cloudstack.network.RoutedIpv4Manager; | |
| import org.apache.cloudstack.region.RegionManager; | |
| import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; | |
| import org.apache.commons.collections.CollectionUtils; | |
| import org.apache.commons.lang.BooleanUtils; | |
| import org.springframework.stereotype.Component; | |
| import com.cloud.api.query.dao.DiskOfferingJoinDao; | |
| import com.cloud.api.query.dao.ServiceOfferingJoinDao; | |
| import com.cloud.api.query.vo.DiskOfferingJoinVO; | |
| import com.cloud.api.query.vo.ServiceOfferingJoinVO; | |
| import com.cloud.configuration.ConfigurationManager; | |
| import com.cloud.configuration.Resource.ResourceOwnerType; | |
| import com.cloud.configuration.ResourceLimit; | |
| import com.cloud.configuration.dao.ResourceCountDao; | |
| import com.cloud.configuration.dao.ResourceLimitDao; | |
| import com.cloud.dc.DedicatedResourceVO; | |
| import com.cloud.dc.dao.DedicatedResourceDao; | |
| import com.cloud.domain.Domain; | |
| import com.cloud.domain.DomainVO; | |
| import com.cloud.domain.dao.DomainDao; | |
| import com.cloud.event.ActionEvent; | |
| import com.cloud.event.EventTypes; | |
| import com.cloud.exception.ConcurrentOperationException; | |
| import com.cloud.exception.InvalidParameterValueException; | |
| import com.cloud.exception.PermissionDeniedException; | |
| import com.cloud.exception.ResourceUnavailableException; | |
| import com.cloud.network.dao.NetworkDomainDao; | |
| import com.cloud.projects.ProjectManager; | |
| import com.cloud.projects.ProjectVO; | |
| import com.cloud.projects.dao.ProjectDao; | |
| import com.cloud.service.dao.ServiceOfferingDao; | |
| import com.cloud.service.dao.ServiceOfferingDetailsDao; | |
| import com.cloud.storage.dao.DiskOfferingDao; | |
| import com.cloud.user.dao.AccountDao; | |
| import com.cloud.utils.Pair; | |
| import com.cloud.utils.component.ManagerBase; | |
| import com.cloud.utils.db.DB; | |
| import com.cloud.utils.db.Filter; | |
| import com.cloud.utils.db.GlobalLock; | |
| import com.cloud.utils.db.SearchBuilder; | |
| import com.cloud.utils.db.SearchCriteria; | |
| import com.cloud.utils.db.Transaction; | |
| import com.cloud.utils.db.TransactionCallback; | |
| import com.cloud.utils.db.TransactionCallbackNoReturn; | |
| import com.cloud.utils.db.TransactionStatus; | |
| import com.cloud.utils.exception.CloudRuntimeException; | |
| import com.cloud.utils.net.NetUtils; | |
| import com.cloud.vm.ReservationContext; | |
| import com.cloud.vm.ReservationContextImpl; | |
| import org.apache.commons.lang3.StringUtils; | |
| @Component | |
| public class DomainManagerImpl extends ManagerBase implements DomainManager, DomainService { | |
| @Inject | |
| private DomainDao _domainDao; | |
| @Inject | |
| private AccountManager _accountMgr; | |
| @Inject | |
| private ResourceCountDao _resourceCountDao; | |
| @Inject | |
| private AccountDao _accountDao; | |
| @Inject | |
| private DiskOfferingJoinDao diskOfferingJoinDao; | |
| @Inject | |
| private DiskOfferingDao diskOfferingDao; | |
| @Inject | |
| private DiskOfferingDetailsDao diskOfferingDetailsDao; | |
| @Inject | |
| private NetworkOfferingDao networkOfferingDao; | |
| @Inject | |
| private NetworkOfferingJoinDao networkOfferingJoinDao; | |
| @Inject | |
| private NetworkOfferingDetailsDao networkOfferingDetailsDao; | |
| @Inject | |
| private ServiceOfferingJoinDao serviceOfferingJoinDao; | |
| @Inject | |
| private ServiceOfferingDao serviceOfferingDao; | |
| @Inject | |
| private ServiceOfferingDetailsDao serviceOfferingDetailsDao; | |
| @Inject | |
| private VpcOfferingDao vpcOfferingDao; | |
| @Inject | |
| private VpcOfferingJoinDao vpcOfferingJoinDao; | |
| @Inject | |
| private VpcOfferingDetailsDao vpcOfferingDetailsDao; | |
| @Inject | |
| private ProjectDao _projectDao; | |
| @Inject | |
| private ProjectManager _projectMgr; | |
| @Inject | |
| private RegionManager _regionMgr; | |
| @Inject | |
| private ResourceLimitDao _resourceLimitDao; | |
| @Inject | |
| private DedicatedResourceDao _dedicatedDao; | |
| @Inject | |
| private NetworkOrchestrationService _networkMgr; | |
| @Inject | |
| private NetworkDomainDao _networkDomainDao; | |
| @Inject | |
| private ConfigurationManager _configMgr; | |
| @Inject | |
| private DomainDetailsDao _domainDetailsDao; | |
| @Inject | |
| private AnnotationDao annotationDao; | |
| @Inject | |
| private ResourceLimitService resourceLimitService; | |
| @Inject | |
| private AffinityGroupDomainMapDao affinityGroupDomainMapDao; | |
| @Inject | |
| private RoutedIpv4Manager routedIpv4Manager; | |
| @Inject | |
| MessageBus _messageBus; | |
| protected GlobalLock getGlobalLock(String name) { | |
| return GlobalLock.getInternLock(name); | |
| } | |
| protected Account getCaller() { | |
| return CallContext.current().getCallingAccount(); | |
| } | |
| @Override | |
| public Domain getDomain(long domainId) { | |
| return _domainDao.findById(domainId); | |
| } | |
| @Override | |
| public Domain getDomain(String domainUuid) { | |
| return _domainDao.findByUuid(domainUuid); | |
| } | |
| @Override | |
| public Domain getDomainByName(String name, long parentId) { | |
| SearchCriteria<DomainVO> sc = _domainDao.createSearchCriteria(); | |
| sc.addAnd("name", SearchCriteria.Op.EQ, name); | |
| sc.addAnd("parent", SearchCriteria.Op.EQ, parentId); | |
| Domain domain = _domainDao.findOneBy(sc); | |
| return domain; | |
| } | |
| @Override | |
| public Set<Long> getDomainChildrenIds(String parentDomainPath) { | |
| Set<Long> childDomains = new HashSet<Long>(); | |
| SearchCriteria<DomainVO> sc = _domainDao.createSearchCriteria(); | |
| sc.addAnd("path", SearchCriteria.Op.LIKE, parentDomainPath + "%"); | |
| List<DomainVO> domains = _domainDao.search(sc, null); | |
| for (DomainVO domain : domains) { | |
| childDomains.add(domain.getId()); | |
| } | |
| return childDomains; | |
| } | |
| @Override | |
| public boolean isChildDomain(Long parentId, Long childId) { | |
| return _domainDao.isChildDomain(parentId, childId); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_CREATE, eventDescription = "creating Domain") | |
| public Domain createDomain(String name, Long parentId, String networkDomain, String domainUUID) { | |
| Account caller = getCaller(); | |
| if (parentId == null) { | |
| parentId = Long.valueOf(Domain.ROOT_DOMAIN); | |
| } | |
| DomainVO parentDomain = _domainDao.findById(parentId); | |
| if (parentDomain == null) { | |
| throw new InvalidParameterValueException("Unable to create domain " + name + ", parent domain " + parentId + " not found."); | |
| } | |
| if (parentDomain.getState().equals(Domain.State.Inactive)) { | |
| throw new CloudRuntimeException("The domain cannot be created as the parent domain " + parentDomain.getName() + " is being deleted"); | |
| } | |
| _accountMgr.checkAccess(caller, parentDomain); | |
| return createDomain(name, parentId, caller.getId(), networkDomain, domainUUID); | |
| } | |
| @Override | |
| @DB | |
| public Domain createDomain(final String name, final Long parentId, final Long ownerId, final String networkDomain, String domainUuid) { | |
| validateDomainNameAndNetworkDomain(name, parentId, networkDomain); | |
| DomainVO domainVO = createDomainVo(name, parentId, ownerId, networkDomain, domainUuid); | |
| DomainVO domain = Transaction.execute(new TransactionCallback<DomainVO>() { | |
| @Override | |
| public DomainVO doInTransaction(TransactionStatus status) { | |
| DomainVO domain = _domainDao.create(domainVO); | |
| _resourceCountDao.createResourceCounts(domain.getId(), ResourceLimit.ResourceOwnerType.Domain); | |
| CallContext.current().putContextParameter(Domain.class, domain.getUuid()); | |
| return domain; | |
| } | |
| }); | |
| if (domain != null) { | |
| _messageBus.publish(_name, MESSAGE_ADD_DOMAIN_EVENT, PublishScope.LOCAL, domain.getId()); | |
| _messageBus.publish(_name, MESSAGE_CREATE_TUNGSTEN_DOMAIN_EVENT, PublishScope.LOCAL, domain); | |
| } | |
| return domain; | |
| } | |
| protected DomainVO createDomainVo(String name, Long parentId, Long ownerId, String networkDomain, String domainUuid) { | |
| if (StringUtils.isBlank(domainUuid)) { | |
| domainUuid = UUID.randomUUID().toString(); | |
| logger.info(String.format("Domain UUID [%s] generated for domain name [%s].", domainUuid, name)); | |
| } | |
| DomainVO domainVO = new DomainVO(name, ownerId, parentId, networkDomain, domainUuid); | |
| return domainVO; | |
| } | |
| protected void validateDomainNameAndNetworkDomain(String name, Long parentId, String networkDomain) { | |
| validateNetworkDomain(networkDomain); | |
| validateUniqueDomainName(name, parentId); | |
| } | |
| protected void validateUniqueDomainName(String name, Long parentId) { | |
| SearchCriteria<DomainVO> sc = _domainDao.createSearchCriteria(); | |
| sc.addAnd("name", SearchCriteria.Op.EQ, name); | |
| sc.addAnd("parent", SearchCriteria.Op.EQ, parentId); | |
| List<DomainVO> domains = _domainDao.search(sc, null); | |
| if (!domains.isEmpty()) { | |
| throw new InvalidParameterValueException(String.format("Domain with name [%s] already exists for the parent with ID [%s].", name, parentId)); | |
| } | |
| } | |
| protected void validateNetworkDomain(String networkDomain) { | |
| if (networkDomain != null && !NetUtils.verifyDomainName(networkDomain)) { | |
| throw new InvalidParameterValueException( | |
| "Invalid network domain. Total length should not exceed 190 chars. Each domain label must be between 1 and 63 characters long." + | |
| " It can contain ASCII letters 'a' through 'z', the digits '0' through '9', and the hyphen ('-'); it cannot start or end with \"-\"." | |
| ); | |
| } | |
| } | |
| @Override | |
| public DomainVO findDomainByPath(String domainPath) { | |
| return _domainDao.findDomainByPath(domainPath); | |
| } | |
| @Override | |
| public Domain findDomainByIdOrPath(final Long id, final String domainPath) { | |
| Long domainId = id; | |
| if (domainId == null || domainId < 1L) { | |
| if (StringUtils.isBlank(domainPath)) { | |
| domainId = Domain.ROOT_DOMAIN; | |
| } else { | |
| final Domain domainVO = findDomainByPath(domainPath.trim()); | |
| if (domainVO != null) { | |
| return domainVO; | |
| } | |
| } | |
| } | |
| if (domainId != null && domainId > 0L) { | |
| return _domainDao.findById(domainId); | |
| } | |
| return null; | |
| } | |
| @Override | |
| public Set<Long> getDomainParentIds(long domainId) { | |
| return _domainDao.getDomainParentIds(domainId); | |
| } | |
| @Override | |
| public boolean removeDomain(long domainId) { | |
| return _domainDao.remove(domainId); | |
| } | |
| @Override | |
| public List<? extends Domain> findInactiveDomains() { | |
| return _domainDao.findInactiveDomains(); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_DELETE, eventDescription = "deleting Domain", async = true) | |
| public boolean deleteDomain(long domainId, Boolean cleanup) { | |
| Account caller = getCaller(); | |
| DomainVO domain = _domainDao.findById(domainId); | |
| if (domain == null) { | |
| throw new InvalidParameterValueException("Failed to delete domain " + domainId + ", domain not found"); | |
| } else if (domainId == Domain.ROOT_DOMAIN) { | |
| throw new PermissionDeniedException("Can't delete ROOT domain"); | |
| } | |
| _accountMgr.checkAccess(caller, domain); | |
| return deleteDomain(domain, cleanup); | |
| } | |
| @Override | |
| public boolean deleteDomain(DomainVO domain, Boolean cleanup) { | |
| GlobalLock lock = getGlobalLock(); | |
| if (lock == null) return false; | |
| try { | |
| // mark domain as inactive | |
| logger.debug("Marking domain {} as {} before actually deleting it", domain, Domain.State.Inactive); | |
| domain.setState(Domain.State.Inactive); | |
| _domainDao.update(domain.getId(), domain); | |
| return cleanDomain(domain, cleanup); | |
| } | |
| finally { | |
| lock.unlock(); | |
| } | |
| } | |
| private GlobalLock getGlobalLock() { | |
| GlobalLock lock = getGlobalLock("DomainCleanup"); | |
| if (lock == null) { | |
| logger.debug("Couldn't get the global lock"); | |
| return null; | |
| } | |
| if (!lock.lock(30)) { | |
| logger.debug("Couldn't lock the db"); | |
| return null; | |
| } | |
| return lock; | |
| } | |
| private boolean cleanDomain(DomainVO domain, Boolean cleanup) { | |
| try { | |
| long ownerId = domain.getAccountId(); | |
| if (BooleanUtils.toBoolean(cleanup)) { | |
| tryCleanupDomain(domain, ownerId); | |
| } else { | |
| removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources(domain); | |
| } | |
| // remove dedicated IPv4 subnets | |
| routedIpv4Manager.removeIpv4SubnetsForZoneByDomainId(domain.getId()); | |
| // remove dedicated BGP peers | |
| routedIpv4Manager.removeBgpPeersByDomainId(domain.getId()); | |
| if (!_configMgr.releaseDomainSpecificVirtualRanges(domain)) { | |
| CloudRuntimeException e = new CloudRuntimeException("Can't delete the domain yet because failed to release domain specific virtual ip ranges"); | |
| e.addProxyObject(domain.getUuid(), "domainId"); | |
| throw e; | |
| } else { | |
| logger.debug("Domain specific Virtual IP ranges are successfully released as a part of domain {} cleanup.", domain); | |
| } | |
| cleanupDomainDetails(domain.getId()); | |
| cleanupDomainOfferings(domain.getId()); | |
| annotationDao.removeByEntityType(AnnotationService.EntityType.DOMAIN.name(), domain.getUuid()); | |
| CallContext.current().putContextParameter(Domain.class, domain.getUuid()); | |
| return true; | |
| } catch (Exception ex) { | |
| logger.error("Exception deleting domain {}", domain, ex); | |
| if (ex instanceof CloudRuntimeException) { | |
| rollbackDomainState(domain); | |
| throw (CloudRuntimeException)ex; | |
| } | |
| else | |
| return false; | |
| } | |
| } | |
| /** | |
| * Roll back domain state to Active | |
| * @param domain domain | |
| */ | |
| protected void rollbackDomainState(DomainVO domain) { | |
| logger.debug("Changing domain {} state back to {} because it can't be removed due to resources referencing to it", domain, Domain.State.Active); | |
| domain.setState(Domain.State.Active); | |
| _domainDao.update(domain.getId(), domain); | |
| } | |
| /** | |
| * Try cleaning up domain. If it couldn't throws CloudRuntimeException | |
| * @param domain domain | |
| * @param ownerId owner id | |
| * @throws ConcurrentOperationException | |
| * @throws ResourceUnavailableException | |
| * @throws CloudRuntimeException when cleanupDomain | |
| */ | |
| protected void tryCleanupDomain(DomainVO domain, long ownerId) throws ConcurrentOperationException, ResourceUnavailableException, CloudRuntimeException { | |
| if (!cleanupDomain(domain.getId(), ownerId)) { | |
| CloudRuntimeException e = | |
| new CloudRuntimeException(String.format("Failed to clean up domain resources and sub domains, delete failed on domain %s", domain)); | |
| e.addProxyObject(domain.getUuid(), "domainId"); | |
| throw e; | |
| } | |
| } | |
| /** | |
| * First check domain resources before removing domain. There are 2 cases: | |
| * <ol> | |
| * <li>Domain doesn't have accounts for cleanup, non-removed networks, or dedicated resources</li> | |
| * <ul><li>Delete domain</li></ul> | |
| * <li>Domain has one of the following: accounts set for cleanup, non-removed networks, dedicated resources</li> | |
| * <ul><li>Dont' delete domain</li><li>Fail operation</li></ul> | |
| * </ol> | |
| * @param domain domain to remove | |
| * @throws CloudRuntimeException when case 2 or when domain cannot be deleted on case 1 | |
| */ | |
| protected void removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources(DomainVO domain) { | |
| boolean hasDedicatedResources = false; | |
| List<Long> networkIds = _networkDomainDao.listNetworkIdsByDomain(domain.getId()); | |
| List<AccountVO> accountsForCleanup = _accountDao.findCleanupsForRemovedAccounts(domain.getId()); | |
| List<DedicatedResourceVO> dedicatedResources = _dedicatedDao.listByDomainId(domain.getId()); | |
| if (CollectionUtils.isNotEmpty(dedicatedResources)) { | |
| logger.error("There are dedicated resources for the domain {}", domain); | |
| hasDedicatedResources = true; | |
| } | |
| if (accountsForCleanup.isEmpty() && networkIds.isEmpty() && !hasDedicatedResources) { | |
| publishRemoveEventsAndRemoveDomain(domain); | |
| } else { | |
| failRemoveOperation(domain, accountsForCleanup, networkIds, hasDedicatedResources); | |
| } | |
| } | |
| /** | |
| * Fail domain remove operation including proper message | |
| * @param domain domain | |
| * @param accountsForCleanup domain accounts for cleanup | |
| * @param networkIds domain network ids | |
| * @param hasDedicatedResources indicates if domain has dedicated resources | |
| * @throws CloudRuntimeException including descriptive message indicating the reason for failure | |
| */ | |
| protected void failRemoveOperation(DomainVO domain, List<AccountVO> accountsForCleanup, List<Long> networkIds, boolean hasDedicatedResources) { | |
| String msg = null; | |
| if (!accountsForCleanup.isEmpty()) { | |
| msg = accountsForCleanup.size() + " accounts to cleanup"; | |
| } else if (!networkIds.isEmpty()) { | |
| msg = networkIds.size() + " non-removed networks"; | |
| } else if (hasDedicatedResources) { | |
| msg = "dedicated resources."; | |
| } | |
| CloudRuntimeException e = new CloudRuntimeException("Can't delete the domain yet because it has " + msg); | |
| e.addProxyObject(domain.getUuid(), "domainId"); | |
| throw e; | |
| } | |
| /** | |
| * Publish pre-remove and remove domain events and remove domain | |
| * @param domain domain to remove | |
| * @throws CloudRuntimeException when domain cannot be removed | |
| */ | |
| protected void publishRemoveEventsAndRemoveDomain(DomainVO domain) { | |
| _messageBus.publish(_name, MESSAGE_PRE_REMOVE_DOMAIN_EVENT, PublishScope.LOCAL, domain); | |
| if (!_domainDao.remove(domain.getId())) { | |
| CloudRuntimeException e = | |
| new CloudRuntimeException(String.format("Delete failed on domain %s; Please make sure all users and sub domains have been removed from the domain before deleting", domain)); | |
| e.addProxyObject(domain.getUuid(), "domainId"); | |
| throw e; | |
| } | |
| _messageBus.publish(_name, MESSAGE_REMOVE_DOMAIN_EVENT, PublishScope.LOCAL, domain); | |
| } | |
| protected void cleanupDomainDetails(Long domainId) { | |
| _domainDetailsDao.deleteDetails(domainId); | |
| } | |
| protected void cleanupDomainOfferings(Long domainId) { | |
| if (domainId == null) { | |
| return; | |
| } | |
| String domainIdString = String.valueOf(domainId); | |
| removeDiskOfferings(domainId, domainIdString); | |
| removeServiceOfferings(domainId, domainIdString); | |
| removeNetworkOfferings(domainId, domainIdString); | |
| removeVpcOfferings(domainId, domainIdString); | |
| } | |
| private void removeVpcOfferings(Long domainId, String domainIdString) { | |
| List<Long> vpcOfferingsDetailsToRemove = new ArrayList<>(); | |
| List<VpcOfferingJoinVO> vpcOfferingsForThisDomain = vpcOfferingJoinDao.findByDomainId(domainId); | |
| for (VpcOfferingJoinVO vpcOffering : vpcOfferingsForThisDomain) { | |
| if (domainIdString.equals(vpcOffering.getDomainId())) { | |
| vpcOfferingDao.remove(vpcOffering.getId()); | |
| } else { | |
| vpcOfferingsDetailsToRemove.add(vpcOffering.getId()); | |
| } | |
| } | |
| for (final Long vpcOfferingId : vpcOfferingsDetailsToRemove) { | |
| vpcOfferingDetailsDao.removeDetail(vpcOfferingId, ApiConstants.DOMAIN_ID, domainIdString); | |
| } | |
| } | |
| private void removeNetworkOfferings(Long domainId, String domainIdString) { | |
| List<Long> networkOfferingsDetailsToRemove = new ArrayList<>(); | |
| List<NetworkOfferingJoinVO> networkOfferingsForThisDomain = networkOfferingJoinDao.findByDomainId(domainId, false); | |
| for (NetworkOfferingJoinVO networkOffering : networkOfferingsForThisDomain) { | |
| if (domainIdString.equals(networkOffering.getDomainId())) { | |
| networkOfferingDao.remove(networkOffering.getId()); | |
| } else { | |
| networkOfferingsDetailsToRemove.add(networkOffering.getId()); | |
| } | |
| } | |
| for (final Long networkOfferingId : networkOfferingsDetailsToRemove) { | |
| networkOfferingDetailsDao.removeDetail(networkOfferingId, ApiConstants.DOMAIN_ID, domainIdString); | |
| } | |
| } | |
| private void removeServiceOfferings(Long domainId, String domainIdString) { | |
| List<Long> serviceOfferingsDetailsToRemove = new ArrayList<>(); | |
| List<ServiceOfferingJoinVO> serviceOfferingsForThisDomain = serviceOfferingJoinDao.findByDomainId(domainId); | |
| for (ServiceOfferingJoinVO serviceOffering : serviceOfferingsForThisDomain) { | |
| if (domainIdString.equals(serviceOffering.getDomainId())) { | |
| serviceOfferingDao.remove(serviceOffering.getId()); | |
| } else { | |
| serviceOfferingsDetailsToRemove.add(serviceOffering.getId()); | |
| } | |
| } | |
| for (final Long serviceOfferingId : serviceOfferingsDetailsToRemove) { | |
| serviceOfferingDetailsDao.removeDetail(serviceOfferingId, ApiConstants.DOMAIN_ID, domainIdString); | |
| } | |
| } | |
| private void removeDiskOfferings(Long domainId, String domainIdString) { | |
| List<Long> diskOfferingsDetailsToRemove = new ArrayList<>(); | |
| List<DiskOfferingJoinVO> diskOfferingsForThisDomain = diskOfferingJoinDao.findByDomainId(domainId); | |
| for (DiskOfferingJoinVO diskOffering : diskOfferingsForThisDomain) { | |
| if (domainIdString.equals(diskOffering.getDomainId())) { | |
| diskOfferingDao.remove(diskOffering.getId()); | |
| } else { | |
| diskOfferingsDetailsToRemove.add(diskOffering.getId()); | |
| } | |
| } | |
| // Remove domain IDs for offerings which may be multi-domain | |
| for (final Long diskOfferingId : diskOfferingsDetailsToRemove) { | |
| diskOfferingDetailsDao.removeDetail(diskOfferingId, ApiConstants.DOMAIN_ID, domainIdString); | |
| } | |
| } | |
| protected boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOperationException, ResourceUnavailableException { | |
| boolean success = true; | |
| DomainVO domainHandle = _domainDao.findById(domainId); | |
| logger.debug("Cleaning up domain {}", domainHandle); | |
| { | |
| domainHandle.setState(Domain.State.Inactive); | |
| _domainDao.update(domainId, domainHandle); | |
| SearchCriteria<DomainVO> sc = _domainDao.createSearchCriteria(); | |
| sc.addAnd("parent", SearchCriteria.Op.EQ, domainId); | |
| List<DomainVO> domains = _domainDao.search(sc, null); | |
| SearchCriteria<DomainVO> sc1 = _domainDao.createSearchCriteria(); | |
| sc1.addAnd("path", SearchCriteria.Op.LIKE, "%" + "replace(" + domainHandle.getPath() + ", '%', '[%]')" + "%"); | |
| List<DomainVO> domainsToBeInactivated = _domainDao.search(sc1, null); | |
| // update all subdomains to inactive so no accounts/users can be created | |
| for (DomainVO domain : domainsToBeInactivated) { | |
| domain.setState(Domain.State.Inactive); | |
| _domainDao.update(domain.getId(), domain); | |
| } | |
| // cleanup sub-domains first | |
| for (DomainVO domain : domains) { | |
| success = (success && cleanupDomain(domain.getId(), domain.getAccountId())); | |
| if (!success) { | |
| logger.warn("Failed to cleanup domain {}", domain); | |
| } | |
| } | |
| } | |
| // delete users which will also delete accounts and release resources for those accounts | |
| SearchCriteria<AccountVO> sc = _accountDao.createSearchCriteria(); | |
| sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); | |
| List<AccountVO> accounts = _accountDao.search(sc, null); | |
| for (AccountVO account : accounts) { | |
| if (account.getType() != Account.Type.PROJECT) { | |
| logger.debug("Deleting account {} as a part of domain {} cleanup", account, domainHandle); | |
| boolean deleteAccount = _accountMgr.deleteAccount(account, CallContext.current().getCallingUserId(), getCaller()); | |
| if (!deleteAccount) { | |
| logger.warn("Failed to cleanup account {} as a part of domain cleanup", account); | |
| } | |
| success = (success && deleteAccount); | |
| } else { | |
| ProjectVO project = _projectDao.findByProjectAccountId(account.getId()); | |
| logger.debug("Deleting project {} as a part of domain {} cleanup", project, domainHandle); | |
| boolean deleteProject = _projectMgr.deleteProject(getCaller(), CallContext.current().getCallingUserId(), project); | |
| if (!deleteProject) { | |
| logger.warn("Failed to cleanup project " + project + " as a part of domain cleanup"); | |
| } | |
| success = (success && deleteProject); | |
| } | |
| } | |
| //delete the domain shared networks | |
| boolean networksDeleted = true; | |
| logger.debug("Deleting networks for domain {}", domainHandle); | |
| List<Long> networkIds = _networkDomainDao.listNetworkIdsByDomain(domainId); | |
| CallContext ctx = CallContext.current(); | |
| ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(ctx.getCallingUserId()), ctx.getCallingAccount()); | |
| for (Long networkId : networkIds) { | |
| logger.debug("Deleting network id={} as a part of domain {} cleanup", networkId, domainHandle); | |
| if (!_networkMgr.destroyNetwork(networkId, context, false)) { | |
| logger.warn("Unable to destroy network id={} as a part of domain {} cleanup.", networkId, domainHandle); | |
| networksDeleted = false; | |
| } else { | |
| logger.debug("Network {} successfully deleted as a part of domain {} cleanup.", networkId, domainHandle); | |
| } | |
| } | |
| //don't proceed if networks failed to cleanup. The cleanup will be performed for inactive domain once again | |
| if (!networksDeleted) { | |
| logger.debug("Failed to delete the shared networks as a part of domain {} cleanup", domainHandle); | |
| return false; | |
| } | |
| // don't remove the domain if there are accounts required cleanup | |
| boolean deleteDomainSuccess = true; | |
| List<AccountVO> accountsForCleanup = _accountDao.findCleanupsForRemovedAccounts(domainId); | |
| if (accountsForCleanup.isEmpty()) { | |
| //release dedication if any, before deleting the domain | |
| List<DedicatedResourceVO> dedicatedResources = _dedicatedDao.listByDomainId(domainId); | |
| if (dedicatedResources != null && !dedicatedResources.isEmpty()) { | |
| logger.debug("Releasing dedicated resources for domain {}", domainHandle); | |
| for (DedicatedResourceVO dr : dedicatedResources) { | |
| if (!_dedicatedDao.remove(dr.getId())) { | |
| logger.warn("Fail to release dedicated resources for domain {}", domainHandle); | |
| return false; | |
| } | |
| } | |
| } | |
| //delete domain | |
| _messageBus.publish(_name, MESSAGE_PRE_REMOVE_DOMAIN_EVENT, PublishScope.LOCAL, domainHandle); | |
| deleteDomainSuccess = _domainDao.remove(domainId); | |
| _messageBus.publish(_name, MESSAGE_REMOVE_DOMAIN_EVENT, PublishScope.LOCAL, domainHandle); | |
| // Delete resource count and resource limits entries set for this domain (if there are any). | |
| _resourceCountDao.removeEntriesByOwner(domainId, ResourceOwnerType.Domain); | |
| _resourceLimitDao.removeEntriesByOwner(domainId, ResourceOwnerType.Domain); | |
| } else { | |
| logger.debug("Can't delete the domain yet because it has " + accountsForCleanup.size() + "accounts that need a cleanup"); | |
| return false; | |
| } | |
| return success && deleteDomainSuccess; | |
| } | |
| @Override | |
| public Pair<List<? extends Domain>, Integer> searchForDomains(ListDomainsCmd cmd) { | |
| Account caller = getCaller(); | |
| Long domainId = cmd.getId(); | |
| boolean listAll = cmd.listAll(); | |
| boolean isRecursive = false; | |
| if (domainId != null) { | |
| Domain domain = getDomain(domainId); | |
| if (domain == null) { | |
| throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist"); | |
| } | |
| _accountMgr.checkAccess(caller, domain); | |
| } else { | |
| if (!_accountMgr.isRootAdmin(caller.getId())) { | |
| domainId = caller.getDomainId(); | |
| } | |
| if (listAll) { | |
| isRecursive = true; | |
| } | |
| } | |
| Filter searchFilter = new Filter(DomainVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); | |
| String domainName = cmd.getDomainName(); | |
| Integer level = cmd.getLevel(); | |
| Object keyword = cmd.getKeyword(); | |
| SearchBuilder<DomainVO> sb = _domainDao.createSearchBuilder(); | |
| sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); | |
| sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); | |
| sb.and("level", sb.entity().getLevel(), SearchCriteria.Op.EQ); | |
| sb.and("path", sb.entity().getPath(), SearchCriteria.Op.LIKE); | |
| sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); | |
| SearchCriteria<DomainVO> sc = sb.create(); | |
| if (keyword != null) { | |
| SearchCriteria<DomainVO> ssc = _domainDao.createSearchCriteria(); | |
| ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); | |
| sc.addAnd("name", SearchCriteria.Op.SC, ssc); | |
| } | |
| if (domainName != null) { | |
| sc.setParameters("name", domainName); | |
| } | |
| if (level != null) { | |
| sc.setParameters("level", level); | |
| } | |
| if (domainId != null) { | |
| if (isRecursive) { | |
| sc.setParameters("path", getDomain(domainId).getPath() + "%"); | |
| } else { | |
| sc.setParameters("id", domainId); | |
| } | |
| } | |
| // return only Active domains to the API | |
| sc.setParameters("state", Domain.State.Active); | |
| Pair<List<DomainVO>, Integer> result = _domainDao.searchAndCount(sc, searchFilter); | |
| return new Pair<List<? extends Domain>, Integer>(result.first(), result.second()); | |
| } | |
| @Override | |
| public Pair<List<? extends Domain>, Integer> searchForDomainChildren(ListDomainChildrenCmd cmd) throws PermissionDeniedException { | |
| Long domainId = cmd.getId(); | |
| String domainName = cmd.getDomainName(); | |
| Boolean isRecursive = cmd.isRecursive(); | |
| Object keyword = cmd.getKeyword(); | |
| boolean listAll = cmd.listAll(); | |
| String path = null; | |
| Account caller = getCaller(); | |
| if (domainId != null) { | |
| _accountMgr.checkAccess(caller, getDomain(domainId)); | |
| } else { | |
| domainId = caller.getDomainId(); | |
| } | |
| DomainVO domain = _domainDao.findById(domainId); | |
| if (domain != null && isRecursive && !listAll) { | |
| path = domain.getPath(); | |
| domainId = null; | |
| } | |
| Filter searchFilter = new Filter(DomainVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); | |
| Pair<List<DomainVO>, Integer> result = searchForDomainChildren(searchFilter, domainId, domainName, keyword, path, true); | |
| return new Pair<List<? extends Domain>, Integer>(result.first(), result.second()); | |
| } | |
| private Pair<List<DomainVO>, Integer> searchForDomainChildren(Filter searchFilter, Long domainId, String domainName, Object keyword, String path, | |
| boolean listActiveOnly) { | |
| SearchCriteria<DomainVO> sc = _domainDao.createSearchCriteria(); | |
| if (keyword != null) { | |
| SearchCriteria<DomainVO> ssc = _domainDao.createSearchCriteria(); | |
| ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); | |
| sc.addAnd("name", SearchCriteria.Op.SC, ssc); | |
| } | |
| if (domainId != null) { | |
| sc.addAnd("parent", SearchCriteria.Op.EQ, domainId); | |
| } | |
| if (domainName != null) { | |
| sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + domainName + "%"); | |
| } | |
| if (path != null) { | |
| sc.addAnd("path", SearchCriteria.Op.NEQ, path); | |
| sc.addAnd("path", SearchCriteria.Op.LIKE, path + "%"); | |
| } | |
| if (listActiveOnly) { | |
| sc.addAnd("state", SearchCriteria.Op.EQ, Domain.State.Active); | |
| } | |
| return _domainDao.searchAndCount(sc, searchFilter); | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_UPDATE, eventDescription = "updating Domain") | |
| @DB | |
| public DomainVO updateDomain(UpdateDomainCmd cmd) { | |
| final Long domainId = cmd.getId(); | |
| final String domainName = cmd.getDomainName(); | |
| final String networkDomain = cmd.getNetworkDomain(); | |
| // check if domain exists in the system | |
| final DomainVO domain = _domainDao.findById(domainId); | |
| if (domain == null) { | |
| InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find domain with specified domain id"); | |
| ex.addProxyObject(domainId.toString(), "domainId"); | |
| throw ex; | |
| } else if (domain.getParent() == null && domainName != null) { | |
| // check if domain is ROOT domain - and deny to edit it with the new name | |
| throw new InvalidParameterValueException("ROOT domain can not be edited with a new name"); | |
| } | |
| // check permissions | |
| Account caller = getCaller(); | |
| _accountMgr.checkAccess(caller, domain); | |
| // domain name is unique in the cloud | |
| if (domainName != null) { | |
| SearchCriteria<DomainVO> sc = _domainDao.createSearchCriteria(); | |
| sc.addAnd("name", SearchCriteria.Op.EQ, domainName); | |
| sc.addAnd("parent", SearchCriteria.Op.EQ, domain.getParent()); | |
| List<DomainVO> domains = _domainDao.search(sc, null); | |
| boolean sameDomain = (domains.size() == 1 && domains.get(0).getId() == domainId); | |
| if (!domains.isEmpty() && !sameDomain) { | |
| InvalidParameterValueException ex = | |
| new InvalidParameterValueException("Failed to update specified domain id with name '" + domainName + "' since it already exists in the system"); | |
| ex.addProxyObject(domain.getUuid(), "domainId"); | |
| throw ex; | |
| } | |
| } | |
| // validate network domain | |
| if (networkDomain != null && !networkDomain.isEmpty()) { | |
| if (!NetUtils.verifyDomainName(networkDomain)) { | |
| throw new InvalidParameterValueException( | |
| "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " | |
| + "and the hyphen ('-'); can't start or end with \"-\""); | |
| } | |
| } | |
| Transaction.execute(new TransactionCallbackNoReturn() { | |
| @Override | |
| public void doInTransactionWithoutResult(TransactionStatus status) { | |
| if (domainName != null) { | |
| String updatedDomainPath = getUpdatedDomainPath(domain.getPath(), domainName); | |
| updateDomainChildren(domain, updatedDomainPath); | |
| domain.setName(domainName); | |
| domain.setPath(updatedDomainPath); | |
| } | |
| if (networkDomain != null) { | |
| if (networkDomain.isEmpty()) { | |
| domain.setNetworkDomain(null); | |
| } else { | |
| domain.setNetworkDomain(networkDomain); | |
| } | |
| } | |
| _domainDao.update(domainId, domain); | |
| CallContext.current().putContextParameter(Domain.class, domain.getUuid()); | |
| } | |
| }); | |
| return _domainDao.findById(domainId); | |
| } | |
| private String getUpdatedDomainPath(String oldPath, String newName) { | |
| String[] tokenizedPath = oldPath.split("/"); | |
| tokenizedPath[tokenizedPath.length - 1] = newName; | |
| StringBuilder finalPath = new StringBuilder(); | |
| for (String token : tokenizedPath) { | |
| finalPath.append(token); | |
| finalPath.append("/"); | |
| } | |
| return finalPath.toString(); | |
| } | |
| private void updateDomainChildren(DomainVO domain, String updatedDomainPrefix) { | |
| List<DomainVO> domainChildren = _domainDao.findAllChildren(domain.getPath(), domain.getId()); | |
| // for each child, update the path | |
| for (DomainVO dom : domainChildren) { | |
| dom.setPath(dom.getPath().replaceFirst(domain.getPath(), updatedDomainPrefix)); | |
| _domainDao.update(dom.getId(), dom); | |
| } | |
| } | |
| @Override | |
| @ActionEvent(eventType = EventTypes.EVENT_DOMAIN_MOVE, eventDescription = "moving Domain") | |
| @DB | |
| public Domain moveDomainAndChildrenToNewParentDomain(MoveDomainCmd cmd) throws ResourceAllocationException { | |
| Long idOfDomainToBeMoved = cmd.getDomainId(); | |
| Long idOfNewParentDomain = cmd.getParentDomainId(); | |
| if (idOfDomainToBeMoved == Domain.ROOT_DOMAIN) { | |
| throw new InvalidParameterValueException("The domain to be moved cannot be the ROOT domain."); | |
| } | |
| if (idOfDomainToBeMoved.equals(idOfNewParentDomain)) { | |
| throw new InvalidParameterValueException("The domain to be moved and the new parent domain cannot be the same."); | |
| } | |
| DomainVO domainToBeMoved = returnDomainIfExistsAndIsActive(idOfDomainToBeMoved); | |
| logger.debug(String.format("Found the domain [%s] as the domain to be moved.", domainToBeMoved)); | |
| DomainVO newParentDomain = returnDomainIfExistsAndIsActive(idOfNewParentDomain); | |
| logger.debug(String.format("Found the domain [%s] as the new parent domain of the domain to be moved [%s].", newParentDomain, domainToBeMoved)); | |
| Account caller = getCaller(); | |
| _accountMgr.checkAccess(caller, domainToBeMoved); | |
| _accountMgr.checkAccess(caller, newParentDomain); | |
| Long idOfCurrentParentOfDomainToBeMoved = domainToBeMoved.getParent(); | |
| if (idOfCurrentParentOfDomainToBeMoved.equals(idOfNewParentDomain)) { | |
| throw new InvalidParameterValueException(String.format("The current parent domain of the domain to be moved is equal to the new parent domain [%s].", newParentDomain)); | |
| } | |
| if (newParentDomain.getPath().startsWith(domainToBeMoved.getPath())) { | |
| throw new InvalidParameterValueException("The new parent domain of the domain cannot be one of its children."); | |
| } | |
| validateUniqueDomainName(domainToBeMoved.getName(), idOfNewParentDomain); | |
| validateNewParentDomainResourceLimits(domainToBeMoved, newParentDomain); | |
| String currentPathOfDomainToBeMoved = domainToBeMoved.getPath(); | |
| String domainToBeMovedName = domainToBeMoved.getName().concat("/"); | |
| String newPathOfDomainToBeMoved = newParentDomain.getPath().concat(domainToBeMovedName); | |
| validateNewParentDomainCanAccessAllDomainToBeMovedResources(domainToBeMoved, newParentDomain, currentPathOfDomainToBeMoved, newPathOfDomainToBeMoved); | |
| DomainVO parentOfDomainToBeMoved = _domainDao.findById(idOfCurrentParentOfDomainToBeMoved); | |
| Transaction.execute(new TransactionCallbackNoReturn() { | |
| @Override | |
| public void doInTransactionWithoutResult(TransactionStatus status) { | |
| logger.debug(String.format("Setting the new parent of the domain to be moved [%s] as [%s].", domainToBeMoved, newParentDomain)); | |
| domainToBeMoved.setParent(idOfNewParentDomain); | |
| updateDomainAndChildrenPathAndLevel(domainToBeMoved, newParentDomain, currentPathOfDomainToBeMoved, newPathOfDomainToBeMoved); | |
| updateResourceCounts(idOfCurrentParentOfDomainToBeMoved, idOfNewParentDomain); | |
| updateChildCounts(parentOfDomainToBeMoved, newParentDomain); | |
| } | |
| }); | |
| return domainToBeMoved; | |
| } | |
| protected void validateNewParentDomainResourceLimit(DomainVO domainToBeMoved, DomainVO newParentDomain, | |
| Resource.ResourceType resourceType, String tag) throws ResourceAllocationException { | |
| long domainToBeMovedId = domainToBeMoved.getId(); | |
| long newParentDomainId = newParentDomain.getId(); | |
| long currentDomainResourceCount = _resourceCountDao.getResourceCount(domainToBeMovedId, ResourceOwnerType.Domain, resourceType, tag); | |
| long newParentDomainResourceCount = _resourceCountDao.getResourceCount(newParentDomainId, ResourceOwnerType.Domain, resourceType, tag); | |
| long newParentDomainResourceLimit = resourceLimitService.findCorrectResourceLimitForDomain(newParentDomain, resourceType, tag); | |
| if (newParentDomainResourceLimit == Resource.RESOURCE_UNLIMITED) { | |
| return; | |
| } | |
| if (currentDomainResourceCount + newParentDomainResourceCount > newParentDomainResourceLimit) { | |
| String message = String.format("Cannot move domain [%s] to parent domain [%s] as maximum domain resource limit of type [%s] would be exceeded. The current resource " | |
| + "count for domain [%s] is [%s], the resource count for the new parent domain [%s] is [%s], and the limit is [%s].", domainToBeMoved, | |
| newParentDomain, resourceType, domainToBeMoved, currentDomainResourceCount, newParentDomain, newParentDomainResourceCount, | |
| newParentDomainResourceLimit); | |
| logger.error(message); | |
| throw new ResourceAllocationException(message, resourceType); | |
| } | |
| } | |
| protected void validateNewParentDomainResourceLimits(DomainVO domainToBeMoved, DomainVO newParentDomain) throws ResourceAllocationException { | |
| List<String> hostTags = resourceLimitService.getResourceLimitHostTags(); | |
| List<String> storageTags = resourceLimitService.getResourceLimitStorageTags(); | |
| for (Resource.ResourceType resourceType : Resource.ResourceType.values()) { | |
| validateNewParentDomainResourceLimit(domainToBeMoved, newParentDomain, resourceType, null); | |
| if (ResourceLimitService.HostTagsSupportingTypes.contains(resourceType)) { | |
| for (String tag : hostTags) { | |
| validateNewParentDomainResourceLimit(domainToBeMoved, newParentDomain, resourceType, tag); | |
| } | |
| } | |
| if (ResourceLimitService.StorageTagsSupportingTypes.contains(resourceType)) { | |
| for (String tag : storageTags) { | |
| validateNewParentDomainResourceLimit(domainToBeMoved, newParentDomain, resourceType, tag); | |
| } | |
| } | |
| } | |
| } | |
| protected void validateNewParentDomainCanAccessAllDomainToBeMovedResources(DomainVO domainToBeMoved, DomainVO newParentDomain, String currentPathOfDomainToBeMoved, | |
| String newPathOfDomainToBeMoved) { | |
| Map<Long, List<String>> idsOfDomainsWithNetworksUsedByDomainToBeMoved = _networkDomainDao.listDomainsOfSharedNetworksUsedByDomainPath(currentPathOfDomainToBeMoved); | |
| validateNewParentDomainCanAccessResourcesOfDomainToBeMoved(newPathOfDomainToBeMoved, domainToBeMoved, newParentDomain, idsOfDomainsWithNetworksUsedByDomainToBeMoved, "Networks"); | |
| Map<Long, List<String>> idsOfDomainsOfAffinityGroupsUsedByDomainToBeMoved = | |
| affinityGroupDomainMapDao.listDomainsOfAffinityGroupsUsedByDomainPath(currentPathOfDomainToBeMoved); | |
| validateNewParentDomainCanAccessResourcesOfDomainToBeMoved(newPathOfDomainToBeMoved, domainToBeMoved, newParentDomain, idsOfDomainsOfAffinityGroupsUsedByDomainToBeMoved, "Affinity groups"); | |
| Map<Long, List<String>> idsOfDomainsOfServiceOfferingsUsedByDomainToBeMoved = | |
| serviceOfferingJoinDao.listDomainsOfServiceOfferingsUsedByDomainPath(currentPathOfDomainToBeMoved); | |
| validateNewParentDomainCanAccessResourcesOfDomainToBeMoved(newPathOfDomainToBeMoved, domainToBeMoved, newParentDomain, idsOfDomainsOfServiceOfferingsUsedByDomainToBeMoved, "Service offerings"); | |
| Map<Long, List<String>> idsOfDomainsOfNetworkOfferingsUsedByDomainToBeMoved = | |
| networkOfferingJoinDao.listDomainsOfNetworkOfferingsUsedByDomainPath(currentPathOfDomainToBeMoved); | |
| validateNewParentDomainCanAccessResourcesOfDomainToBeMoved(newPathOfDomainToBeMoved, domainToBeMoved, newParentDomain, idsOfDomainsOfNetworkOfferingsUsedByDomainToBeMoved, "Network offerings"); | |
| Map<Long, List<String>> idsOfDomainsOfDedicatedResourcesUsedByDomainToBeMoved = | |
| _dedicatedDao.listDomainsOfDedicatedResourcesUsedByDomainPath(currentPathOfDomainToBeMoved); | |
| validateNewParentDomainCanAccessResourcesOfDomainToBeMoved(newPathOfDomainToBeMoved, domainToBeMoved, newParentDomain, idsOfDomainsOfDedicatedResourcesUsedByDomainToBeMoved, "Dedicated resources"); | |
| } | |
| protected void validateNewParentDomainCanAccessResourcesOfDomainToBeMoved(String newPathOfDomainToBeMoved, DomainVO domainToBeMoved, DomainVO newParentDomain, | |
| Map<Long, List<String>> idsOfDomainsWithResourcesUsedByDomainToBeMoved, String resourceToLog) { | |
| Map<DomainVO, List<String>> domainsOfResourcesInaccessibleToNewParentDomain = new HashMap<>(); | |
| for (Map.Entry<Long, List<String>> entry : idsOfDomainsWithResourcesUsedByDomainToBeMoved.entrySet()) { | |
| DomainVO domainWithResourceUsedByDomainToBeMoved = _domainDao.findById(entry.getKey()); | |
| Pattern pattern = Pattern.compile(domainWithResourceUsedByDomainToBeMoved.getPath().replace("/", "\\/").concat(".*")); | |
| Matcher matcher = pattern.matcher(newPathOfDomainToBeMoved); | |
| if (!matcher.matches()) { | |
| domainsOfResourcesInaccessibleToNewParentDomain.put(domainWithResourceUsedByDomainToBeMoved, entry.getValue()); | |
| } | |
| } | |
| if (!domainsOfResourcesInaccessibleToNewParentDomain.isEmpty()) { | |
| logger.error(String.format("The new parent domain [%s] does not have access to domains [%s] used by [%s] in the domain to be moved [%s].", | |
| newParentDomain, domainsOfResourcesInaccessibleToNewParentDomain.keySet(), domainsOfResourcesInaccessibleToNewParentDomain.values(), domainToBeMoved)); | |
| throw new InvalidParameterValueException(String.format("New parent domain [%s] does not have access to [%s] used by domain [%s], therefore, domain [%s] cannot be moved.", | |
| newParentDomain, resourceToLog, domainToBeMoved, domainToBeMoved)); | |
| } | |
| } | |
| protected DomainVO returnDomainIfExistsAndIsActive(Long idOfDomain) { | |
| logger.debug(String.format("Checking if domain with ID [%s] exists and is active.", idOfDomain)); | |
| DomainVO domain = _domainDao.findById(idOfDomain); | |
| if (domain == null) { | |
| throw new InvalidParameterValueException(String.format("Unable to find a domain with the specified ID [%s].", idOfDomain)); | |
| } else if (domain.getState().equals(Domain.State.Inactive)) { | |
| throw new InvalidParameterValueException(String.format("Unable to use the domain [%s] as it is in state [%s].", domain, Domain.State.Inactive)); | |
| } | |
| return domain; | |
| } | |
| protected void updateDomainAndChildrenPathAndLevel(DomainVO domainToBeMoved, DomainVO newParentDomain, String oldPath, String newPath) { | |
| Integer oldRootLevel = domainToBeMoved.getLevel(); | |
| Integer newLevel = newParentDomain.getLevel() + 1; | |
| updateDomainPathAndLevel(domainToBeMoved, oldPath, newPath, oldRootLevel, newLevel); | |
| List<DomainVO> childrenDomain = _domainDao.findAllChildren(oldPath, domainToBeMoved.getId()); | |
| for (DomainVO childDomain : childrenDomain) { | |
| updateDomainPathAndLevel(childDomain, oldPath, newPath, oldRootLevel, newLevel); | |
| } | |
| } | |
| protected void updateDomainPathAndLevel(DomainVO domain, String oldPath, String newPath, Integer oldRootLevel, Integer newLevel) { | |
| String finalPath = StringUtils.replaceOnce(domain.getPath(), oldPath, newPath); | |
| domain.setPath(finalPath); | |
| Integer currentLevel = domain.getLevel(); | |
| int finalLevel = newLevel + currentLevel - oldRootLevel; | |
| domain.setLevel(finalLevel); | |
| logger.debug(String.format("Updating the path to [%s] and the level to [%s] of the domain [%s].", finalPath, finalLevel, domain)); | |
| _domainDao.update(domain.getId(), domain); | |
| } | |
| protected void updateResourceCounts(Long idOfOldParentDomain, Long idOfNewParentDomain) { | |
| logger.debug(String.format("Updating the resource counts of the old parent domain [%s] and of the new parent domain [%s].", idOfOldParentDomain, idOfNewParentDomain)); | |
| resourceLimitService.recalculateResourceCount(null, idOfOldParentDomain, null); | |
| resourceLimitService.recalculateResourceCount(null, idOfNewParentDomain, null); | |
| } | |
| protected void updateChildCounts(DomainVO oldParentDomain, DomainVO newParentDomain) { | |
| int finalOldParentChildCount = oldParentDomain.getChildCount() - 1; | |
| oldParentDomain.setChildCount(finalOldParentChildCount); | |
| oldParentDomain.setNextChildSeq(finalOldParentChildCount + 1); | |
| logger.debug(String.format("Updating the child count of the old parent domain [%s] to [%s].", oldParentDomain, finalOldParentChildCount)); | |
| _domainDao.update(oldParentDomain.getId(), oldParentDomain); | |
| int finalNewParentChildCount = newParentDomain.getChildCount() + 1; | |
| newParentDomain.setChildCount(finalNewParentChildCount); | |
| newParentDomain.setNextChildSeq(finalNewParentChildCount + 1); | |
| logger.debug(String.format("Updating the child count of the new parent domain [%s] to [%s].", newParentDomain, finalNewParentChildCount)); | |
| _domainDao.update(newParentDomain.getId(), newParentDomain); | |
| } | |
| } |