mirror of https://github.com/apache/cloudstack.git
Prevent deletion of account and domain if either of them has deleted protected instance (#12901)
This commit is contained in:
parent
df7ff97271
commit
b196e97cc3
|
|
@ -21,6 +21,7 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.cloud.hypervisor.Hypervisor;
|
||||
import com.cloud.utils.Pair;
|
||||
|
|
@ -192,4 +193,8 @@ public interface VMInstanceDao extends GenericDao<VMInstanceVO, Long>, StateDao<
|
|||
int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List<Long> domainIds);
|
||||
|
||||
List<VMInstanceVO> listByIdsIncludingRemoved(List<Long> ids);
|
||||
|
||||
List<VMInstanceVO> listDeleteProtectedVmsByAccountId(long accountId);
|
||||
|
||||
List<VMInstanceVO> listDeleteProtectedVmsByDomainIds(Set<Long> domainIds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,13 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -106,6 +108,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;
|
||||
|
|
@ -368,6 +372,19 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
|
|||
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
|
||||
|
|
@ -1296,4 +1313,22 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
|
|||
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);
|
||||
Filter filter = new Filter(VMInstanceVO.class, null, false, 0L, 10L);
|
||||
return listBy(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VMInstanceVO> listDeleteProtectedVmsByDomainIds(Set<Long> domainIds) {
|
||||
SearchCriteria<VMInstanceVO> sc = DeleteProtectedVmSearchByDomainIds.create();
|
||||
sc.setParameters(ApiConstants.DOMAIN_IDS, domainIds.toArray());
|
||||
sc.setParameters(ApiConstants.DELETE_PROTECTION, true);
|
||||
Filter filter = new Filter(VMInstanceVO.class, null, false, 0L, 10L);
|
||||
return listBy(sc, filter);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2099,6 +2099,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
|
||||
checkIfAccountManagesProjects(accountId);
|
||||
verifyCallerPrivilegeForUserOrAccountOperations(account);
|
||||
validateNoDeleteProtectedVmsForAccount(account);
|
||||
|
||||
CallContext.current().putContextParameter(Account.class, account.getUuid());
|
||||
|
||||
|
|
@ -2138,6 +2139,23 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||
return true;
|
||||
}
|
||||
|
||||
private void validateNoDeleteProtectedVmsForAccount(Account account) {
|
||||
long accountId = account.getId();
|
||||
List<VMInstanceVO> deleteProtectedVms = _vmDao.listDeleteProtectedVmsByAccountId(accountId);
|
||||
if (CollectionUtils.isEmpty(deleteProtectedVms)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
|
@ -104,6 +105,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
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;
|
||||
|
|
@ -364,6 +366,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom
|
|||
}
|
||||
|
||||
_accountMgr.checkAccess(caller, domain);
|
||||
// Check across the domain hierarchy (current + children) for any delete-protected instances
|
||||
validateNoDeleteProtectedVmsForDomain(domain);
|
||||
|
||||
return deleteDomain(domain, cleanup);
|
||||
}
|
||||
|
|
@ -724,6 +728,22 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom
|
|||
return success && deleteDomainSuccess;
|
||||
}
|
||||
|
||||
private void validateNoDeleteProtectedVmsForDomain(Domain parentDomain) {
|
||||
Set<Long> allDomainIds = getDomainChildrenIds(parentDomain.getPath());
|
||||
List<VMInstanceVO> deleteProtectedVms = vmInstanceDao.listDeleteProtectedVmsByDomainIds(allDomainIds);
|
||||
if (CollectionUtils.isEmpty(deleteProtectedVms)) {
|
||||
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: {}", parentDomain, vmUuids);
|
||||
}
|
||||
|
||||
throw new InvalidParameterValueException(
|
||||
String.format("Cannot delete Domain '%s'. One or more Instances have delete protection enabled.",
|
||||
parentDomain.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<? extends Domain>, Integer> searchForDomains(ListDomainsCmd cmd) {
|
||||
Account caller = getCaller();
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import com.cloud.utils.db.GlobalLock;
|
|||
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)
|
||||
|
|
@ -123,6 +124,8 @@ public class DomainManagerImplTest {
|
|||
Account adminAccount;
|
||||
@Mock
|
||||
GlobalLock lock;
|
||||
@Mock
|
||||
VMInstanceDao vmInstanceDao;
|
||||
|
||||
List<AccountVO> domainAccountsForCleanup;
|
||||
List<Long> domainNetworkIds;
|
||||
|
|
@ -213,6 +216,7 @@ public class DomainManagerImplTest {
|
|||
@Test
|
||||
public void testDeleteDomainNoCleanup() {
|
||||
Mockito.when(_configMgr.releaseDomainSpecificVirtualRanges(Mockito.any())).thenReturn(true);
|
||||
Mockito.doReturn(Collections.emptySet()).when(domainManager).getDomainChildrenIds(Mockito.any());
|
||||
domainManager.deleteDomain(DOMAIN_ID, testDomainCleanup);
|
||||
Mockito.verify(domainManager).deleteDomain(domain, testDomainCleanup);
|
||||
Mockito.verify(domainManager).removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources(domain);
|
||||
|
|
@ -278,6 +282,7 @@ public class DomainManagerImplTest {
|
|||
Mockito.when(_dedicatedDao.listByDomainId(Mockito.anyLong())).thenReturn(new ArrayList<DedicatedResourceVO>());
|
||||
Mockito.when(domainDaoMock.remove(Mockito.anyLong())).thenReturn(true);
|
||||
Mockito.when(_configMgr.releaseDomainSpecificVirtualRanges(Mockito.any())).thenReturn(true);
|
||||
Mockito.doReturn(Collections.emptySet()).when(domainManager).getDomainChildrenIds(Mockito.any());
|
||||
|
||||
try {
|
||||
Assert.assertTrue(domainManager.deleteDomain(20l, false));
|
||||
|
|
@ -309,7 +314,7 @@ public class DomainManagerImplTest {
|
|||
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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue