From 5304e1c9cc3ac6a1801ce8ddd1b9ce9f99153b2a Mon Sep 17 00:00:00 2001 From: Rakesh Venkatesh Date: Tue, 26 Jan 2021 12:18:10 +0100 Subject: [PATCH 1/2] Provide a cleanup flag so that the project will be deleted only when there are no resources left in the project. If users click on delete project by mistake then everything is deleted. --- .../com/cloud/projects/ProjectService.java | 2 +- .../user/project/DeleteProjectCmd.java | 9 +++- .../cloud/projects/ProjectManagerImpl.java | 51 ++++++++++++++++++- .../projects/MockProjectManagerImpl.java | 2 +- ui/src/config/section/project.js | 9 +++- 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/com/cloud/projects/ProjectService.java b/api/src/main/java/com/cloud/projects/ProjectService.java index 4efc000fae5c..f93b3c576efe 100644 --- a/api/src/main/java/com/cloud/projects/ProjectService.java +++ b/api/src/main/java/com/cloud/projects/ProjectService.java @@ -55,7 +55,7 @@ public interface ProjectService { * - project id * @return true if the project was deleted successfully, false otherwise */ - boolean deleteProject(long id); + boolean deleteProject(long id, Boolean cleanup); /** * Gets a project by id diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java index 171d51712aeb..c15476c701e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java @@ -48,6 +48,9 @@ public class DeleteProjectCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be deleted") private Long id; + @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, description = "true if all project resources have to be cleaned up, false otherwise") + private Boolean cleanup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -56,6 +59,10 @@ public Long geId() { return id; } + public Boolean isCleanup() { + return cleanup; + } + @Override public String getCommandName() { return s_name; @@ -68,7 +75,7 @@ public String getCommandName() { @Override public void execute() { CallContext.current().setEventDetails("Project Id: " + id); - boolean result = _projectService.deleteProject(id); + boolean result = _projectService.deleteProject(id, isCleanup()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 6d51c19e0860..eb8c58b30604 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -27,11 +27,24 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.inject.Inject; import javax.mail.MessagingException; import javax.naming.ConfigurationException; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcManager; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; import org.apache.cloudstack.acl.ProjectRole; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.acl.dao.ProjectRoleDao; @@ -125,6 +138,18 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C private ProjectRoleDao projectRoleDao; @Inject private UserDao userDao; + @Inject + private VolumeDao _volumeDao; + @Inject + private UserVmDao _userVmDao; + @Inject + private VMTemplateDao _templateDao; + @Inject + private NetworkDao _networkDao; + @Inject + private VMSnapshotDao _vmSnapshotDao; + @Inject + private VpcManager _vpcMgr; protected boolean _invitationRequired = false; protected long _invitationTimeOut = 86400000; @@ -285,7 +310,7 @@ public Project enableProject(long projectId) { @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_DELETE, eventDescription = "deleting project", async = true) - public boolean deleteProject(long projectId) { + public boolean deleteProject(long projectId, Boolean isCleanup) { CallContext ctx = CallContext.current(); ProjectVO project = getProject(projectId); @@ -297,7 +322,29 @@ public boolean deleteProject(long projectId) { CallContext.current().setProject(project); _accountMgr.checkAccess(ctx.getCallingAccount(), AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId())); - return deleteProject(ctx.getCallingAccount(), ctx.getCallingUserId(), project); + if (isCleanup != null && isCleanup) { + return deleteProject(ctx.getCallingAccount(), ctx.getCallingUserId(), project); + } else { + List userTemplates = _templateDao.listByAccountId(project.getProjectAccountId()); + List vmSnapshots = _vmSnapshotDao.listByAccountId(project.getProjectAccountId()); + List vms = _userVmDao.listByAccountId(project.getProjectAccountId()); + List volumes = _volumeDao.findDetachedByAccount(project.getProjectAccountId()); + List networks = _networkDao.listByOwner(project.getProjectAccountId()); + List vpcs = _vpcMgr.getVpcsForAccount(project.getProjectAccountId()); + + Optional message = Stream.of(userTemplates, vmSnapshots, vms, volumes, networks, vpcs) + .filter(entity -> !entity.isEmpty()) + .map(entity -> entity.size() + " " + entity.get(0).getEntityType().getSimpleName() + " to clean up") + .findFirst(); + + if (message.isEmpty()) { + return deleteProject(ctx.getCallingAccount(), ctx.getCallingUserId(), project); + } + + CloudRuntimeException e = new CloudRuntimeException("Can't delete the project yet because it has " + message.get()); + e.addProxyObject(project.getUuid(), "projectId"); + throw e; + } } @DB diff --git a/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java b/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java index f8af30f2eb2f..988d675ec04e 100644 --- a/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java +++ b/server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java @@ -37,7 +37,7 @@ public Project createProject(String name, String displayText, String accountName } @Override - public boolean deleteProject(long id) { + public boolean deleteProject(long id, Boolean cleanup) { // TODO Auto-generated method stub return false; } diff --git a/ui/src/config/section/project.js b/ui/src/config/section/project.js index 531901c50b68..3a693c5f2c6b 100644 --- a/ui/src/config/section/project.js +++ b/ui/src/config/section/project.js @@ -148,7 +148,14 @@ export default { }, groupAction: true, popup: true, - groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + groupMap: (selection) => { return selection.map(x => { return { id: x } }) }, + args: (record, store) => { + const fields = [] + if (store.apis.deleteProject.params.filter(x => x.name === 'cleanup').length > 0) { + fields.push('cleanup') + } + return fields + } } ] } From 1845d0220f3fac0aa0698a581ba049cb78e30a04 Mon Sep 17 00:00:00 2001 From: Rakesh Venkatesh Date: Thu, 23 Sep 2021 11:15:24 +0200 Subject: [PATCH 2/2] fix travis failures --- .../cloudstack/api/command/user/project/DeleteProjectCmd.java | 2 +- tools/marvin/marvin/lib/base.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java index c15476c701e9..4e4a290f021b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java @@ -48,7 +48,7 @@ public class DeleteProjectCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be deleted") private Long id; - @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, description = "true if all project resources have to be cleaned up, false otherwise") + @Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, since = "4.16.0", description = "true if all project resources have to be cleaned up, false otherwise") private Boolean cleanup; ///////////////////////////////////////////////////// diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index aeb8bfb9d101..b8c1138e846c 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -4100,6 +4100,7 @@ def delete(self, apiclient): cmd = deleteProject.deleteProjectCmd() cmd.id = self.id + cmd.cleanup = True apiclient.deleteProject(cmd) def update(self, apiclient, **kwargs):