From a17570ef51b3c4de6cced915f0a9286983296192 Mon Sep 17 00:00:00 2001 From: alena Date: Thu, 6 Oct 2011 16:10:10 -0700 Subject: [PATCH] 1)Implemented Suspend/Activate project 2)Email/token based invitation system --- api/src/com/cloud/api/ApiConstants.java | 1 + api/src/com/cloud/api/BaseCmd.java | 7 +- .../api/commands/ActivateProjectCmd.java | 87 +++++ .../api/commands/AddAccountToProjectCmd.java | 12 +- .../cloud/api/commands/CreateProjectCmd.java | 2 +- .../api/commands/CreateVlanIpRangeCmd.java | 7 +- .../cloud/api/commands/JoinProjectCmd.java | 12 +- .../com/cloud/api/commands/ListEventsCmd.java | 7 + .../cloud/api/commands/ListProjectsCmd.java | 5 +- .../api/commands/ListVlanIpRangesCmd.java | 8 +- .../cloud/api/commands/SuspendProjectCmd.java | 103 +++++ .../cloud/api/commands/UpdateProjectCmd.java | 2 +- .../com/cloud/api/response/AlertResponse.java | 17 +- .../com/cloud/api/response/EventResponse.java | 66 +--- .../cloud/api/response/IPAddressResponse.java | 3 + .../response/ProjectInvitationResponse.java | 11 +- .../cloud/api/response/ProjectResponse.java | 8 +- .../api/response/ResourceCountResponse.java | 53 +-- .../api/response/ResourceLimitResponse.java | 8 +- .../api/response/VlanIpRangeResponse.java | 110 ++---- api/src/com/cloud/event/Event.java | 4 +- api/src/com/cloud/event/EventTypes.java | 2 + api/src/com/cloud/projects/Project.java | 2 +- .../com/cloud/projects/ProjectService.java | 12 +- client/tomcatconf/commands.properties.in | 2 + .../src/com/cloud/api/ApiResponseHelper.java | 87 ++--- .../api/commands/GetUsageRecordsCmd.java | 28 +- .../src/com/cloud/configuration/Config.java | 9 +- .../ConfigurationManagerImpl.java | 22 +- .../cloud/projects/ProjectManagerImpl.java | 367 +++++++++++++++--- server/src/com/cloud/projects/ProjectVO.java | 10 +- .../com/cloud/projects/dao/ProjectDao.java | 5 + .../cloud/projects/dao/ProjectDaoImpl.java | 13 + .../projects/dao/ProjectInvitationDao.java | 3 + .../dao/ProjectInvitationDaoImpl.java | 32 +- .../cloud/server/ManagementServerExtImpl.java | 23 +- .../cloud/server/ManagementServerImpl.java | 51 ++- .../api/response/UsageRecordResponse.java | 113 ++---- .../com/cloud/user/AccountManagerImpl.java | 25 +- setup/db/create-schema.sql | 5 +- setup/db/db/schema-2212to30.sql | 7 + 41 files changed, 948 insertions(+), 403 deletions(-) create mode 100644 api/src/com/cloud/api/commands/ActivateProjectCmd.java create mode 100644 api/src/com/cloud/api/commands/SuspendProjectCmd.java diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java index 2b4d410ddf2..f95c3d889df 100755 --- a/api/src/com/cloud/api/ApiConstants.java +++ b/api/src/com/cloud/api/ApiConstants.java @@ -266,5 +266,6 @@ public class ApiConstants { public static final String ROLE = "role"; public static final String USER = "user"; public static final String ACTIVE_ONLY = "activeonly"; + public static final String TOKEN = "token"; } diff --git a/api/src/com/cloud/api/BaseCmd.java b/api/src/com/cloud/api/BaseCmd.java index 3803d6c1685..eb2530fed26 100755 --- a/api/src/com/cloud/api/BaseCmd.java +++ b/api/src/com/cloud/api/BaseCmd.java @@ -480,7 +480,12 @@ public abstract class BaseCmd { Project project = _projectService.getProject(projectId); if (project != null) { - return project.getProjectAccountId(); + if (project.getState() == Project.State.Active) { + return project.getProjectAccountId(); + } else { + throw new InvalidParameterValueException("Can't add resources to the project id=" + projectId + " in state=" + project.getState() + " as it's no longer active"); + } + } else { throw new InvalidParameterValueException("Unable to find project by id " + projectId); } diff --git a/api/src/com/cloud/api/commands/ActivateProjectCmd.java b/api/src/com/cloud/api/commands/ActivateProjectCmd.java new file mode 100644 index 00000000000..b96e7dce9b5 --- /dev/null +++ b/api/src/com/cloud/api/commands/ActivateProjectCmd.java @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ProjectResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.projects.Project; +import com.cloud.user.UserContext; + +@Implementation(description="Activates a project", responseObject=ProjectResponse.class) +public class ActivateProjectCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(ActivateProjectCmd.class.getName()); + + private static final String s_name = "activaterojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="id of the project to be modified") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Project project= _projectService.getProject(id); + //verify input parameters + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + id); + } + + return _projectService.getProjectOwner(id).getId(); + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + UserContext.current().setEventDetails("Project id: "+ getId()); + Project project = _projectService.activateProject(id); + if (project != null) { + ProjectResponse response = _responseGenerator.createProjectResponse(project); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to activate a project"); + } + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java b/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java index 7048acd55d6..98b5a569668 100644 --- a/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java +++ b/api/src/com/cloud/api/commands/AddAccountToProjectCmd.java @@ -43,8 +43,11 @@ public class AddAccountToProjectCmd extends BaseCmd { @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, required=true, description="id of the project to add the account to") private Long projectId; - @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, description="name of the account to be added to the project") + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="name of the account to be added to the project") private String accountName; + + @Parameter(name=ApiConstants.EMAIL, type=CommandType.STRING, description="email to which invitation to the project is going to be sent") + private String email; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -59,8 +62,9 @@ public class AddAccountToProjectCmd extends BaseCmd { return projectId; } - public void setProjectId(Long projectId) { - this.projectId = projectId; + + public String getEmail() { + return email; } @Override @@ -75,7 +79,7 @@ public class AddAccountToProjectCmd extends BaseCmd { @Override public void execute(){ UserContext.current().setEventDetails("Project id: "+ projectId + "; accountName " + accountName); - boolean result = _projectService.addAccountToProject(getProjectId(), getAccountName()); + boolean result = _projectService.addAccountToProject(getProjectId(), getAccountName(), getEmail()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/api/src/com/cloud/api/commands/CreateProjectCmd.java b/api/src/com/cloud/api/commands/CreateProjectCmd.java index 7c6d59df409..05ac04147bb 100644 --- a/api/src/com/cloud/api/commands/CreateProjectCmd.java +++ b/api/src/com/cloud/api/commands/CreateProjectCmd.java @@ -45,7 +45,7 @@ public class CreateProjectCmd extends BaseCmd { @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="account who will own the project") private String accountName; - @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, required=true, description="domain ID of the account owning a project") + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="domain ID of the account owning a project") private Long domainId; @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="name of the project") diff --git a/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java b/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java index 9d12fc6f057..3353af834f6 100644 --- a/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java +++ b/api/src/com/cloud/api/commands/CreateVlanIpRangeCmd.java @@ -44,6 +44,9 @@ public class CreateVlanIpRangeCmd extends BaseCmd { @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="account who will own the VLAN. If VLAN is Zone wide, this parameter should be ommited") private String accountName; + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="project who will own the VLAN. If VLAN is Zone wide, this parameter should be ommited") + private Long projectId; @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="domain ID of the account owning a VLAN") private Long domainId; @@ -119,7 +122,9 @@ public class CreateVlanIpRangeCmd extends BaseCmd { return zoneId; } - + public Long getProjectId() { + return projectId; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/com/cloud/api/commands/JoinProjectCmd.java b/api/src/com/cloud/api/commands/JoinProjectCmd.java index da566299959..37333964efc 100644 --- a/api/src/com/cloud/api/commands/JoinProjectCmd.java +++ b/api/src/com/cloud/api/commands/JoinProjectCmd.java @@ -36,12 +36,14 @@ public class JoinProjectCmd extends BaseCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.PROJECT_ID, required=true, type=CommandType.LONG, description="list by project id") + @Parameter(name=ApiConstants.PROJECT_ID, required=true, type=CommandType.LONG, description="id of the project to join") private Long projectId; - @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="list invitations for specified account; this parameter has to be specified with domainId") + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, required=true, description="account that is joining the project") private String accountName; + @Parameter(name=ApiConstants.TOKEN, type=CommandType.STRING, description="list invitations for specified account; this parameter has to be specified with domainId") + private String token; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -59,6 +61,10 @@ public class JoinProjectCmd extends BaseCmd { return s_name; } + public String getToken() { + return token; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -73,7 +79,7 @@ public class JoinProjectCmd extends BaseCmd { @Override public void execute(){ UserContext.current().setEventDetails("Project id: "+ projectId + "; accountName " + accountName); - boolean result = _projectService.joinProject(projectId, accountName); + boolean result = _projectService.joinProject(projectId, accountName, token); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/api/src/com/cloud/api/commands/ListEventsCmd.java b/api/src/com/cloud/api/commands/ListEventsCmd.java index 921de2f6722..e9eef0450e4 100755 --- a/api/src/com/cloud/api/commands/ListEventsCmd.java +++ b/api/src/com/cloud/api/commands/ListEventsCmd.java @@ -68,6 +68,9 @@ public class ListEventsCmd extends BaseListCmd { @Parameter(name=ApiConstants.TYPE, type=CommandType.STRING, description="the event type (see event types)") private String type; + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="list events by projectId") + private Long projectId; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -108,6 +111,10 @@ public class ListEventsCmd extends BaseListCmd { public String getType() { return type; } + + public Long getProjectId() { + return projectId; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/com/cloud/api/commands/ListProjectsCmd.java b/api/src/com/cloud/api/commands/ListProjectsCmd.java index 2fa9b7f1c1d..b85ca528106 100644 --- a/api/src/com/cloud/api/commands/ListProjectsCmd.java +++ b/api/src/com/cloud/api/commands/ListProjectsCmd.java @@ -54,6 +54,9 @@ public class ListProjectsCmd extends BaseListCmd { @Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, description="list projects by display text") private String displayText; + @Parameter(name=ApiConstants.STATE, type=CommandType.STRING, description="list projects by state") + private String state; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -89,7 +92,7 @@ public class ListProjectsCmd extends BaseListCmd { @Override public void execute(){ - List projects = _projectService.listProjects(id, name, displayText, accountName, domainId, this.getKeyword(), this.getStartIndex(), this.getPageSizeVal()); + List projects = _projectService.listProjects(id, name, displayText, state, accountName, domainId, this.getKeyword(), this.getStartIndex(), this.getPageSizeVal()); ListResponse response = new ListResponse(); List projectResponses = new ArrayList(); for (Project project : projects) { diff --git a/api/src/com/cloud/api/commands/ListVlanIpRangesCmd.java b/api/src/com/cloud/api/commands/ListVlanIpRangesCmd.java index a742a283795..1198584d9e2 100644 --- a/api/src/com/cloud/api/commands/ListVlanIpRangesCmd.java +++ b/api/src/com/cloud/api/commands/ListVlanIpRangesCmd.java @@ -43,6 +43,9 @@ public class ListVlanIpRangesCmd extends BaseListCmd { @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="the account with which the VLAN IP range is associated. Must be used with the domainId parameter.") private String accountName; + + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="project who will own the VLAN") + private Long projectId; @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="the domain ID with which the VLAN IP range is associated. If used with the account parameter, returns all VLAN IP ranges for that account in the specified domain.") private Long domainId; @@ -101,7 +104,10 @@ public class ListVlanIpRangesCmd extends BaseListCmd { return forVirtualNetwork; } - + public Long getProjectId() { + return projectId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/com/cloud/api/commands/SuspendProjectCmd.java b/api/src/com/cloud/api/commands/SuspendProjectCmd.java new file mode 100644 index 00000000000..dc699af4483 --- /dev/null +++ b/api/src/com/cloud/api/commands/SuspendProjectCmd.java @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.api.commands; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseAsyncCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.ProjectResponse; +import com.cloud.api.response.SuccessResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.projects.Project; +import com.cloud.user.UserContext; + +@Implementation(description="Suspends a project", responseObject=ProjectResponse.class) +public class SuspendProjectCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(SuspendProjectCmd.class.getName()); + + private static final String s_name = "suspendprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, required=true, description="id of the project to be suspended") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long geId() { + return id; + } + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ConcurrentOperationException, ResourceUnavailableException{ + UserContext.current().setEventDetails("Project Id: " + id); + Project project = _projectService.suspendProject(id); + if (project != null) { + ProjectResponse response = _responseGenerator.createProjectResponse(project); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to suspend a project"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PROJECT_SUSPEND; + } + + @Override + public String getEventDescription() { + return "Suspending project: " + id; + } + + @Override + public long getEntityOwnerId() { + Project project= _projectService.getProject(id); + //verify input parameters + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + id); + } + + return _projectService.getProjectOwner(id).getId(); + } + +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/UpdateProjectCmd.java b/api/src/com/cloud/api/commands/UpdateProjectCmd.java index 1ef1387452d..f8e7239d009 100644 --- a/api/src/com/cloud/api/commands/UpdateProjectCmd.java +++ b/api/src/com/cloud/api/commands/UpdateProjectCmd.java @@ -32,7 +32,7 @@ import com.cloud.user.UserContext; @Implementation(description="Updates a project", responseObject=ProjectResponse.class) public class UpdateProjectCmd extends BaseCmd { - public static final Logger s_logger = Logger.getLogger(CreateProjectCmd.class.getName()); + public static final Logger s_logger = Logger.getLogger(UpdateProjectCmd.class.getName()); private static final String s_name = "updateprojectresponse"; diff --git a/api/src/com/cloud/api/response/AlertResponse.java b/api/src/com/cloud/api/response/AlertResponse.java index 1d44a7b4da2..65c3fe79244 100644 --- a/api/src/com/cloud/api/response/AlertResponse.java +++ b/api/src/com/cloud/api/response/AlertResponse.java @@ -23,6 +23,7 @@ import com.cloud.api.ApiConstants; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; +@SuppressWarnings("unused") public class AlertResponse extends BaseResponse { @SerializedName(ApiConstants.ID) @Param(description="the id of the alert") private Long id; @@ -36,34 +37,18 @@ public class AlertResponse extends BaseResponse { @SerializedName(ApiConstants.SENT) @Param(description="the date and time the alert was sent") private Date lastSent; - public Long getId() { - return id; - } - public void setId(Long id) { this.id = id; } - public Short getAlertType() { - return alertType; - } - public void setAlertType(Short alertType) { this.alertType = alertType; } - public String getDescription() { - return description; - } - public void setDescription(String description) { this.description = description; } - public Date getLastSent() { - return lastSent; - } - public void setLastSent(Date lastSent) { this.lastSent = lastSent; } diff --git a/api/src/com/cloud/api/response/EventResponse.java b/api/src/com/cloud/api/response/EventResponse.java index bd74aed4a64..15540a2b23f 100644 --- a/api/src/com/cloud/api/response/EventResponse.java +++ b/api/src/com/cloud/api/response/EventResponse.java @@ -24,7 +24,8 @@ import com.cloud.event.Event; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; -public class EventResponse extends BaseResponse { +@SuppressWarnings("unused") +public class EventResponse extends BaseResponse implements ControlledEntityResponse{ @SerializedName(ApiConstants.ID) @Param(description="the ID of the event") private Long id; @@ -42,6 +43,12 @@ public class EventResponse extends BaseResponse { @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account name for the account that owns the object being acted on in the event (e.g. the owner of the virtual machine, ip address, or security group)") private String accountName; + + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id of the ipaddress") + private Long projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="the project name of the address") + private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the id of the account's domain") private Long domainId; @@ -58,91 +65,60 @@ public class EventResponse extends BaseResponse { @SerializedName("parentid") @Param(description="whether the event is parented") private Long parentId; - public Long getId() { - return id; - } - public void setId(Long id) { this.id = id; } - public String getUsername() { - return username; - } - public void setUsername(String username) { this.username = username; } - public String getEventType() { - return eventType; - } - public void setEventType(String eventType) { this.eventType = eventType; } - public String getLevel() { - return level; - } - public void setLevel(String level) { this.level = level; } - public String getDescription() { - return description; - } - public void setDescription(String description) { this.description = description; } - public String getAccountName() { - return accountName; - } - + @Override public void setAccountName(String accountName) { this.accountName = accountName; } - public Long getDomainId() { - return domainId; - } - + @Override public void setDomainId(Long domainId) { this.domainId = domainId; } - public String getDomainName() { - return domainName; - } - + @Override public void setDomainName(String domainName) { this.domainName = domainName; } - public Date getCreated() { - return created; - } - public void setCreated(Date created) { this.created = created; } - public Event.State getState() { - return state; - } - public void setState(Event.State state) { this.state = state; } - public Long getParentId() { - return parentId; - } - public void setParentId(Long parentId) { this.parentId = parentId; } + + @Override + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } } diff --git a/api/src/com/cloud/api/response/IPAddressResponse.java b/api/src/com/cloud/api/response/IPAddressResponse.java index 33d136c27e3..7bc9ebacd31 100644 --- a/api/src/com/cloud/api/response/IPAddressResponse.java +++ b/api/src/com/cloud/api/response/IPAddressResponse.java @@ -114,14 +114,17 @@ public class IPAddressResponse extends BaseResponse implements ControlledEntityR this.sourceNat = sourceNat; } + @Override public void setAccountName(String accountName) { this.accountName = accountName; } + @Override public void setDomainId(Long domainId) { this.domainId = domainId; } + @Override public void setDomainName(String domainName) { this.domainName = domainName; } diff --git a/api/src/com/cloud/api/response/ProjectInvitationResponse.java b/api/src/com/cloud/api/response/ProjectInvitationResponse.java index d49e2df089c..017a1e611d0 100644 --- a/api/src/com/cloud/api/response/ProjectInvitationResponse.java +++ b/api/src/com/cloud/api/response/ProjectInvitationResponse.java @@ -16,11 +16,14 @@ public class ProjectInvitationResponse extends BaseResponse implements Controlle private Long domainId; @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name where the project belongs to") - private String domain; + private String domainName; @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account name of the project's owner") private String accountName; + @SerializedName(ApiConstants.EMAIL) @Param(description="the email the invitation was sent to") + private String email; + @SerializedName(ApiConstants.STATE) @Param(description="the invitation state") private String invitationState; @@ -37,7 +40,7 @@ public class ProjectInvitationResponse extends BaseResponse implements Controlle } public void setDomainName(String domain) { - this.domain = domain; + this.domainName = domain; } public void setAccountName(String accountName) { @@ -48,5 +51,7 @@ public class ProjectInvitationResponse extends BaseResponse implements Controlle this.invitationState = invitationState; } - + public void setEmail(String email) { + this.email = email; + } } diff --git a/api/src/com/cloud/api/response/ProjectResponse.java b/api/src/com/cloud/api/response/ProjectResponse.java index 323a98bb678..719333a9d9d 100644 --- a/api/src/com/cloud/api/response/ProjectResponse.java +++ b/api/src/com/cloud/api/response/ProjectResponse.java @@ -42,6 +42,9 @@ public class ProjectResponse extends BaseResponse{ @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account name of the project's owner") private String ownerName; + + @SerializedName(ApiConstants.STATE) @Param(description="the state of the project") + private String state; public void setId(Long id) { this.id = id; @@ -66,5 +69,8 @@ public class ProjectResponse extends BaseResponse{ public void setOwner(String owner) { this.ownerName = owner; } - + + public void setState(String state) { + this.state = state; + } } diff --git a/api/src/com/cloud/api/response/ResourceCountResponse.java b/api/src/com/cloud/api/response/ResourceCountResponse.java index 609c31b98f4..930ef23b285 100644 --- a/api/src/com/cloud/api/response/ResourceCountResponse.java +++ b/api/src/com/cloud/api/response/ResourceCountResponse.java @@ -17,62 +17,65 @@ */ package com.cloud.api.response; +import com.cloud.api.ApiConstants; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; -public class ResourceCountResponse extends BaseResponse { - @SerializedName("account") @Param(description="the account for which resource count's are updated") +@SuppressWarnings("unused") +public class ResourceCountResponse extends BaseResponse implements ControlledEntityResponse{ + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account for which resource count's are updated") private String accountName; + + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id for which resource count's are updated") + private Long projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="the project name for which resource count's are updated") + private String projectName; - @SerializedName("domainid") @Param(description="the domain ID for which resource count's are updated") + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain ID for which resource count's are updated") private Long domainId; - @SerializedName("domain") @Param(description="the domain name for which resource count's are updated") + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name for which resource count's are updated") private String domainName; - @SerializedName("resourcetype") @Param(description="resource type. Values include 0, 1, 2, 3, 4. See the resourceType parameter for more information on these values.") + @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName("resourcecount") @Param(description="resource count") private long resourceCount; - - public String getAccountName() { - return accountName; - } + @Override public void setAccountName(String accountName) { this.accountName = accountName; } - public Long getDomainId() { - return domainId; - } - + @Override public void setDomainId(Long domainId) { this.domainId = domainId; } - public String getDomainName() { - return domainName; - } - + @Override public void setDomainName(String domainName) { this.domainName = domainName; } - public String getResourceType() { - return resourceType; - } - public void setResourceType(String resourceType) { this.resourceType = resourceType; } - public Long getResourceCount() { - return resourceCount; - } - public void setResourceCount(Long resourceCount) { this.resourceCount = resourceCount; } + + @Override + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } + } diff --git a/api/src/com/cloud/api/response/ResourceLimitResponse.java b/api/src/com/cloud/api/response/ResourceLimitResponse.java index da3671df6fd..f754cfe03e0 100644 --- a/api/src/com/cloud/api/response/ResourceLimitResponse.java +++ b/api/src/com/cloud/api/response/ResourceLimitResponse.java @@ -23,16 +23,16 @@ import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") public class ResourceLimitResponse extends BaseResponse implements ControlledEntityResponse { - @SerializedName("account") @Param(description="the account of the resource limit") + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account of the resource limit") private String accountName; - @SerializedName("domainid") @Param(description="the domain ID of the resource limit") + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain ID of the resource limit") private Long domainId; - @SerializedName("domain") @Param(description="the domain name of the resource limit") + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the resource limit") private String domainName; - @SerializedName("resourcetype") @Param(description="resource type. Values include 0, 1, 2, 3, 4. See the resourceType parameter for more information on these values.") + @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName("max") @Param(description="the maximum number of the resource. A -1 means the resource currently has no limit.") diff --git a/api/src/com/cloud/api/response/VlanIpRangeResponse.java b/api/src/com/cloud/api/response/VlanIpRangeResponse.java index 7fbb9bf3f99..7effb13307e 100644 --- a/api/src/com/cloud/api/response/VlanIpRangeResponse.java +++ b/api/src/com/cloud/api/response/VlanIpRangeResponse.java @@ -17,172 +17,130 @@ */ package com.cloud.api.response; +import com.cloud.api.ApiConstants; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; -public class VlanIpRangeResponse extends BaseResponse { - @SerializedName("id") @Param(description="the ID of the VLAN IP range") +@SuppressWarnings("unused") +public class VlanIpRangeResponse extends BaseResponse implements ControlledEntityResponse{ + @SerializedName(ApiConstants.ID) @Param(description="the ID of the VLAN IP range") private Long id; @SerializedName("forvirtualnetwork") @Param(description="the virtual network for the VLAN IP range") private Boolean forVirtualNetwork; - @SerializedName("zoneid") @Param(description="the Zone ID of the VLAN IP range") + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the Zone ID of the VLAN IP range") private Long zoneId; - @SerializedName("vlan") @Param(description="the ID or VID of the VLAN.") + @SerializedName(ApiConstants.VLAN) @Param(description="the ID or VID of the VLAN.") private String vlan; - @SerializedName("account") @Param(description="the account of the VLAN IP range") + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the account of the VLAN IP range") private String accountName; - @SerializedName("domainid") @Param(description="the domain ID of the VLAN IP range") + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain ID of the VLAN IP range") private Long domainId; - @SerializedName("domain") @Param(description="the domain name of the VLAN IP range") + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the VLAN IP range") private String domainName; - @SerializedName("podid") @Param(description="the Pod ID for the VLAN IP range") + @SerializedName(ApiConstants.POD_ID) @Param(description="the Pod ID for the VLAN IP range") private Long podId; @SerializedName("podname") @Param(description="the Pod name for the VLAN IP range") private String podName; - @SerializedName("gateway") @Param(description="the gateway of the VLAN IP range") + @SerializedName(ApiConstants.GATEWAY) @Param(description="the gateway of the VLAN IP range") private String gateway; - @SerializedName("netmask") @Param(description="the netmask of the VLAN IP range") + @SerializedName(ApiConstants.NETMASK) @Param(description="the netmask of the VLAN IP range") private String netmask; - @SerializedName("description") @Param(description="the description of the VLAN IP range") + @SerializedName(ApiConstants.DESCRIPTION) @Param(description="the description of the VLAN IP range") private String description; - @SerializedName("startip") @Param(description="the start ip of the VLAN IP range") + @SerializedName(ApiConstants.START_IP) @Param(description="the start ip of the VLAN IP range") private String startIp; - @SerializedName("endip") @Param(description="the end ip of the VLAN IP range") + @SerializedName(ApiConstants.END_IP) @Param(description="the end ip of the VLAN IP range") private String endIp; - @SerializedName("networkid") @Param(description="the network id of vlan range") + @SerializedName(ApiConstants.NETWORK_ID) @Param(description="the network id of vlan range") private Long networkId; - - public Long getId() { - return id; - } + + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id of the vlan range") + private Long projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="the project name of the vlan range") + private String projectName; public void setId(Long id) { this.id = id; } - public Boolean getForVirtualNetwork() { - return forVirtualNetwork; - } - public void setForVirtualNetwork(Boolean forVirtualNetwork) { this.forVirtualNetwork = forVirtualNetwork; } - public Long getZoneId() { - return zoneId; - } - public void setZoneId(Long zoneId) { this.zoneId = zoneId; } - - public String getVlan() { - return vlan; - } - + public void setVlan(String vlan) { this.vlan = vlan; } - - public String getAccountName() { - return accountName; - } - + public void setAccountName(String accountName) { this.accountName = accountName; } - public Long getDomainId() { - return domainId; - } - public void setDomainId(Long domainId) { this.domainId = domainId; } - public String getDomainName() { - return domainName; - } - public void setDomainName(String domainName) { this.domainName = domainName; } - public Long getPodId() { - return podId; - } - public void setPodId(Long podId) { this.podId = podId; } - public String getPodName() { - return podName; - } - public void setPodName(String podName) { this.podName = podName; } - public String getGateway() { - return gateway; - } - public void setGateway(String gateway) { this.gateway = gateway; } - public String getNetmask() { - return netmask; - } - public void setNetmask(String netmask) { this.netmask = netmask; } - public String getDescription() { - return description; - } - public void setDescription(String description) { this.description = description; } - public String getStartIp() { - return startIp; - } - public void setStartIp(String startIp) { this.startIp = startIp; } - public String getEndIp() { - return endIp; - } - public void setEndIp(String endIp) { this.endIp = endIp; } - public Long getNetworkId() { - return networkId; - } - public void setNetworkId(Long networkId) { this.networkId = networkId; } + + @Override + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } } diff --git a/api/src/com/cloud/event/Event.java b/api/src/com/cloud/event/Event.java index 8e89090ac58..9f1207e8236 100644 --- a/api/src/com/cloud/event/Event.java +++ b/api/src/com/cloud/event/Event.java @@ -19,7 +19,9 @@ package com.cloud.event; import java.util.Date; -public interface Event { +import com.cloud.acl.ControlledEntity; + +public interface Event extends ControlledEntity{ public enum State { Created, Scheduled, diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index dba135ba9cc..25319a7b362 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -207,6 +207,8 @@ public class EventTypes { public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE"; public static final String EVENT_PROJECT_UPDATE = "PROJECT.UPDATE"; public static final String EVENT_PROJECT_DELETE = "PROJECT.DELETE"; + public static final String EVENT_PROJECT_ACTIVATE = "PROJECT.ACTIVATE"; + public static final String EVENT_PROJECT_SUSPEND = "PROJECT.SUSPEND"; public static final String EVENT_PROJECT_ACCOUNT_ADD = "PROJECT.ACCOUNT.ADD"; public static final String EVENT_PROJECT_ACCOUNT_REMOVE = "PROJECT.ACCOUNT.REMOVE"; } diff --git a/api/src/com/cloud/projects/Project.java b/api/src/com/cloud/projects/Project.java index cf253730271..04fd60099cb 100644 --- a/api/src/com/cloud/projects/Project.java +++ b/api/src/com/cloud/projects/Project.java @@ -22,7 +22,7 @@ import java.util.Date; import com.cloud.domain.PartOf; public interface Project extends PartOf{ - public enum State {Active, Inactive, Suspended} + public enum State {Active, Disabled, Suspended} String getDisplayText(); diff --git a/api/src/com/cloud/projects/ProjectService.java b/api/src/com/cloud/projects/ProjectService.java index 950d5fd6375..edd241590d8 100644 --- a/api/src/com/cloud/projects/ProjectService.java +++ b/api/src/com/cloud/projects/ProjectService.java @@ -2,7 +2,9 @@ package com.cloud.projects; import java.util.List; +import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.projects.ProjectAccount.Role; import com.cloud.user.Account; @@ -36,7 +38,7 @@ public interface ProjectService { */ Project getProject(long id); - List listProjects(Long id, String name, String displayText, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize); + List listProjects(Long id, String name, String displayText, String state, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize); ProjectAccount assignAccountToProject(Project project, long accountId, Role accountRole); @@ -50,7 +52,7 @@ public interface ProjectService { Project updateProject(long id, String displayText, String newOwnerName); - boolean addAccountToProject(long projectId, String accountName); + boolean addAccountToProject(long projectId, String accountName, String email); boolean deleteAccountFromProject(long projectId, String accountName); @@ -58,5 +60,9 @@ public interface ProjectService { List listProjectInvitations(Long projectId, String accountName, Long domainId, String state, boolean activeOnly, Long startIndex, Long pageSizeVal); - boolean joinProject(long projectId, String accountName); + boolean joinProject(long projectId, String accountName, String token); + + Project activateProject(long projectId); + + Project suspendProject(long projectId) throws ConcurrentOperationException, ResourceUnavailableException; } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 3355a2e0b14..778c8ff71d2 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -269,6 +269,8 @@ listSSHKeyPairs=com.cloud.api.commands.ListSSHKeyPairsCmd;15 createProject=com.cloud.api.commands.CreateProjectCmd;15 deleteProject=com.cloud.api.commands.DeleteProjectCmd;15 updateProject=com.cloud.api.commands.UpdateProjectCmd;15 +activateProject=com.cloud.api.commands.ActivateProjectCmd;15 +suspendProject=com.cloud.api.commands.SuspendProjectCmd;15 listProjects=com.cloud.api.commands.ListProjectsCmd;15 addAccountToProject=com.cloud.api.commands.AddAccountToProjectCmd;15 deleteAccountFromProject=com.cloud.api.commands.DeleteAccountFromProjectCmd;15 diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index c4d460ef91c..5ee0b0e7cd9 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -405,13 +405,11 @@ public class ApiResponseHelper implements ResponseGenerator { if (resourceCount.getResourceOwnerType() == ResourceOwnerType.Account) { Account accountTemp = ApiDBUtils.findAccountById(resourceCount.getOwnerId()); if (accountTemp != null) { - resourceCountResponse.setAccountName(accountTemp.getAccountName()); - resourceCountResponse.setDomainId(accountTemp.getDomainId()); - resourceCountResponse.setDomainName(ApiDBUtils.findDomainById(accountTemp.getDomainId()).getName()); + populateAccount(resourceCountResponse, accountTemp.getId()); + populateDomain(resourceCountResponse, accountTemp.getDomainId()); } } else if (resourceCount.getResourceOwnerType() == ResourceOwnerType.Domain) { - resourceCountResponse.setDomainId(resourceCount.getOwnerId()); - resourceCountResponse.setDomainName(ApiDBUtils.findDomainById(resourceCount.getOwnerId()).getName()); + populateDomain(resourceCountResponse, resourceCount.getOwnerId()); } resourceCountResponse.setResourceType(Integer.valueOf(resourceCount.getType().getOrdinal()).toString()); @@ -629,9 +627,8 @@ public class ApiResponseHelper implements ResponseGenerator { vlanResponse.setNetworkId(vlan.getNetworkId()); Account owner = ApiDBUtils.getVlanAccount(vlan.getId()); if (owner != null) { - vlanResponse.setAccountName(owner.getAccountName()); - vlanResponse.setDomainId(owner.getDomainId()); - vlanResponse.setDomainName(ApiDBUtils.findDomainById(owner.getDomainId()).getName()); + populateAccount(vlanResponse, owner.getId()); + populateDomain(vlanResponse, owner.getDomainId()); } vlanResponse.setObjectName("vlan"); @@ -1475,12 +1472,9 @@ public class ApiResponseHelper implements ResponseGenerator { isoResponse.setCreated(iso.getCreated()); isoResponse.setChecksum(iso.getChecksum()); isoResponse.setPasswordEnabled(false); - Account owner = ApiDBUtils.findAccountById(iso.getAccountId()); - if (owner != null) { - isoResponse.setAccountName(owner.getAccountName()); - isoResponse.setDomainId(owner.getDomainId()); - isoResponse.setDomainName(ApiDBUtils.findDomainById(owner.getDomainId()).getName()); - } + + populateOwner(isoResponse, iso); + isoResponse.setObjectName("iso"); isoResponses.add(isoResponse); return isoResponses; @@ -1529,15 +1523,8 @@ public class ApiResponseHelper implements ResponseGenerator { isoResponse.setOsTypeId(-1L); isoResponse.setOsTypeName(""); } - - // add account ID and name - Account owner = ApiDBUtils.findAccountById(iso.getAccountId()); - if (owner != null) { - isoResponse.setAccountName(owner.getAccountName()); - isoResponse.setDomainId(owner.getDomainId()); - // TODO: implement - isoResponse.setDomainName(ApiDBUtils.findDomainById(owner.getDomainId()).getName()); - } + + populateOwner(isoResponse, iso); Account account = UserContext.current().getCaller(); boolean isAdmin = false; @@ -1592,12 +1579,8 @@ public class ApiResponseHelper implements ResponseGenerator { netGrpResponse.setId(networkGroup.getId()); netGrpResponse.setName(networkGroup.getName()); netGrpResponse.setDescription(networkGroup.getDescription()); - netGrpResponse.setAccountName(networkGroup.getAccountName()); populateOwner(netGrpResponse, networkGroup); - - netGrpResponse.setDomainId(networkGroup.getDomainId()); - netGrpResponse.setDomainName(ApiDBUtils.findDomainById(networkGroup.getDomainId()).getName()); List ingressRules = networkGroup.getIngressRules(); if ((ingressRules != null) && !ingressRules.isEmpty()) { @@ -1639,12 +1622,10 @@ public class ApiResponseHelper implements ResponseGenerator { @Override public SecurityGroupResponse createSecurityGroupResponse(SecurityGroup group) { SecurityGroupResponse response = new SecurityGroupResponse(); - Account account = ApiDBUtils.findAccountById(group.getAccountId()); + + populateOwner(response, group); - response.setAccountName(account.getAccountName()); - response.setDomainId(group.getDomainId()); response.setDescription(group.getDescription()); - response.setDomainName(ApiDBUtils.findDomainById(group.getDomainId()).getName()); response.setId(group.getId()); response.setName(group.getName()); @@ -1721,16 +1702,16 @@ public class ApiResponseHelper implements ResponseGenerator { @Override public EventResponse createEventResponse(Event event) { EventResponse responseEvent = new EventResponse(); - responseEvent.setAccountName(event.getAccountName()); responseEvent.setCreated(event.getCreateDate()); responseEvent.setDescription(event.getDescription()); - responseEvent.setDomainId(event.getDomainId()); responseEvent.setEventType(event.getType()); responseEvent.setId(event.getId()); responseEvent.setLevel(event.getLevel()); responseEvent.setParentId(event.getStartId()); responseEvent.setState(event.getState()); - responseEvent.setDomainName(ApiDBUtils.findDomainById(event.getDomainId()).getName()); + + populateOwner(responseEvent, event); + User user = ApiDBUtils.findUserById(event.getUserId()); if (user != null) { responseEvent.setUsername(user.getUsername()); @@ -1998,10 +1979,9 @@ public class ApiResponseHelper implements ResponseGenerator { account = ApiDBUtils.findAccountById(securityGroup.getAccountId()); securiytGroupAccounts.put(securityGroup.getAccountId(), account); } - - response.setAccountName(account.getAccountName()); - response.setDomainId(account.getDomainId()); - response.setDomainName(ApiDBUtils.findDomainById(securityGroup.getDomainId()).getName()); + + populateAccount(response, account.getId()); + populateDomain(response, account.getDomainId()); List responses = new ArrayList(); for (IngressRule ingressRule : ingressRules) { @@ -2068,9 +2048,9 @@ public class ApiResponseHelper implements ResponseGenerator { securiytGroupAccounts.put(securityGroup.getAccountId(), account); } - response.setAccountName(account.getAccountName()); - response.setDomainId(account.getDomainId()); - response.setDomainName(ApiDBUtils.findDomainById(securityGroup.getDomainId()).getName()); + populateAccount(response, account.getId()); + populateDomain(response, account.getDomainId()); + List responses = new ArrayList(); for (EgressRule egressRule : egressRules) { @@ -2258,6 +2238,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setId(project.getId()); response.setName(project.getName()); response.setDisplaytext(project.getDisplayText()); + response.setState(project.getState().toString()); Domain domain = ApiDBUtils.findDomainById(project.getDomainId()); response.setDomainId(domain.getId()); @@ -2384,9 +2365,11 @@ public class ApiResponseHelper implements ResponseGenerator { sgr.setId(sgd.getId()); sgr.setName(sgd.getName()); sgr.setDescription(sgd.getDescription()); - sgr.setAccountName(sgd.getAccountName()); - sgr.setDomainId(sgd.getDomainId()); - sgr.setDomainName(sgd.getDomainName()); + + Account account = ApiDBUtils.findAccountByNameDomain(sgd.getAccountName(), sgd.getDomainId()); + populateAccount(sgr, account.getId()); + populateDomain(sgr, sgd.getDomainId()); + sgr.setObjectName(sgd.getObjectName()); securityGroupResponse.add(sgr); } @@ -2425,7 +2408,7 @@ public class ApiResponseHelper implements ResponseGenerator { } private void populateOwner(ControlledEntityResponse response, ControlledEntity object) { - Account account = ApiDBUtils.findAccountById(object.getAccountId()); + Account account = ApiDBUtils.findAccountByIdIncludingRemoved(object.getAccountId()); if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { //find the project @@ -2442,7 +2425,7 @@ public class ApiResponseHelper implements ResponseGenerator { } private void populateAccount(ControlledEntityResponse response, long accountId) { - Account account = ApiDBUtils.findAccountById(accountId); + Account account = ApiDBUtils.findAccountByIdIncludingRemoved(accountId); if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { //find the project Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId()); @@ -2497,9 +2480,15 @@ public class ApiResponseHelper implements ResponseGenerator { response.setProjectName(ApiDBUtils.findProjectById(invite.getProjectId()).getName()); response.setInvitationState(invite.getState().toString()); - Account account = ApiDBUtils.findAccountById(invite.getAccountId()); - response.setAccountName(account.getAccountName()); - populateDomain(response, account.getDomainId()); + if (invite.getAccountId() != null) { + Account account = ApiDBUtils.findAccountById(invite.getAccountId()); + response.setAccountName(account.getAccountName()); + + } else { + response.setEmail(invite.getEmail()); + } + + populateDomain(response, invite.getDomainId()); response.setObjectName("projectinvitation"); return response; diff --git a/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java b/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java index 5f7d0707792..7010805a16d 100644 --- a/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java +++ b/server/src/com/cloud/api/commands/GetUsageRecordsCmd.java @@ -30,15 +30,16 @@ import org.apache.log4j.Logger; import com.cloud.api.ApiConstants; import com.cloud.api.ApiDBUtils; -import com.cloud.api.BaseCmd; import com.cloud.api.BaseListCmd; import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.response.ListResponse; +import com.cloud.projects.Project; import com.cloud.server.ManagementServerExt; import com.cloud.server.api.response.UsageRecordResponse; import com.cloud.usage.UsageTypes; import com.cloud.usage.UsageVO; +import com.cloud.user.Account; @Implementation(description="Lists usage records for accounts", responseObject=UsageRecordResponse.class) public class GetUsageRecordsCmd extends BaseListCmd { @@ -65,6 +66,9 @@ public class GetUsageRecordsCmd extends BaseListCmd { @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.LONG, description="List usage records for the specified account") private Long accountId; + @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.LONG, description="List usage records for specified project") + private Long projectId; + @Parameter(name=ApiConstants.TYPE, type=CommandType.LONG, description="List usage records for the specified usage type") private Long usageType; @@ -95,6 +99,10 @@ public class GetUsageRecordsCmd extends BaseListCmd { public Long getUsageType() { return usageType; } + + public Long getProjectId() { + return projectId; + } ///////////////////////////////////////////////////// /////////////// Misc parameters /////////////////// @@ -213,10 +221,20 @@ public class GetUsageRecordsCmd extends BaseListCmd { UsageRecordResponse usageRecResponse = new UsageRecordResponse(); if (usageRecordGeneric instanceof UsageVO) { UsageVO usageRecord = (UsageVO)usageRecordGeneric; - - usageRecResponse.setAccountName(ApiDBUtils.findAccountByIdIncludingRemoved(usageRecord.getAccountId()).getAccountName()); - usageRecResponse.setAccountId(usageRecord.getAccountId()); - usageRecResponse.setDomainId(usageRecord.getDomainId()); + + Account account = ApiDBUtils.findAccountByIdIncludingRemoved(usageRecord.getAccountId()); + if (account.getType() == Account.ACCOUNT_TYPE_PROJECT) { + //find the project + Project project = ApiDBUtils.findProjectByProjectAccountId(account.getId()); + usageRecResponse.setProjectId(project.getId()); + usageRecResponse.setProjectName(project.getName()); + } else { + usageRecResponse.setAccountId(account.getId()); + usageRecResponse.setAccountName(account.getAccountName()); + } + + usageRecResponse.setDomainId(usageRecord.getDomainId()); + usageRecResponse.setZoneId(usageRecord.getZoneId()); usageRecResponse.setDescription(usageRecord.getDescription()); usageRecResponse.setUsage(usageRecord.getUsageDisplay()); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 2c588bafcd8..a3669125f71 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -289,7 +289,14 @@ public enum Config { DefaultMaxProjectVolumes("Project Defaults", ManagementServer.class, Long.class, "max.project.volumes", "20", "The default maximum number of volumes that can be created for a project", null), ProjectInviteRequired("Project Defaults", ManagementServer.class, Boolean.class, "project.invite.required", "false", "If invitation confirmation is required when add account to project. Default value is false", null), - ProjectInvitationExpirationTime("Project Defaults", ManagementServer.class, Long.class, "project.invite.timeout", "86400", "Invitation expiration time (in seconds). Default is 1 day - 86400 seconds", null); + ProjectInvitationExpirationTime("Project Defaults", ManagementServer.class, Long.class, "project.invite.timeout", "86400", "Invitation expiration time (in seconds). Default is 1 day - 86400 seconds", null), + + ProjectEmailSender("Project Defaults", ManagementServer.class, String.class, "project.email.sender", null, "Sender of project invitation email (will be in the From header of the email)", null), + ProjectSMTPHost("Project Defaults", ManagementServer.class, String.class, "project.smtp.host", null, "SMTP hostname used for sending out email project invitations", null), + ProjectSMTPPassword("Project Defaults", ManagementServer.class, String.class, "project.smtp.password", null, "Password for SMTP authentication (applies only if project.smtp.useAuth is true)", null), + ProjectSMTPPort("Project Defaults", ManagementServer.class, Integer.class, "project.smtp.port", "465", "Port the SMTP server is listening on", null), + ProjectSMTPUseAuth("Project Defaults", ManagementServer.class, String.class, "project.smtp.useAuth", null, "If true, use SMTP authentication when sending emails", null), + ProjectSMTPUsername("Project Defaults", ManagementServer.class, String.class, "project.smtp.username", null, "Username for SMTP authentication (applies only if project.smtp.useAuth is true)", null); private final String _category; private final Class _componentClass; diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 8b9fb9a8f63..226054359e2 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -110,6 +110,8 @@ import com.cloud.offering.ServiceOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.org.Grouping; +import com.cloud.projects.Project; +import com.cloud.projects.ProjectManager; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; @@ -202,6 +204,8 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura CapacityDao _capacityDao; @Inject ResourceLimitService _resourceLimitMgr; + @Inject + ProjectManager _projectMgr; // FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao? protected static final DataCenterLinkLocalIpAddressDaoImpl _LinkLocalIpAllocDao = ComponentLocator.inject(DataCenterLinkLocalIpAddressDaoImpl.class); @@ -1932,11 +1936,25 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura Boolean forVirtualNetwork = cmd.isForVirtualNetwork(); Long networkId = cmd.getNetworkID(); String networkVlanId = null; - - // If an account name and domain ID are specified, look up the account + + //projectId and accountName can't be specified together String accountName = cmd.getAccountName(); + Long projectId = cmd.getProjectId(); Long domainId = cmd.getDomainId(); Account account = null; + + if (projectId != null) { + if (accountName != null) { + throw new InvalidParameterValueException("Account and projectId are mutually exclusive"); + } + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + + account = _accountMgr.getAccount(project.getProjectAccountId()); + } + if ((accountName != null) && (domainId != null)) { account = _accountDao.findActiveAccount(accountName, domainId); if (account == null) { diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java index 3a2434462df..6ff5647233b 100644 --- a/server/src/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/com/cloud/projects/ProjectManagerImpl.java @@ -17,11 +17,21 @@ */ package com.cloud.projects; +import java.io.UnsupportedEncodingException; import java.sql.Date; import java.util.List; import java.util.Map; +import java.util.Properties; +import java.util.Random; import javax.ejb.Local; +import javax.mail.Authenticator; +import javax.mail.Message.RecipientType; +import javax.mail.MessagingException; +import javax.mail.PasswordAuthentication; +import javax.mail.Session; +import javax.mail.URLName; +import javax.mail.internet.InternetAddress; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -36,9 +46,11 @@ 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.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.projects.Project.State; import com.cloud.projects.ProjectAccount.Role; import com.cloud.projects.dao.ProjectAccountDao; @@ -51,6 +63,7 @@ import com.cloud.user.DomainManager; import com.cloud.user.ResourceLimitService; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; +import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; @@ -60,11 +73,16 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.sun.mail.smtp.SMTPMessage; +import com.sun.mail.smtp.SMTPSSLTransport; +import com.sun.mail.smtp.SMTPTransport; @Local(value = { ProjectService.class, ProjectManager.class }) public class ProjectManagerImpl implements ProjectManager, Manager{ public static final Logger s_logger = Logger.getLogger(ProjectManagerImpl.class); private String _name; + private EmailInvite _emailInvite; @Inject private DomainDao _domainDao; @@ -98,7 +116,25 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ Map configs = _configDao.getConfiguration(params); _invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key())); _invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key())); + + + // set up the email system for project invitations + String smtpHost = configs.get("project.smtp.host"); + int smtpPort = NumbersUtil.parseInt(configs.get("project.smtp.port"), 25); + String useAuthStr = configs.get("project.smtp.useAuth"); + boolean useAuth = ((useAuthStr == null) ? false : Boolean.parseBoolean(useAuthStr)); + String smtpUsername = configs.get("project.smtp.username"); + String smtpPassword = configs.get("project.smtp.password"); + String emailSender = configs.get("project.email.sender"); + String smtpDebugStr = configs.get("project.smtp.debug"); + boolean smtpDebug = false; + if (smtpDebugStr != null) { + smtpDebug = Boolean.parseBoolean(smtpDebugStr); + } + + _emailInvite = new EmailInvite(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug); + return true; } @@ -171,7 +207,8 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ @ActionEvent(eventType = EventTypes.EVENT_PROJECT_DELETE, eventDescription = "deleting project", async = true) @DB public boolean deleteProject (long projectId) { - Account caller = UserContext.current().getCaller(); + UserContext ctx = UserContext.current(); + Account caller = ctx.getCaller(); ProjectVO project= getProject(projectId); //verify input parameters @@ -184,45 +221,57 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ //mark project as inactive first, so you can't add resources to it Transaction txn = Transaction.currentTxn(); txn.start(); - s_logger.debug("Marking project id=" + projectId + " with state " + State.Inactive + " as a part of project delete..."); - project.setState(State.Inactive); + s_logger.debug("Marking project id=" + projectId + " with state " + State.Disabled + " as a part of project delete..."); + project.setState(State.Disabled); boolean updateResult = _projectDao.update(projectId, project); _resourceLimitMgr.decrementResourceCount(project.getProjectAccountId(), ResourceType.project); txn.commit(); if (updateResult) { - if (!cleanupProject(project, null, null)) { + if (!cleanupProject(project, _accountDao.findById(caller.getId()), ctx.getCallerUserId())) { s_logger.warn("Failed to cleanup project's id=" + projectId + " resources, not removing the project yet"); return false; } else { return _projectDao.remove(projectId); } } else { - s_logger.warn("Failed to mark the project id=" + projectId + " with state " + State.Inactive); + s_logger.warn("Failed to mark the project id=" + projectId + " with state " + State.Disabled); return false; } } + @DB private boolean cleanupProject(Project project, AccountVO caller, Long callerUserId) { - boolean result=true; - - //Unassign all users from the project - s_logger.debug("Unassigning all accounts from project " + project + " as a part of project cleanup..."); - List projectAccounts = _projectAccountDao.listByProjectId(project.getId()); - for (ProjectAccount projectAccount : projectAccounts) { - result = result && unassignAccountFromProject(projectAccount.getProjectId(), projectAccount.getAccountId()); - } - - if (result) { - s_logger.debug("Accounts are unassign successfully from project " + project + " as a part of project cleanup..."); - } - + boolean result=true; //Delete project's account AccountVO account = _accountDao.findById(project.getProjectAccountId()); s_logger.debug("Deleting projects " + project + " internal account id=" + account.getId() + " as a part of project cleanup..."); result = result && _accountMgr.deleteAccount(account, callerUserId, caller); + if (result) { + //Unassign all users from the project + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + s_logger.debug("Unassigning all accounts from project " + project + " as a part of project cleanup..."); + List projectAccounts = _projectAccountDao.listByProjectId(project.getId()); + for (ProjectAccount projectAccount : projectAccounts) { + result = result && unassignAccountFromProject(projectAccount.getProjectId(), projectAccount.getAccountId()); + } + + s_logger.debug("Removing all invitations for the project " + project + " as a part of project cleanup..."); + _projectInvitationDao.cleanupInvitations(project.getId()); + + txn.commit(); + if (result) { + s_logger.debug("Accounts are unassign successfully from project " + project + " as a part of project cleanup..."); + } + } else { + s_logger.warn("Failed to cleanup project's internal account"); + } + return result; } @@ -248,7 +297,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } @Override - public List listProjects(Long id, String name, String displayText, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize) { + public List listProjects(Long id, String name, String displayText, String state, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize) { Account caller = UserContext.current().getCaller(); Long accountId = null; String path = null; @@ -314,6 +363,10 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ sc.addAnd("accountId", Op.EQ, accountId); } + if (state != null) { + sc.addAnd("state", Op.EQ, state); + } + if (keyword != null) { SearchCriteria ssc = _projectDao.createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); @@ -438,7 +491,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } @Override - public boolean addAccountToProject(long projectId, String accountName) { + public boolean addAccountToProject(long projectId, String accountName, String email) { Account caller = UserContext.current().getCaller(); //check that the project exists @@ -449,30 +502,26 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } //check that account-to-add exists - Account account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); - if (account == null) { - throw new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId()); - } - - //verify permissions - _accountMgr.checkAccess(caller, _domainDao.findById(project.getDomainId()), AccessType.ModifyProject); - - //Check if the account already added to the project - ProjectAccount projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, account.getId()); - if (projectAccount != null) { - s_logger.debug("Account " + accountName + " already added to the project id=" + projectId); - return true; + Account account = null; + if (accountName != null) { + account = _accountMgr.getActiveAccountByName(accountName, project.getDomainId()); + if (account == null) { + throw new InvalidParameterValueException("Unable to find account name=" + accountName + " in domain id=" + project.getDomainId()); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getDomainId()), AccessType.ModifyProject); + + //Check if the account already added to the project + ProjectAccount projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, account.getId()); + if (projectAccount != null) { + s_logger.debug("Account " + accountName + " already added to the project id=" + projectId); + return true; + } } if (_invitationRequired) { - //TODO - token based registration - if (generateInvitation(projectId, account.getId()) != null) { - return true; - } else { - s_logger.warn("Failed to generate invitation for account " + accountName + " to project id=" + projectId); - return false; - } - + return inviteAccountToProject(project, account, email); } else { if (assignAccountToProject(project, account.getId(), ProjectAccount.Role.Regular) != null) { return true; @@ -483,6 +532,30 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } } + private boolean inviteAccountToProject(Project project, Account account, String email) { + if (account != null) { + if (createAccountInvitation(project, account.getId()) != null) { + return true; + } else { + s_logger.warn("Failed to generate invitation for account " + account.getAccountName() + " to project id=" + project); + return false; + } + } + + if (email != null) { + //generate the token + String token = generateToken(10); + if (generateTokenBasedInvitation(project, email, token) != null) { + return true; + } else { + s_logger.warn("Failed to generate invitation for email " + email + " to project id=" + project); + return false; + } + } + + return false; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACCOUNT_REMOVE, eventDescription = "removing account from project") public boolean deleteAccountFromProject(long projectId, String accountName) { @@ -557,13 +630,13 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ return _projectAccountDao.search(sc, searchFilter); } - public ProjectInvitation generateInvitation(long projectId, Long accountId) { + public ProjectInvitation createAccountInvitation(Project project, Long accountId) { //verify if the invitation was already generated - ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId); + ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, project.getId()); if (invite != null) { if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) { - throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + projectId); + throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + project); } else { if (invite.getState() == ProjectInvitation.State.Pending) { expireInvitation(invite); @@ -571,7 +644,33 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } } - return _projectInvitationDao.persist(new ProjectInvitationVO(projectId, accountId, _accountMgr.getAccount(accountId).getDomainId(), null, null)); + return _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), accountId, project.getDomainId(), null, null)); + } + + public ProjectInvitation generateTokenBasedInvitation(Project project, String email, String token) { + //verify if the invitation was already generated + ProjectInvitationVO invite = _projectInvitationDao.findPendingByEmailAndProjectId(email, project.getId()); + + if (invite != null) { + if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) { + throw new InvalidParameterValueException("There is already a pending invitation for email=" + email + " to the project id=" + project); + } else { + if (invite.getState() == ProjectInvitation.State.Pending) { + expireInvitation(invite); + } + } + } + + ProjectInvitation projectInvitation = _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), null, project.getDomainId(), email, token)); + try { + _emailInvite.sendInvite(token, email, project.getId()); + } catch (Exception ex){ + s_logger.warn("Failed to send project id=" + project + " invitation to the email " + email + "; removing the invitation record from the db", ex); + _projectInvitationDao.remove(projectInvitation.getId()); + return null; + } + + return projectInvitation; } private boolean expireInvitation(ProjectInvitationVO invite) { @@ -656,7 +755,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } @Override @DB - public boolean joinProject(long projectId, String accountName) { + public boolean joinProject(long projectId, String accountName, String token) { Account caller = UserContext.current().getCaller(); Long accountId = null; boolean result = true; @@ -683,7 +782,13 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ } //check that invitation exists - ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId); + ProjectInvitationVO invite = null; + if (token == null) { + invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId); + } else { + invite = _projectInvitationDao.findPendingByTokenAndProjectId(token, projectId); + } + if (invite != null) { if (!_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) { expireInvitation(invite); @@ -721,4 +826,172 @@ public class ProjectManagerImpl implements ProjectManager, Manager{ public List listPermittedProjectAccounts(long accountId) { return _projectAccountDao.listPermittedAccountIds(accountId); } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACTIVATE, eventDescription = "activating project") + public Project activateProject(long projectId) { + Account caller = UserContext.current().getCaller(); + + //check that the project exists + ProjectVO project = getProject(projectId); + + if (project == null) { + throw new InvalidParameterValueException("Unable to find the project id=" + projectId); + } + + //verify permissions + _accountMgr.checkAccess(caller, _domainDao.findById(project.getDomainId()), AccessType.ModifyProject); + + //allow project activation only when it's in Suspended state + Project.State currentState = project.getState(); + + if (currentState == State.Active) { + s_logger.debug("The project id=" + projectId + " is already active, no need to activate it again"); + return project; + } + + if (currentState != State.Suspended) { + throw new InvalidParameterValueException("Can't activate the project in " + currentState + " state"); + } + + project.setState(Project.State.Active); + _projectDao.update(projectId, project); + + return _projectDao.findById(projectId); + } + + + @Override + @ActionEvent(eventType = EventTypes.EVENT_PROJECT_SUSPEND, eventDescription = "suspending project", async = true) + public Project suspendProject (long projectId) throws ConcurrentOperationException, ResourceUnavailableException { + Account caller = UserContext.current().getCaller(); + + ProjectVO project= getProject(projectId); + //verify input parameters + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + + _accountMgr.checkAccess(caller, _domainDao.findById(project.getDomainId()), AccessType.ModifyProject); + + if (suspendProject(project)) { + s_logger.debug("Successfully suspended project id=" + projectId); + return _projectDao.findById(projectId); + } else { + throw new CloudRuntimeException("Failed to suspend project id=" + projectId); + } + + } + + private boolean suspendProject(ProjectVO project) throws ConcurrentOperationException, ResourceUnavailableException{ + s_logger.debug("Marking project " + project + " with state " + State.Suspended + " as a part of project suspend..."); + project.setState(State.Suspended); + boolean updateResult = _projectDao.update(project.getId(), project); + + if (updateResult) { + long projectAccountId = project.getProjectAccountId(); + if (!_accountMgr.disableAccount(projectAccountId)) { + s_logger.warn("Failed to suspend all project's " + project + " resources; the resources will be suspended later by background thread"); + } + } else { + throw new CloudRuntimeException("Failed to mark the project " + project + " with state " + State.Suspended); + } + return true; + } + + + public static String generateToken(int length) { + String charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + Random rand = new Random(System.currentTimeMillis()); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + int pos = rand.nextInt(charset.length()); + sb.append(charset.charAt(pos)); + } + return sb.toString(); + } + + class EmailInvite { + private Session _smtpSession; + private final String _smtpHost; + private int _smtpPort = -1; + private boolean _smtpUseAuth = false; + private final String _smtpUsername; + private final String _smtpPassword; + private final String _emailSender; + + public EmailInvite(String smtpHost, int smtpPort, boolean smtpUseAuth, final String smtpUsername, final String smtpPassword, String emailSender, boolean smtpDebug) { + _smtpHost = smtpHost; + _smtpPort = smtpPort; + _smtpUseAuth = smtpUseAuth; + _smtpUsername = smtpUsername; + _smtpPassword = smtpPassword; + _emailSender = emailSender; + + if (_smtpHost != null) { + Properties smtpProps = new Properties(); + smtpProps.put("mail.smtp.host", smtpHost); + smtpProps.put("mail.smtp.port", smtpPort); + smtpProps.put("mail.smtp.auth", ""+smtpUseAuth); + if (smtpUsername != null) { + smtpProps.put("mail.smtp.user", smtpUsername); + } + + smtpProps.put("mail.smtps.host", smtpHost); + smtpProps.put("mail.smtps.port", smtpPort); + smtpProps.put("mail.smtps.auth", "" + smtpUseAuth); + if (smtpUsername != null) { + smtpProps.put("mail.smtps.user", smtpUsername); + } + + if ((smtpUsername != null) && (smtpPassword != null)) { + _smtpSession = Session.getInstance(smtpProps, new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(smtpUsername, smtpPassword); + } + }); + } else { + _smtpSession = Session.getInstance(smtpProps); + } + _smtpSession.setDebug(smtpDebug); + } else { + _smtpSession = null; + } + } + + public void sendInvite(String token, String email, long projectId) throws MessagingException, UnsupportedEncodingException { + if (_smtpSession != null) { + InternetAddress address = null; + if (email != null) { + try { + address= new InternetAddress(email, email); + } catch (Exception ex) { + s_logger.error("Exception creating address for: " + email, ex); + } + } + + String content = "You've been invited to join the CloudStack project id=" + projectId + ". Please use token " + token + " to complete registration"; + + SMTPMessage msg = new SMTPMessage(_smtpSession); + msg.setSender(new InternetAddress(_emailSender, _emailSender)); + msg.setFrom(new InternetAddress(_emailSender, _emailSender)); + msg.addRecipient(RecipientType.TO, address); + msg.setSubject("You are invited to join the cloud stack project id=" + projectId); + msg.setSentDate(new Date(System.currentTimeMillis() >> 10)); + msg.setContent(content, "text/plain"); + msg.saveChanges(); + + SMTPTransport smtpTrans = null; + if (_smtpUseAuth) { + smtpTrans = new SMTPSSLTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword)); + } else { + smtpTrans = new SMTPTransport(_smtpSession, new URLName("smtp", _smtpHost, _smtpPort, null, _smtpUsername, _smtpPassword)); + } + smtpTrans.connect(); + smtpTrans.sendMessage(msg, msg.getAllRecipients()); + smtpTrans.close(); + } + } + } } diff --git a/server/src/com/cloud/projects/ProjectVO.java b/server/src/com/cloud/projects/ProjectVO.java index ae5ea6e2b77..5c93c371b3b 100644 --- a/server/src/com/cloud/projects/ProjectVO.java +++ b/server/src/com/cloud/projects/ProjectVO.java @@ -50,16 +50,16 @@ public class ProjectVO implements Project{ @Column(name="project_account_id") long projectAccountId; + @Column(name="state") + @Enumerated(value=EnumType.STRING) + private State state; + @Column(name=GenericDao.CREATED_COLUMN) private Date created; @Column(name=GenericDao.REMOVED_COLUMN) private Date removed; - @Column(name="state") - @Enumerated(value=EnumType.STRING) - private State state; - protected ProjectVO(){ } @@ -68,7 +68,7 @@ public class ProjectVO implements Project{ this.displayText = displayText; this.projectAccountId = projectAccountId; this.domainId = domainId; - this.state = State.Inactive; + this.state = State.Active; } @Override diff --git a/server/src/com/cloud/projects/dao/ProjectDao.java b/server/src/com/cloud/projects/dao/ProjectDao.java index 53a397a054e..767dc369349 100644 --- a/server/src/com/cloud/projects/dao/ProjectDao.java +++ b/server/src/com/cloud/projects/dao/ProjectDao.java @@ -17,6 +17,9 @@ */ package com.cloud.projects.dao; +import java.util.List; + +import com.cloud.projects.Project; import com.cloud.projects.ProjectVO; import com.cloud.utils.db.GenericDao; @@ -27,5 +30,7 @@ public interface ProjectDao extends GenericDao{ Long countProjectsForDomain(long domainId); ProjectVO findByProjectAccountId(long projectAccountId); + + List listByState(Project.State state); } diff --git a/server/src/com/cloud/projects/dao/ProjectDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectDaoImpl.java index 4c307fd5292..6760a918d67 100644 --- a/server/src/com/cloud/projects/dao/ProjectDaoImpl.java +++ b/server/src/com/cloud/projects/dao/ProjectDaoImpl.java @@ -1,9 +1,13 @@ package com.cloud.projects.dao; +import java.util.List; + import javax.ejb.Local; import org.apache.log4j.Logger; +import com.cloud.projects.Project; +import com.cloud.projects.ProjectInvitationVO; import com.cloud.projects.ProjectVO; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; @@ -24,6 +28,7 @@ public class ProjectDaoImpl extends GenericDaoBase implements P AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), SearchCriteria.Op.EQ); AllFieldsSearch.and("domainId", AllFieldsSearch.entity().getDomainId(), SearchCriteria.Op.EQ); AllFieldsSearch.and("projectAccountId", AllFieldsSearch.entity().getProjectAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ); AllFieldsSearch.done(); CountByDomain = createSearchBuilder(Long.class); @@ -75,4 +80,12 @@ public class ProjectDaoImpl extends GenericDaoBase implements P return findOneBy(sc); } + + @Override + public List listByState(Project.State state) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("state", state); + + return listBy(sc); + } } diff --git a/server/src/com/cloud/projects/dao/ProjectInvitationDao.java b/server/src/com/cloud/projects/dao/ProjectInvitationDao.java index 21089426da8..aa8eccd0cdd 100644 --- a/server/src/com/cloud/projects/dao/ProjectInvitationDao.java +++ b/server/src/com/cloud/projects/dao/ProjectInvitationDao.java @@ -27,4 +27,7 @@ public interface ProjectInvitationDao extends GenericDao listExpiredInvitations(); boolean expirePendingInvitations(long timeOut); boolean isActive(long id, long timeout); + ProjectInvitationVO findPendingByEmailAndProjectId(String email, long projectId); + ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId); + void cleanupInvitations(long projectId); } diff --git a/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java index 8ef568d8db1..3d0d425afa6 100644 --- a/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java +++ b/server/src/com/cloud/projects/dao/ProjectInvitationDaoImpl.java @@ -26,6 +26,8 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("email", email); + sc.setParameters("projectId", projectId); + sc.setParameters("state", State.Pending); + + return findOneBy(sc); + } + + @Override + public ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("token", token); + sc.setParameters("projectId", projectId); + sc.setParameters("state", State.Pending); + + return findOneBy(sc); + } + + @Override + public void cleanupInvitations(long projectId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("projectId", projectId); + + int numberRemoved = remove(sc); + s_logger.debug("Removed " + numberRemoved + " invitations for project id=" + projectId); + } } diff --git a/server/src/com/cloud/server/ManagementServerExtImpl.java b/server/src/com/cloud/server/ManagementServerExtImpl.java index 613211a7577..2afcb074d1e 100644 --- a/server/src/com/cloud/server/ManagementServerExtImpl.java +++ b/server/src/com/cloud/server/ManagementServerExtImpl.java @@ -31,6 +31,7 @@ import com.cloud.api.commands.GetUsageRecordsCmd; import com.cloud.domain.dao.DomainDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.Project; import com.cloud.server.api.response.UsageTypeResponse; import com.cloud.usage.UsageJobVO; import com.cloud.usage.UsageTypes; @@ -102,12 +103,24 @@ public class ManagementServerExtImpl extends ManagementServerImpl implements Man Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); Account userAccount = null; - Account account = (Account)UserContext.current().getCaller(); + Account caller = (Account)UserContext.current().getCaller(); Long usageType = cmd.getUsageType(); + Long projectId = cmd.getProjectId(); + + if (projectId != null) { + if (accountId != null) { + throw new InvalidParameterValueException("Projectid and accountId can't be specified together"); + } + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + accountId = project.getProjectAccountId(); + } //if accountId is not specified, use accountName and domainId if ((accountId == null) && (accountName != null) && (domainId != null)) { - if (_domainDao.isChildDomain(account.getDomainId(), domainId)) { + if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) { Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null); List accounts = _accountDao.listAccounts(accountName, domainId, filter); if(accounts.size() > 0){ @@ -127,13 +140,13 @@ public class ManagementServerExtImpl extends ManagementServerImpl implements Man //If accountId couldn't be found using accountName and domainId, get it from userContext if(accountId == null){ - accountId = account.getId(); + accountId = caller.getId(); //List records for all the accounts if the caller account is of type admin. //If account_id or account_name is explicitly mentioned, list records for the specified account only even if the caller is of type admin - if(account.getType() == Account.ACCOUNT_TYPE_ADMIN){ + if(caller.getType() == Account.ACCOUNT_TYPE_ADMIN){ isAdmin = true; } - s_logger.debug("Account details not available. Using userContext accountId: "+accountId); + s_logger.debug("Account details not available. Using userContext accountId: " + accountId); } Date startDate = cmd.getStartDate(); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index d9f13ffec6e..6f81f3813cc 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -1137,8 +1137,12 @@ public class ManagementServerImpl implements ManagementServer { Long networkId = cmd.getNetworkId(); Boolean forVirtual = cmd.getForVirtualNetwork(); String vlanType = null; + Long projectId = cmd.getProjectId(); if (accountName != null && domainId != null) { + if (projectId != null) { + throw new InvalidParameterValueException("Account and projectId can't be specified together"); + } Account account = _accountDao.findActiveAccount(accountName, domainId); if (account == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); @@ -1154,6 +1158,15 @@ public class ManagementServerImpl implements ManagementServer { vlanType = VlanType.DirectAttached.toString(); } } + + //set project information + if (projectId != null) { + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + accountId = project.getProjectAccountId(); + } Filter searchFilter = new Filter(VlanVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); @@ -1599,33 +1612,49 @@ public class ManagementServerImpl implements ManagementServer { @Override public List searchForEvents(ListEventsCmd cmd) { - Account account = UserContext.current().getCaller(); - Long accountId = null; + Account caller = UserContext.current().getCaller(); + List permittedAccounts = new ArrayList(); boolean isAdmin = false; String accountName = cmd.getAccountName(); Long domainId = cmd.getDomainId(); + Long projectId = cmd.getProjectId(); - if ((account == null) || isAdmin(account.getType())) { + if ((caller == null) || isAdmin(caller.getType())) { isAdmin = true; // validate domainId before proceeding if (domainId != null) { - if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) { + if ((caller != null) && !_domainDao.isChildDomain(caller.getDomainId(), domainId)) { throw new PermissionDeniedException("Invalid domain id (" + domainId + ") given, unable to list events."); } if (accountName != null) { Account userAccount = _accountDao.findAccount(accountName, domainId); if (userAccount != null) { - accountId = userAccount.getId(); + permittedAccounts.add(userAccount.getId()); } else { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); } } } else { - domainId = ((account == null) ? DomainVO.ROOT_DOMAIN : account.getDomainId()); + domainId = ((caller == null) ? DomainVO.ROOT_DOMAIN : caller.getDomainId()); } } else { - accountId = account.getId(); + permittedAccounts.add(caller.getId()); + } + + //set project information + if (projectId != null) { + permittedAccounts.clear(); + Project project = _projectMgr.getProject(projectId); + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + if (!_projectMgr.canAccessProjectAccount(caller, project.getProjectAccountId())) { + throw new InvalidParameterValueException("Account " + caller + " can't access project id=" + projectId); + } + permittedAccounts.add(project.getProjectAccountId()); + } else if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL){ + permittedAccounts.addAll(_projectMgr.listPermittedProjectAccounts(caller.getId())); } Filter searchFilter = new Filter(EventVO.class, "createDate", false, cmd.getStartIndex(), cmd.getPageSizeVal()); @@ -1650,7 +1679,7 @@ public class ManagementServerImpl implements ManagementServer { sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("levelL", sb.entity().getLevel(), SearchCriteria.Op.LIKE); sb.and("levelEQ", sb.entity().getLevel(), SearchCriteria.Op.EQ); - sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); + sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.IN); sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.LIKE); sb.and("domainIdEQ", sb.entity().getDomainId(), SearchCriteria.Op.EQ); sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); @@ -1658,7 +1687,7 @@ public class ManagementServerImpl implements ManagementServer { sb.and("createDateG", sb.entity().getCreateDate(), SearchCriteria.Op.GTEQ); sb.and("createDateL", sb.entity().getCreateDate(), SearchCriteria.Op.LTEQ); - if ((accountId == null) && (accountName == null) && (domainId != null) && isAdmin) { + if ((permittedAccounts.isEmpty()) && (accountName == null) && (domainId != null) && isAdmin) { // if accountId isn't specified, we can do a domain match for the admin case SearchBuilder domainSearch = _domainDao.createSearchBuilder(); domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); @@ -1682,8 +1711,8 @@ public class ManagementServerImpl implements ManagementServer { sc.setParameters("levelEQ", level); } - if (accountId != null) { - sc.setParameters("accountId", accountId); + if (!permittedAccounts.isEmpty()) { + sc.setParameters("accountId", permittedAccounts.toArray()); } else if (domainId != null) { if (accountName != null) { sc.setParameters("domainIdEQ", domainId); diff --git a/server/src/com/cloud/server/api/response/UsageRecordResponse.java b/server/src/com/cloud/server/api/response/UsageRecordResponse.java index 787a890a5c4..fcd06c593e1 100644 --- a/server/src/com/cloud/server/api/response/UsageRecordResponse.java +++ b/server/src/com/cloud/server/api/response/UsageRecordResponse.java @@ -21,18 +21,29 @@ package com.cloud.server.api.response; import com.cloud.api.ApiConstants; import com.cloud.api.response.BaseResponse; +import com.cloud.api.response.ControlledEntityResponse; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; -public class UsageRecordResponse extends BaseResponse { +@SuppressWarnings("unused") +public class UsageRecordResponse extends BaseResponse implements ControlledEntityResponse{ @SerializedName(ApiConstants.ACCOUNT) @Param(description="the user account name") private String accountName; @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the user account Id") private Long accountId; + + @SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id of the ipaddress") + private Long projectId; + + @SerializedName(ApiConstants.PROJECT) @Param(description="the project name of the address") + private String projectName; @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain ID number") private Long domainId; + + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain the public IP address is associated with") + private String domainName; @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID number") private Long zoneId; @@ -88,163 +99,84 @@ public class UsageRecordResponse extends BaseResponse { @SerializedName("issourcenat") @Param(description="source Nat flag for IPAddress") private Boolean isSourceNat; - public String getAccountName() { - return accountName; - } - + @Override public void setAccountName(String accountName) { this.accountName = accountName; } - - public Long getAccountId() { - return accountId; - } public void setAccountId(Long accountId) { this.accountId = accountId; } - - - public Long getDomainId() { - return domainId; - } - + + @Override public void setDomainId(Long domainId) { this.domainId = domainId; } - public Long getZoneId() { - return zoneId; - } - public void setZoneId(Long zoneId) { this.zoneId = zoneId; } - public String getDescription() { - return description; - } - public void setDescription(String description) { this.description = description; } - public String getUsage() { - return usage; - } - public void setUsage(String usage) { this.usage = usage; } - public Integer getUsageType() { - return usageType; - } - public void setUsageType(Integer usageType) { this.usageType = usageType; } - public String getRawUsage() { - return rawUsage; - } - public void setRawUsage(String rawUsage) { this.rawUsage = rawUsage; } - public Long getVirtualMachineId() { - return virtualMachineId; - } - public void setVirtualMachineId(Long virtualMachineId) { this.virtualMachineId = virtualMachineId; } - public String getVmName() { - return vmName; - } - public void setVmName(String vmName) { this.vmName = vmName; } - public Long getServiceOfferingId() { - return serviceOfferingId; - } - public void setServiceOfferingId(Long serviceOfferingId) { this.serviceOfferingId = serviceOfferingId; } - public Long getTemplateId() { - return templateId; - } - public void setTemplateId(Long templateId) { this.templateId = templateId; } - public Long getUsageId() { - return usageId; - } - public void setUsageId(Long usageId) { this.usageId = usageId; } - public String getType() { - return type; - } - public void setType(String type) { this.type = type; } - - public Long getSize() { - return size; - } public void setSize(Long size) { this.size = size; } - public String getStartDate() { - return startDate; - } - public void setStartDate(String startDate) { this.startDate = startDate; } - public String getEndDate() { - return endDate; - } - public void setEndDate(String endDate) { this.endDate = endDate; } - public String getIpAddress() { - return ipAddress; - } - public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } - public String getAssignedDate() { - return assignedDate; - } - public void setAssignedDate(String assignedDate) { this.assignedDate = assignedDate; } - public String getReleasedDate() { - return releasedDate; - } - public void setReleasedDate(String releasedDate) { this.releasedDate = releasedDate; } @@ -252,8 +184,19 @@ public class UsageRecordResponse extends BaseResponse { public void setSourceNat(Boolean isSourceNat) { this.isSourceNat = isSourceNat; } + + @Override + public void setProjectId(Long projectId) { + this.projectId = projectId; + } - public Boolean isSourceNat() { - return isSourceNat; + @Override + public void setProjectName(String projectName) { + this.projectName = projectName; + } + + @Override + public void setDomainName(String domainName) { + this.domainName = domainName; } } diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 09900991628..7c21e0d99e4 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -80,6 +80,7 @@ import com.cloud.network.security.dao.SecurityGroupDao; import com.cloud.network.vpn.RemoteAccessVpnService; import com.cloud.projects.Project; import com.cloud.projects.ProjectManager; +import com.cloud.projects.dao.ProjectDao; import com.cloud.server.auth.UserAuthenticator; import com.cloud.storage.StorageManager; import com.cloud.storage.VMTemplateVO; @@ -180,6 +181,8 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag private DomainManager _domainMgr; @Inject private ProjectManager _projectMgr; + @Inject + private ProjectDao _projectDao; private Adapters _userAuthenticators; @@ -414,7 +417,6 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag } return cleanupAccount(account, callerUserId, caller); - } @Override @@ -1154,7 +1156,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag List disabledAccounts = _accountDao.findCleanupsForDisabledAccounts(); s_logger.info("Found " + disabledAccounts.size() + " disabled accounts to cleanup"); for (AccountVO account : disabledAccounts) { - s_logger.debug("Cleaning up " + account.getId()); + s_logger.debug("Disabling account " + account.getId()); try { if (disableAccount(account.getId())) { account.setNeedsCleanup(false); @@ -1176,13 +1178,30 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag s_logger.debug("Removing inactive domain id=" + domainId); _domainMgr.removeDomain(domainId); } else { - s_logger.debug("Can't remove inactive domain id=" + domainId + " as it has accounts that need clenaup"); + s_logger.debug("Can't remove inactive domain id=" + domainId + " as it has accounts that need cleanup"); } } catch (Exception e) { s_logger.error("Skipping due to error on domain " + domainId, e); } } + //cleanup inactive projects + List inactiveProjects = _projectDao.listByState(Project.State.Disabled); + s_logger.info("Found " + inactiveProjects.size() + " disabled projects to cleanup"); + for (Project project : inactiveProjects) { + try { + Account projectAccount = getAccount(project.getProjectAccountId()); + if (projectAccount == null) { + s_logger.debug("Removing inactive project id=" + project.getId()); + _projectMgr.deleteProject(project.getId()); + } else { + s_logger.debug("Can't remove disabled project " + project + " as it has non removed account id=" + project.getId()); + } + } catch (Exception e) { + s_logger.error("Skipping due to error on project " + project, e); + } + } + } catch (Exception e) { s_logger.error("Exception ", e); } finally { diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index b9269c8310e..1ae24aa8a68 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1709,7 +1709,10 @@ CREATE TABLE `cloud`.`project_invitations` ( PRIMARY KEY (`id`), CONSTRAINT `fk_project_invitations__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_project_invitations__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, - CONSTRAINT `fk_project_invitations__project_id` FOREIGN KEY(`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE + CONSTRAINT `fk_project_invitations__project_id` FOREIGN KEY(`project_id`) REFERENCES `projects`(`id`) ON DELETE CASCADE, + UNIQUE (`project_id`, `account_id`), + UNIQUE (`project_id`, `email`), + UNIQUE (`project_id`, `token`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/setup/db/db/schema-2212to30.sql b/setup/db/db/schema-2212to30.sql index f057e7bb18a..dae28eeec6d 100644 --- a/setup/db/db/schema-2212to30.sql +++ b/setup/db/db/schema-2212to30.sql @@ -85,6 +85,13 @@ INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-serv INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.invite.required', 'false', 'If invitation confirmation is required when add account to project. Default value is false'); INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.invite.timeout', '86400', 'Invitation expiration time (in seconds). Default is 1 day - 86400 seconds'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.email.sender', null, 'Sender of project invitation email (will be in the From header of the email).'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.smtp.host', null, 'SMTP hostname used for sending out email project invitations'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.smtp.password', null, 'Password for SMTP authentication (applies only if project.smtp.useAuth is true)'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.smtp.port', '465', 'Port the SMTP server is listening on'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.smtp.useAuth', null, 'If true, use SMTP authentication when sending emails'); +INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'project.smtp.username', null, 'Username for SMTP authentication (applies only if project.smtp.useAuth is true)'); + ALTER TABLE `cloud`.`domain_router` ADD COLUMN `template_version` varchar(100) COMMENT 'template version' AFTER role; ALTER TABLE `cloud`.`domain_router` ADD COLUMN `scripts_version` varchar(100) COMMENT 'scripts version' AFTER template_version;