Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,8 @@ List<VMInstanceVO> searchRemovedByRemoveDate(final Date startDate, final Date en
int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List<Long> domainIds);

List<VMInstanceVO> listByIdsIncludingRemoved(List<Long> ids);

List<VMInstanceVO> listDeleteProtectedVmsByAccountId(long accountId);

List<VMInstanceVO> listDeleteProtectedVmsByDomainIds(List<Long> domainIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.apache.cloudstack.api.ApiConstants;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -106,6 +107,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
protected SearchBuilder<VMInstanceVO> IdsPowerStateSelectSearch;
GenericSearchBuilder<VMInstanceVO, Integer> CountByOfferingId;
GenericSearchBuilder<VMInstanceVO, Integer> CountUserVmNotInDomain;
SearchBuilder<VMInstanceVO> DeleteProtectedVmSearchByAccount;
SearchBuilder<VMInstanceVO> DeleteProtectedVmSearchByDomainIds;

@Inject
ResourceTagDao tagsDao;
Expand Down Expand Up @@ -368,6 +371,19 @@ protected void init() {
CountUserVmNotInDomain.and("domainIdsNotIn", CountUserVmNotInDomain.entity().getDomainId(), Op.NIN);
CountUserVmNotInDomain.done();

DeleteProtectedVmSearchByAccount = createSearchBuilder();
DeleteProtectedVmSearchByAccount.selectFields(DeleteProtectedVmSearchByAccount.entity().getUuid());
DeleteProtectedVmSearchByAccount.and(ApiConstants.ACCOUNT_ID, DeleteProtectedVmSearchByAccount.entity().getAccountId(), Op.EQ);
DeleteProtectedVmSearchByAccount.and(ApiConstants.DELETE_PROTECTION, DeleteProtectedVmSearchByAccount.entity().isDeleteProtection(), Op.EQ);
DeleteProtectedVmSearchByAccount.and(ApiConstants.REMOVED, DeleteProtectedVmSearchByAccount.entity().getRemoved(), Op.NULL);
DeleteProtectedVmSearchByAccount.done();

DeleteProtectedVmSearchByDomainIds = createSearchBuilder();
DeleteProtectedVmSearchByDomainIds.selectFields(DeleteProtectedVmSearchByDomainIds.entity().getUuid());
DeleteProtectedVmSearchByDomainIds.and(ApiConstants.DOMAIN_IDS, DeleteProtectedVmSearchByDomainIds.entity().getDomainId(), Op.IN);
DeleteProtectedVmSearchByDomainIds.and(ApiConstants.DELETE_PROTECTION, DeleteProtectedVmSearchByDomainIds.entity().isDeleteProtection(), Op.EQ);
DeleteProtectedVmSearchByDomainIds.and(ApiConstants.REMOVED, DeleteProtectedVmSearchByDomainIds.entity().getRemoved(), Op.NULL);
DeleteProtectedVmSearchByDomainIds.done();
}

@Override
Expand Down Expand Up @@ -1296,4 +1312,20 @@ public List<VMInstanceVO> listByIdsIncludingRemoved(List<Long> ids) {
sc.setParameters("ids", ids.toArray());
return listIncludingRemovedBy(sc);
}

@Override
public List<VMInstanceVO> listDeleteProtectedVmsByAccountId(long accountId) {
SearchCriteria<VMInstanceVO> sc = DeleteProtectedVmSearchByAccount.create();
sc.setParameters(ApiConstants.ACCOUNT_ID, accountId);
sc.setParameters(ApiConstants.DELETE_PROTECTION, true);
return listBy(sc);
}

@Override
public List<VMInstanceVO> listDeleteProtectedVmsByDomainIds(List<Long> domainIds) {
SearchCriteria<VMInstanceVO> sc = DeleteProtectedVmSearchByDomainIds.create();
sc.setParameters(ApiConstants.DOMAIN_IDS, domainIds);
sc.setParameters(ApiConstants.DELETE_PROTECTION, true);
return listBy(sc);
Comment thread
sudo87 marked this conversation as resolved.
Outdated
}
}
18 changes: 18 additions & 0 deletions server/src/main/java/com/cloud/user/AccountManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,7 @@ public boolean deleteUserAccount(long accountId) {
return true;
}

validateNoDeleteProtectedVmsForAccount(account);
checkIfAccountManagesProjects(accountId);
verifyCallerPrivilegeForUserOrAccountOperations(account);
Comment thread
sudo87 marked this conversation as resolved.
Outdated

Comment thread
sudo87 marked this conversation as resolved.
Outdated
Expand Down Expand Up @@ -2138,6 +2139,23 @@ protected boolean isDeleteNeeded(AccountVO account, long accountId, Account call
return true;
}

private void validateNoDeleteProtectedVmsForAccount(Account account) {
long accountId = account.getId();
List<VMInstanceVO> deleteProtectedVms = _vmDao.listDeleteProtectedVmsByAccountId(accountId);
if (deleteProtectedVms.isEmpty()) {
Comment thread
DaanHoogland marked this conversation as resolved.
Outdated
return;
}

if (logger.isDebugEnabled()) {
List<String> vmUuids = deleteProtectedVms.stream().map(VMInstanceVO::getUuid).collect(Collectors.toList());
logger.debug("Cannot delete Account {}, delete protection enabled for Instances: {}", account, vmUuids);
Comment thread
sudo87 marked this conversation as resolved.
}

throw new InvalidParameterValueException(
String.format("Cannot delete Account '%s'. One or more Instances have delete protection enabled.",
account.getAccountName()));
}

@Override
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_ENABLE, eventDescription = "enabling account", async = true)
public AccountVO enableAccount(String accountName, Long domainId, Long accountId) {
Expand Down
30 changes: 27 additions & 3 deletions server/src/main/java/com/cloud/user/DomainManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.inject.Inject;

Expand Down Expand Up @@ -104,6 +105,7 @@
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.ReservationContextImpl;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;

import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -622,9 +624,6 @@ protected boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOp
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);
Expand All @@ -633,6 +632,12 @@ protected boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOp
sc1.addAnd("path", SearchCriteria.Op.LIKE, "%" + "replace(" + domainHandle.getPath() + ", '%', '[%]')" + "%");
List<DomainVO> domainsToBeInactivated = _domainDao.search(sc1, null);

// Validate that no Instance in this domain or its subdomains has delete protection
validateNoDeleteProtectedVmsForDomain(domainHandle, domainsToBeInactivated);

Comment thread
DaanHoogland marked this conversation as resolved.
Outdated
domainHandle.setState(Domain.State.Inactive);
_domainDao.update(domainId, domainHandle);

Comment thread
sudo87 marked this conversation as resolved.
Outdated
// update all subdomains to inactive so no accounts/users can be created
for (DomainVO domain : domainsToBeInactivated) {
domain.setState(Domain.State.Inactive);
Expand Down Expand Up @@ -724,6 +729,25 @@ protected boolean cleanupDomain(Long domainId, Long ownerId) throws ConcurrentOp
return success && deleteDomainSuccess;
}

private void validateNoDeleteProtectedVmsForDomain(Domain domainHandle, List<DomainVO> subDomains) {
List<Long> allDomainIds = subDomains.stream().map(Domain::getId).collect(Collectors.toList());
allDomainIds.add(domainHandle.getId());

List<VMInstanceVO> deleteProtectedVms = vmInstanceDao.listDeleteProtectedVmsByDomainIds(allDomainIds);
if (deleteProtectedVms.isEmpty()) {
return;
}

if (logger.isDebugEnabled()) {
List<String> vmUuids = deleteProtectedVms.stream().map(VMInstanceVO::getUuid).collect(Collectors.toList());
logger.debug("Cannot delete Domain {}, it has delete protection enabled for Instances: {}", domainHandle, vmUuids);
Comment thread
DaanHoogland marked this conversation as resolved.
Outdated
}

throw new InvalidParameterValueException(
String.format("Cannot delete Domain '%s'. One or more Instances have delete protection enabled.",
domainHandle.getName()));
}

@Override
public Pair<List<? extends Domain>, Integer> searchForDomains(ListDomainsCmd cmd) {
Account caller = getCaller();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.dao.VMInstanceDao;


@RunWith(MockitoJUnitRunner.class)
Expand Down Expand Up @@ -123,6 +124,8 @@ public class DomainManagerImplTest {
Account adminAccount;
@Mock
GlobalLock lock;
@Mock
VMInstanceDao vmInstanceDao;

List<AccountVO> domainAccountsForCleanup;
List<Long> domainNetworkIds;
Expand Down Expand Up @@ -309,7 +312,7 @@ public void deleteDomainCleanup() {
Mockito.when(_resourceCountDao.removeEntriesByOwner(Mockito.anyLong(), Mockito.eq(ResourceOwnerType.Domain))).thenReturn(1l);
Mockito.when(_resourceLimitDao.removeEntriesByOwner(Mockito.anyLong(), Mockito.eq(ResourceOwnerType.Domain))).thenReturn(1l);
Mockito.when(_configMgr.releaseDomainSpecificVirtualRanges(Mockito.any())).thenReturn(true);

Mockito.when(vmInstanceDao.listDeleteProtectedVmsByDomainIds(Mockito.any())).thenReturn(Collections.emptyList());
try {
Assert.assertTrue(domainManager.deleteDomain(20l, true));
} finally {
Expand Down
Loading