// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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.
//
// Automatically generated by addcopyright.py at 04/03/2012
package com.cloud.user;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.api.commands.ListDomainChildrenCmd;
import com.cloud.api.commands.ListDomainsCmd;
import com.cloud.configuration.ResourceLimit;
import com.cloud.configuration.dao.ResourceCountDao;
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.projects.ProjectManager;
import com.cloud.projects.ProjectVO;
import com.cloud.projects.dao.ProjectDao;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.DiskOfferingVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
@Local(value = { DomainManager.class, DomainService.class })
public class DomainManagerImpl implements DomainManager, DomainService, Manager {
public static final Logger s_logger = Logger.getLogger(DomainManagerImpl.class);
private String _name;
@Inject
private DomainDao _domainDao;
@Inject
private AccountManager _accountMgr;
@Inject
private ResourceCountDao _resourceCountDao;
@Inject
private AccountDao _accountDao;
@Inject
private DiskOfferingDao _diskOfferingDao;
@Inject
private ServiceOfferingDao _offeringsDao;
@Inject
private ProjectDao _projectDao;
@Inject
private ProjectManager _projectMgr;
@Override
public Domain getDomain(long domainId) {
return _domainDao.findById(domainId);
}
@Override
public String getName() {
return _name;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
_name = name;
return true;
}
@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) {
Account caller = UserContext.current().getCaller();
if (parentId == null) {
parentId = Long.valueOf(DomainVO.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);
}
@Override
@DB
public Domain createDomain(String name, Long parentId, Long ownerId, String networkDomain) {
// Verify network domain
if (networkDomain != null) {
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 \"-\"");
}
}
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("Domain with name " + name + " already exists for the parent id=" + parentId);
}
Transaction txn = Transaction.currentTxn();
txn.start();
DomainVO domain = _domainDao.create(new DomainVO(name, ownerId, parentId, networkDomain));
_resourceCountDao.createResourceCounts(domain.getId(), ResourceLimit.ResourceOwnerType.Domain);
txn.commit();
return domain;
}
@Override
public DomainVO findDomainByPath(String domainPath) {
return _domainDao.findDomainByPath(domainPath);
}
@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 = UserContext.current().getCaller();
DomainVO domain = _domainDao.findById(domainId);
if (domain == null) {
throw new InvalidParameterValueException("Failed to delete domain " + domainId + ", domain not found");
} else if (domainId == DomainVO.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) {
// mark domain as inactive
s_logger.debug("Marking domain id=" + domain.getId() + " as " + Domain.State.Inactive + " before actually deleting it");
domain.setState(Domain.State.Inactive);
_domainDao.update(domain.getId(), domain);
boolean rollBackState = false;
try {
long ownerId = domain.getAccountId();
if ((cleanup != null) && cleanup.booleanValue()) {
if (!cleanupDomain(domain.getId(), ownerId)) {
s_logger.error("Failed to clean up domain resources and sub domains, delete failed on domain " + domain.getName() + " (id: " + domain.getId() + ").");
return false;
}
} else {
List<AccountVO> accountsForCleanup = _accountDao.findCleanupsForRemovedAccounts(domain.getId());
if (accountsForCleanup.isEmpty()) {
if (!_domainDao.remove(domain.getId())) {
s_logger.error("Delete failed on domain " + domain.getName() + " (id: " + domain.getId()
+ "); please make sure all users and sub domains have been removed from the domain before deleting");
rollBackState = true;
return false;
}
} else {
s_logger.warn("Can't delete the domain yet because it has " + accountsForCleanup.size() + "accounts that need a cleanup");
rollBackState = true;
return false;
}
}
cleanupDomainOfferings(domain.getId());
return true;
} catch (Exception ex) {
s_logger.error("Exception deleting domain with id " + domain.getId(), ex);
return false;
} finally {
//when success is false
if (rollBackState) {
s_logger.debug("Changing domain id=" + domain.getId() + " state back to " + Domain.State.Active + " because it can't be removed due to resources referencing to it");
domain.setState(Domain.State.Active);
_domainDao.update(domain.getId(), domain);
}
}
}
private void cleanupDomainOfferings(Long domainId) {
// delete the service and disk offerings associated with this domain
List<DiskOfferingVO> diskOfferingsForThisDomain = _diskOfferingDao.listByDomainId(domainId);
for (DiskOfferingVO diskOffering : diskOfferingsForThisDomain) {
_diskOfferingDao.remove(diskOffering.getId());
}
List<ServiceOfferingVO> serviceOfferingsForThisDomain = _offeringsDao.findServiceOfferingByDomainId(domainId);
for (ServiceOfferingVO serviceOffering : serviceOfferingsForThisDomain) {
_offeringsDao.remove(serviceOffering.getId());
}
}
private boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOperationException, ResourceUnavailableException {
s_logger.debug("Cleaning up domain id=" + domainId);
boolean success = true;
{
DomainVO domainHandle = _domainDao.findById(domainId);
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, "%" + 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) {
s_logger.warn("Failed to cleanup domain id=" + domain.getId());
}
}
}
// 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.ACCOUNT_TYPE_PROJECT) {
s_logger.debug("Deleting account " + account + " as a part of domain id=" + domainId + " cleanup");
success = (success && _accountMgr.deleteAccount(account, UserContext.current().getCallerUserId(), UserContext.current().getCaller()));
if (!success) {
s_logger.warn("Failed to cleanup account id=" + account.getId() + " as a part of domain cleanup");
}
} else {
ProjectVO project = _projectDao.findByProjectAccountId(account.getId());
s_logger.debug("Deleting project " + project + " as a part of domain id=" + domainId + " cleanup");
success = (success && _projectMgr.deleteProject(UserContext.current().getCaller(), UserContext.current().getCallerUserId(), project));
if (!success) {
s_logger.warn("Failed to cleanup project " + project + " as a part of domain cleanup");
}
}
}
// don't remove the domain if there are accounts required cleanup
boolean deleteDomainSuccess = true;
List<AccountVO> accountsForCleanup = _accountDao.findCleanupsForRemovedAccounts(domainId);
if (accountsForCleanup.isEmpty()) {
deleteDomainSuccess = _domainDao.remove(domainId);
} else {
s_logger.debug("Can't delete the domain yet because it has " + accountsForCleanup.size() + "accounts that need a cleanup");
}
return success && deleteDomainSuccess;
}
@Override
public List<DomainVO> searchForDomains(ListDomainsCmd cmd) {
Account caller = UserContext.current().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 {
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.LIKE);
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);
return _domainDao.search(sc, searchFilter);
}
@Override
public List<DomainVO> 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 = UserContext.current().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());
List<DomainVO> domainList = searchForDomainChildren(searchFilter, domainId, domainName, keyword, path, true);
return domainList;
}
private List<DomainVO> 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.search(sc, searchFilter);
}
}