Skip to content

Commit a9c42fd

Browse files
raveningRakesh Venkatesh
andauthored
Provide option to force delete the project (apache#4617)
* 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. * fix travis failures Co-authored-by: Rakesh Venkatesh <rakeshv@apache.org>
1 parent 9de5ef9 commit a9c42fd

File tree

6 files changed

+68
-6
lines changed

6 files changed

+68
-6
lines changed

api/src/main/java/com/cloud/projects/ProjectService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public interface ProjectService {
5555
* - project id
5656
* @return true if the project was deleted successfully, false otherwise
5757
*/
58-
boolean deleteProject(long id);
58+
boolean deleteProject(long id, Boolean cleanup);
5959

6060
/**
6161
* Gets a project by id

api/src/main/java/org/apache/cloudstack/api/command/user/project/DeleteProjectCmd.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public class DeleteProjectCmd extends BaseAsyncCmd {
4848
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ProjectResponse.class, required = true, description = "id of the project to be deleted")
4949
private Long id;
5050

51+
@Parameter(name = ApiConstants.CLEANUP, type = CommandType.BOOLEAN, since = "4.16.0", description = "true if all project resources have to be cleaned up, false otherwise")
52+
private Boolean cleanup;
53+
5154
/////////////////////////////////////////////////////
5255
/////////////////// Accessors ///////////////////////
5356
/////////////////////////////////////////////////////
@@ -56,6 +59,10 @@ public Long geId() {
5659
return id;
5760
}
5861

62+
public Boolean isCleanup() {
63+
return cleanup;
64+
}
65+
5966
@Override
6067
public String getCommandName() {
6168
return s_name;
@@ -68,7 +75,7 @@ public String getCommandName() {
6875
@Override
6976
public void execute() {
7077
CallContext.current().setEventDetails("Project Id: " + id);
71-
boolean result = _projectService.deleteProject(id);
78+
boolean result = _projectService.deleteProject(id, isCleanup());
7279
if (result) {
7380
SuccessResponse response = new SuccessResponse(getCommandName());
7481
this.setResponseObject(response);

server/src/main/java/com/cloud/projects/ProjectManagerImpl.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,24 @@
2727
import java.util.concurrent.ScheduledExecutorService;
2828
import java.util.concurrent.TimeUnit;
2929
import java.util.stream.Collectors;
30+
import java.util.stream.Stream;
3031

3132
import javax.inject.Inject;
3233
import javax.mail.MessagingException;
3334
import javax.naming.ConfigurationException;
3435

36+
import com.cloud.network.dao.NetworkDao;
37+
import com.cloud.network.dao.NetworkVO;
38+
import com.cloud.network.vpc.Vpc;
39+
import com.cloud.network.vpc.VpcManager;
40+
import com.cloud.storage.VMTemplateVO;
41+
import com.cloud.storage.VolumeVO;
42+
import com.cloud.storage.dao.VMTemplateDao;
43+
import com.cloud.storage.dao.VolumeDao;
44+
import com.cloud.vm.UserVmVO;
45+
import com.cloud.vm.dao.UserVmDao;
46+
import com.cloud.vm.snapshot.VMSnapshotVO;
47+
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
3548
import org.apache.cloudstack.acl.ProjectRole;
3649
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
3750
import org.apache.cloudstack.acl.dao.ProjectRoleDao;
@@ -125,6 +138,18 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
125138
private ProjectRoleDao projectRoleDao;
126139
@Inject
127140
private UserDao userDao;
141+
@Inject
142+
private VolumeDao _volumeDao;
143+
@Inject
144+
private UserVmDao _userVmDao;
145+
@Inject
146+
private VMTemplateDao _templateDao;
147+
@Inject
148+
private NetworkDao _networkDao;
149+
@Inject
150+
private VMSnapshotDao _vmSnapshotDao;
151+
@Inject
152+
private VpcManager _vpcMgr;
128153

129154
protected boolean _invitationRequired = false;
130155
protected long _invitationTimeOut = 86400000;
@@ -285,7 +310,7 @@ public Project enableProject(long projectId) {
285310

286311
@Override
287312
@ActionEvent(eventType = EventTypes.EVENT_PROJECT_DELETE, eventDescription = "deleting project", async = true)
288-
public boolean deleteProject(long projectId) {
313+
public boolean deleteProject(long projectId, Boolean isCleanup) {
289314
CallContext ctx = CallContext.current();
290315

291316
ProjectVO project = getProject(projectId);
@@ -297,7 +322,29 @@ public boolean deleteProject(long projectId) {
297322
CallContext.current().setProject(project);
298323
_accountMgr.checkAccess(ctx.getCallingAccount(), AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
299324

300-
return deleteProject(ctx.getCallingAccount(), ctx.getCallingUserId(), project);
325+
if (isCleanup != null && isCleanup) {
326+
return deleteProject(ctx.getCallingAccount(), ctx.getCallingUserId(), project);
327+
} else {
328+
List<VMTemplateVO> userTemplates = _templateDao.listByAccountId(project.getProjectAccountId());
329+
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.listByAccountId(project.getProjectAccountId());
330+
List<UserVmVO> vms = _userVmDao.listByAccountId(project.getProjectAccountId());
331+
List<VolumeVO> volumes = _volumeDao.findDetachedByAccount(project.getProjectAccountId());
332+
List<NetworkVO> networks = _networkDao.listByOwner(project.getProjectAccountId());
333+
List<? extends Vpc> vpcs = _vpcMgr.getVpcsForAccount(project.getProjectAccountId());
334+
335+
Optional<String> message = Stream.of(userTemplates, vmSnapshots, vms, volumes, networks, vpcs)
336+
.filter(entity -> !entity.isEmpty())
337+
.map(entity -> entity.size() + " " + entity.get(0).getEntityType().getSimpleName() + " to clean up")
338+
.findFirst();
339+
340+
if (message.isEmpty()) {
341+
return deleteProject(ctx.getCallingAccount(), ctx.getCallingUserId(), project);
342+
}
343+
344+
CloudRuntimeException e = new CloudRuntimeException("Can't delete the project yet because it has " + message.get());
345+
e.addProxyObject(project.getUuid(), "projectId");
346+
throw e;
347+
}
301348
}
302349

303350
@DB

server/src/test/java/com/cloud/projects/MockProjectManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public Project createProject(String name, String displayText, String accountName
3737
}
3838

3939
@Override
40-
public boolean deleteProject(long id) {
40+
public boolean deleteProject(long id, Boolean cleanup) {
4141
// TODO Auto-generated method stub
4242
return false;
4343
}

tools/marvin/marvin/lib/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4100,6 +4100,7 @@ def delete(self, apiclient):
41004100

41014101
cmd = deleteProject.deleteProjectCmd()
41024102
cmd.id = self.id
4103+
cmd.cleanup = True
41034104
apiclient.deleteProject(cmd)
41044105

41054106
def update(self, apiclient, **kwargs):

ui/src/config/section/project.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,14 @@ export default {
148148
},
149149
groupAction: true,
150150
popup: true,
151-
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
151+
groupMap: (selection) => { return selection.map(x => { return { id: x } }) },
152+
args: (record, store) => {
153+
const fields = []
154+
if (store.apis.deleteProject.params.filter(x => x.name === 'cleanup').length > 0) {
155+
fields.push('cleanup')
156+
}
157+
return fields
158+
}
152159
}
153160
]
154161
}

0 commit comments

Comments
 (0)