From 17f785f07a3031f0b6638e526340d62817b5251d Mon Sep 17 00:00:00 2001 From: alena Date: Fri, 1 Jul 2011 09:21:42 -0700 Subject: [PATCH] Intermidiate checkin for Projects feature; added 3 new apis - create/delete/listProject(s) - as a part of checkin --- api/src/com/cloud/api/BaseCmd.java | 79 +++--- api/src/com/cloud/api/ResponseGenerator.java | 10 +- .../cloud/api/commands/CreateProjectCmd.java | 123 ++++++++++ .../cloud/api/commands/DeleteProjectCmd.java | 100 ++++++++ .../cloud/api/commands/ListProjectsCmd.java | 111 +++++++++ .../cloud/api/response/ProjectResponse.java | 103 ++++++++ api/src/com/cloud/event/EventTypes.java | 4 + api/src/com/cloud/projects/Project.java | 25 ++ .../com/cloud/projects/ProjectService.java | 36 +++ client/tomcatconf/commands.properties.in | 4 + .../src/com/cloud/api/ApiResponseHelper.java | 29 ++- .../src/com/cloud/configuration/Config.java | 1 + .../DefaultComponentLibrary.java | 4 + .../com/cloud/projects/ProjectManager.java | 4 + .../cloud/projects/ProjectManagerImpl.java | 224 ++++++++++++++++++ server/src/com/cloud/projects/ProjectVO.java | 140 +++++++++++ .../com/cloud/projects/dao/ProjectDao.java | 8 + .../cloud/projects/dao/ProjectDaoImpl.java | 11 + .../com/cloud/user/AccountManagerImpl.java | 5 +- setup/db/create-schema.sql | 20 ++ 20 files changed, 989 insertions(+), 52 deletions(-) create mode 100644 api/src/com/cloud/api/commands/CreateProjectCmd.java create mode 100644 api/src/com/cloud/api/commands/DeleteProjectCmd.java create mode 100644 api/src/com/cloud/api/commands/ListProjectsCmd.java create mode 100644 api/src/com/cloud/api/response/ProjectResponse.java create mode 100644 api/src/com/cloud/projects/Project.java create mode 100644 api/src/com/cloud/projects/ProjectService.java create mode 100644 server/src/com/cloud/projects/ProjectManager.java create mode 100644 server/src/com/cloud/projects/ProjectManagerImpl.java create mode 100644 server/src/com/cloud/projects/ProjectVO.java create mode 100644 server/src/com/cloud/projects/dao/ProjectDao.java create mode 100644 server/src/com/cloud/projects/dao/ProjectDaoImpl.java diff --git a/api/src/com/cloud/api/BaseCmd.java b/api/src/com/cloud/api/BaseCmd.java index 80f0dd1aa0c..30fe4be5ead 100755 --- a/api/src/com/cloud/api/BaseCmd.java +++ b/api/src/com/cloud/api/BaseCmd.java @@ -18,43 +18,44 @@ package com.cloud.api; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; - -import org.apache.log4j.Logger; - -import com.cloud.configuration.ConfigurationService; -import com.cloud.consoleproxy.ConsoleProxyService; -import com.cloud.dao.EntityManager; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.NetworkService; -import com.cloud.network.VirtualNetworkApplianceService; -import com.cloud.network.lb.LoadBalancingRulesService; -import com.cloud.network.rules.RulesService; -import com.cloud.network.security.SecurityGroupService; -import com.cloud.network.vpn.RemoteAccessVpnService; -import com.cloud.resource.ResourceService; -import com.cloud.server.ManagementService; -import com.cloud.storage.StorageService; -import com.cloud.storage.snapshot.SnapshotService; -import com.cloud.template.TemplateService; -import com.cloud.user.Account; -import com.cloud.user.AccountService; -import com.cloud.user.UserContext; -import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentLocator; -import com.cloud.vm.UserVmService; -import com.cloud.vm.BareMetalVmService; + +import org.apache.log4j.Logger; + +import com.cloud.configuration.ConfigurationService; +import com.cloud.consoleproxy.ConsoleProxyService; +import com.cloud.dao.EntityManager; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.NetworkService; +import com.cloud.network.VirtualNetworkApplianceService; +import com.cloud.network.lb.LoadBalancingRulesService; +import com.cloud.network.rules.RulesService; +import com.cloud.network.security.SecurityGroupService; +import com.cloud.network.vpn.RemoteAccessVpnService; +import com.cloud.projects.ProjectService; +import com.cloud.resource.ResourceService; +import com.cloud.server.ManagementService; +import com.cloud.storage.StorageService; +import com.cloud.storage.snapshot.SnapshotService; +import com.cloud.template.TemplateService; +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.UserContext; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.vm.BareMetalVmService; +import com.cloud.vm.UserVmService; public abstract class BaseCmd { private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName()); @@ -114,7 +115,8 @@ public abstract class BaseCmd { public static RulesService _rulesService; public static LoadBalancingRulesService _lbService; public static RemoteAccessVpnService _ravService; - public static BareMetalVmService _bareMetalVmService; + public static BareMetalVmService _bareMetalVmService; + public static ProjectService _projectService; static void setComponents(ResponseGenerator generator) { @@ -136,7 +138,8 @@ public abstract class BaseCmd { _lbService = locator.getManager(LoadBalancingRulesService.class); _ravService = locator.getManager(RemoteAccessVpnService.class); _responseGenerator = generator; - _bareMetalVmService = locator.getManager(BareMetalVmService.class); + _bareMetalVmService = locator.getManager(BareMetalVmService.class); + _projectService = locator.getManager(ProjectService.class); } public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException; diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java index 57d26ef5d46..0b2a1611d8a 100644 --- a/api/src/com/cloud/api/ResponseGenerator.java +++ b/api/src/com/cloud/api/ResponseGenerator.java @@ -19,7 +19,6 @@ package com.cloud.api; import java.text.DecimalFormat; import java.util.List; -import java.util.Set; import com.cloud.api.commands.QueryAsyncJobResultCmd; import com.cloud.api.response.AccountResponse; @@ -43,9 +42,10 @@ import com.cloud.api.response.LoadBalancerResponse; import com.cloud.api.response.NetworkOfferingResponse; import com.cloud.api.response.NetworkResponse; import com.cloud.api.response.PodResponse; +import com.cloud.api.response.ProjectResponse; import com.cloud.api.response.RemoteAccessVpnResponse; -import com.cloud.api.response.ResourceLimitResponse; import com.cloud.api.response.ResourceCountResponse; +import com.cloud.api.response.ResourceLimitResponse; import com.cloud.api.response.SecurityGroupResponse; import com.cloud.api.response.ServiceOfferingResponse; import com.cloud.api.response.SnapshotPolicyResponse; @@ -63,8 +63,8 @@ import com.cloud.api.response.ZoneResponse; import com.cloud.async.AsyncJob; import com.cloud.capacity.Capacity; import com.cloud.configuration.Configuration; -import com.cloud.configuration.ResourceLimit; import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceLimit; import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; @@ -86,6 +86,7 @@ import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; +import com.cloud.projects.Project; import com.cloud.storage.Snapshot; import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; @@ -95,7 +96,6 @@ import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; import com.cloud.vm.InstanceGroup; import com.cloud.vm.VirtualMachine; @@ -201,5 +201,7 @@ public interface ResponseGenerator { Long getSecurityGroupId(String groupName, long accountId); List createIsoResponses(long isoId, Long zoneId, boolean readyOnly); + + ProjectResponse createProjectResponse(Project project); } diff --git a/api/src/com/cloud/api/commands/CreateProjectCmd.java b/api/src/com/cloud/api/commands/CreateProjectCmd.java new file mode 100644 index 00000000000..2fb817b189f --- /dev/null +++ b/api/src/com/cloud/api/commands/CreateProjectCmd.java @@ -0,0 +1,123 @@ +/** + * 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.projects.Project; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@Implementation(description="Creates a project", responseObject=ProjectResponse.class) +public class CreateProjectCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(CreateProjectCmd.class.getName()); + + private static final String s_name = "createprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @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, 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") + private String name; + + @Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, required=true, description="display text of the project") + private String displayText; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="the zone id of the project") + private Long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public String getName() { + return name; + } + + public String getDisplayText() { + return displayText; + } + + public Long getZoneId() { + return zoneId; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if ((account == null) || isAdmin(account.getType())) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + UserContext.current().setEventDetails("Project Name: "+ getName() + ", zoneId " + zoneId); + Project project = _projectService.createProject(getName(), getDisplayText(), getZoneId(), getAccountName(), getDomainId()); + if (project != null) { + ProjectResponse response = _responseGenerator.createProjectResponse(project); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create a project"); + } + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/DeleteProjectCmd.java b/api/src/com/cloud/api/commands/DeleteProjectCmd.java new file mode 100644 index 00000000000..8719dea68d3 --- /dev/null +++ b/api/src/com/cloud/api/commands/DeleteProjectCmd.java @@ -0,0 +1,100 @@ +/** + * 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.BaseCmd.CommandType; +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.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.projects.Project; +import com.cloud.user.UserContext; + +@Implementation(description="Deletes a project", responseObject=SuccessResponse.class) +public class DeleteProjectCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeleteProjectCmd.class.getName()); + + private static final String s_name = "deleteprojectresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, description="id of the project to be deleted") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long geId() { + return id; + } + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + UserContext.current().setEventDetails("Project Id: " + id); + boolean result = _projectService.deleteProject(id); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to delete project"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_PROJECT_DELETE; + } + + @Override + public String getEventDescription() { + return "Deleting project: " + id; + } + + @Override + public long getEntityOwnerId() { + Project project = _projectService.getProject(id); + if (project == null) { + throw new InvalidParameterValueException("Project id=" + id + " doesn't exist"); + } else { + return _projectService.getProject(id).getAccountId(); + } + } +} \ No newline at end of file diff --git a/api/src/com/cloud/api/commands/ListProjectsCmd.java b/api/src/com/cloud/api/commands/ListProjectsCmd.java new file mode 100644 index 00000000000..2ff4f1551d6 --- /dev/null +++ b/api/src/com/cloud/api/commands/ListProjectsCmd.java @@ -0,0 +1,111 @@ +/** + * 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 java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseListCmd; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.response.ListResponse; +import com.cloud.api.response.ProjectResponse; +import com.cloud.projects.Project; + +@Implementation(description="Lists projects and provides detailed information for listed projects", responseObject=ProjectResponse.class) +public class ListProjectsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListProjectsCmd.class.getName()); + private static final String s_name = "listprojectsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.LONG, description="list projects by project ID") + private Long id; + + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="list projects by owner name") + private String accountName; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="list projects for the domain specified") + private Long domainId; + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="list projects by name") + private String name; + + @Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, description="list projects by display text") + private String displayText; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, description="list projects for specific zone") + private Long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public String getName() { + return name; + } + + public String getDisplayText() { + return displayText; + } + + public Long getZoneId() { + return zoneId; + } + + @Override + public String getCommandName() { + return s_name; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + List projects = _projectService.listProjects(id, name, displayText, zoneId, accountName, domainId, this.getKeyword(), this.getStartIndex(), this.getPageSizeVal()); + ListResponse response = new ListResponse(); + List projectResponses = new ArrayList(); + for (Project project : projects) { + ProjectResponse projectResponse = _responseGenerator.createProjectResponse(project); + projectResponses.add(projectResponse); + } + response.setResponses(projectResponses); + response.setResponseName(getCommandName()); + + this.setResponseObject(response); + } +} diff --git a/api/src/com/cloud/api/response/ProjectResponse.java b/api/src/com/cloud/api/response/ProjectResponse.java new file mode 100644 index 00000000000..1d1f2d1ae76 --- /dev/null +++ b/api/src/com/cloud/api/response/ProjectResponse.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.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ProjectResponse extends BaseResponse{ + + @SerializedName("id") @Param(description="the id of the project") + private Long id; + + @SerializedName("name") @Param(description="the name of the project") + private String name; + + @SerializedName("displaytext") @Param(description="the displaytext of the project") + private String displaytext; + + @SerializedName("zoneid") @Param(description="zone id of the project") + private Long zoneId; + + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the project") + private String accountName; + + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the project owner") + private Long domainId; + + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the project owner") + private String domain; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getZoneId() { + return zoneId; + } + + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + 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 getDisplaytext() { + return displaytext; + } + + public void setDisplaytext(String displaytext) { + this.displaytext = displaytext; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } +} diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 3b6bb30c4a6..ab291437c5a 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -200,4 +200,8 @@ public class EventTypes { public static final String EVENT_ZONE_VLAN_ASSIGN = "ZONE.VLAN.ASSIGN"; public static final String EVENT_ZONE_VLAN_RELEASE = "ZONE.VLAN.RELEASE"; + //Projects + public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE"; + public static final String EVENT_PROJECT_DELETE = "PROJECT.DELETE"; + } diff --git a/api/src/com/cloud/projects/Project.java b/api/src/com/cloud/projects/Project.java new file mode 100644 index 00000000000..5131428a995 --- /dev/null +++ b/api/src/com/cloud/projects/Project.java @@ -0,0 +1,25 @@ +package com.cloud.projects; + +import java.util.Date; + +import com.cloud.acl.ControlledEntity; + +public interface Project extends ControlledEntity{ + + String getDisplayText(); + + long getDomainId(); + + long getAccountId(); + + long getId(); + + Date getCreated(); + + Date getRemoved(); + + long getDataCenterId(); + + String getName(); + +} diff --git a/api/src/com/cloud/projects/ProjectService.java b/api/src/com/cloud/projects/ProjectService.java new file mode 100644 index 00000000000..cb2def11986 --- /dev/null +++ b/api/src/com/cloud/projects/ProjectService.java @@ -0,0 +1,36 @@ +package com.cloud.projects; + +import java.util.List; + + +public interface ProjectService { + /** + * Creates a new project + * + * @param name - project name + * @param displayText - project display text + * @param zoneId - id of the zone the project belongs to + * @param accountName - account name of the project owner + * @param domainId - domainid of the project owner + * @return the project if created successfully, null otherwise + */ + Project createProject(String name, String displayText, long zoneId, String accountName, Long domainId); + + /** + * Deletes a project + * + * @param id - project id + * @return true if the project was deleted successfully, false otherwise + */ + boolean deleteProject(long id); + + /** + * Gets a project by id + * + * @param id - project id + * @return project object + */ + Project getProject(long id); + + List listProjects(Long id, String name, String displayText, Long zoneId, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize); +} diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 0deb6d5039e..e2265d5cfcc 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -260,3 +260,7 @@ createSSHKeyPair=com.cloud.api.commands.CreateSSHKeyPairCmd;15 deleteSSHKeyPair=com.cloud.api.commands.DeleteSSHKeyPairCmd;15 listSSHKeyPairs=com.cloud.api.commands.ListSSHKeyPairsCmd;15 +#### Projects commands +createProject=com.cloud.api.commands.CreateProjectCmd;15 +deleteProject=com.cloud.api.commands.DeleteProjectCmd;15 +listProjects=com.cloud.api.commands.ListProjectsCmd;15 diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 6055d1efa27..14d4b47cb64 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -55,11 +55,11 @@ import com.cloud.api.response.ListResponse; import com.cloud.api.response.LoadBalancerResponse; import com.cloud.api.response.NetworkOfferingResponse; import com.cloud.api.response.NetworkResponse; -import com.cloud.api.response.NicResponse; import com.cloud.api.response.PodResponse; +import com.cloud.api.response.ProjectResponse; import com.cloud.api.response.RemoteAccessVpnResponse; -import com.cloud.api.response.ResourceLimitResponse; import com.cloud.api.response.ResourceCountResponse; +import com.cloud.api.response.ResourceLimitResponse; import com.cloud.api.response.SecurityGroupResponse; import com.cloud.api.response.SecurityGroupResultObject; import com.cloud.api.response.ServiceOfferingResponse; @@ -81,9 +81,9 @@ import com.cloud.async.AsyncJobResult; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; import com.cloud.configuration.Configuration; +import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.ResourceLimit; -import com.cloud.configuration.ResourceCount; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -116,11 +116,11 @@ import com.cloud.network.rules.StaticNatRule; import com.cloud.network.security.IngressRule; import com.cloud.network.security.SecurityGroup; import com.cloud.network.security.SecurityGroupRules; -import com.cloud.network.security.SecurityGroupVO; import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; +import com.cloud.projects.Project; import com.cloud.server.Criteria; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; @@ -148,12 +148,10 @@ import com.cloud.user.UserContext; import com.cloud.user.UserStatisticsVO; import com.cloud.user.UserVO; import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.net.NetUtils; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.InstanceGroup; -import com.cloud.vm.InstanceGroupVO; import com.cloud.vm.NicProfile; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; @@ -2076,4 +2074,23 @@ public class ApiResponseHelper implements ResponseGenerator { return sg.getId(); } } + + @Override + public ProjectResponse createProjectResponse(Project project) { + ProjectResponse response = new ProjectResponse(); + response.setId(project.getId()); + response.setName(project.getName()); + response.setDisplaytext(project.getDisplayText()); + response.setZoneId(project.getDataCenterId()); + + Account owner = ApiDBUtils.findAccountById(project.getAccountId()); + response.setAccountName(owner.getAccountName()); + + Domain domain = ApiDBUtils.findDomainById(owner.getDomainId()); + response.setDomainId(domain.getId()); + response.setDomain(domain.getName()); + + response.setObjectName("project"); + return response; + } } diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index a3cb23afcc7..80c9ad1a79d 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -238,6 +238,7 @@ public enum Config { DefaultMaxAccountTemplates("Account Defaults", ManagementServer.class, Long.class, "max.account.templates", "20", "The default maximum number of templates that can be deployed for an account", null), DefaultMaxAccountSnapshots("Account Defaults", ManagementServer.class, Long.class, "max.account.snapshots", "20", "The default maximum number of snapshots that can be created for an account", null), DefaultMaxAccountVolumes("Account Defaults", ManagementServer.class, Long.class, "max.account.volumes", "20", "The default maximum number of volumes that can be created for an account", null), + DefaultMaxAccountProjects("Account Defaults", ManagementServer.class, Long.class, "max.account.projects", "20", "The default maximum number of projects that can be created for an account", null), DirectAgentLoadSize("Advanced", ManagementServer.class, Integer.class, "direct.agent.load.size", "16", "The number of direct agents to load each time", null), AgentLbEnable("Advanced", ClusterManager.class, Boolean.class, "agent.lb.enabled", "true", "If agent load balancing enabled in cluster setup", null), diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 2bf2b978c12..6ee3400332e 100644 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -103,6 +103,8 @@ import com.cloud.network.security.dao.SecurityGroupWorkDaoImpl; import com.cloud.network.security.dao.VmRulesetLogDaoImpl; import com.cloud.network.vpn.RemoteAccessVpnManagerImpl; import com.cloud.offerings.dao.NetworkOfferingDaoImpl; +import com.cloud.projects.ProjectManagerImpl; +import com.cloud.projects.dao.ProjectDaoImpl; import com.cloud.resource.ResourceManagerImpl; import com.cloud.service.dao.ServiceOfferingDaoImpl; import com.cloud.storage.StorageManagerImpl; @@ -274,6 +276,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addDao("DcDetailsDao", DcDetailsDaoImpl.class); addDao("SwiftDao", SwiftDaoImpl.class); addDao("AgentTransferMapDao", HostTransferMapDaoImpl.class); + addDao("ProjectDao", ProjectDaoImpl.class); } @Override @@ -321,6 +324,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com ComponentInfo info = addManager("ConsoleProxyManager", ConsoleProxyManagerImpl.class); info.addParameter("consoleproxy.sslEnabled", "true"); addManager("ClusteredAgentManager", ClusteredAgentManagerImpl.class); + addManager("ProjectManager", ProjectManagerImpl.class); } @Override diff --git a/server/src/com/cloud/projects/ProjectManager.java b/server/src/com/cloud/projects/ProjectManager.java new file mode 100644 index 00000000000..449868b9f45 --- /dev/null +++ b/server/src/com/cloud/projects/ProjectManager.java @@ -0,0 +1,4 @@ +package com.cloud.projects; + +public interface ProjectManager extends ProjectService{ +} diff --git a/server/src/com/cloud/projects/ProjectManagerImpl.java b/server/src/com/cloud/projects/ProjectManagerImpl.java new file mode 100644 index 00000000000..f25182f45a5 --- /dev/null +++ b/server/src/com/cloud/projects/ProjectManagerImpl.java @@ -0,0 +1,224 @@ +package com.cloud.projects; + +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; +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.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.dao.ProjectDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.UserContext; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.component.ComponentLocator; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.Manager; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; + +@Local(value = { ProjectService.class }) +public class ProjectManagerImpl implements ProjectManager, Manager{ + public static final Logger s_logger = Logger.getLogger(ProjectManagerImpl.class); + private String _name; + private long _maxProjects; + + @Inject + private AccountDao _accountDao; + @Inject + private DomainDao _domainDao; + @Inject + private ProjectDao _projectDao; + @Inject + AccountManager _accountMgr; + @Inject + ConfigurationManager _configMgr; + + + @Override + public boolean configure(final String name, final Map params) throws ConfigurationException { + _name = name; + + ComponentLocator locator = ComponentLocator.getCurrentLocator(); + ConfigurationDao configDao = locator.getDao(ConfigurationDao.class); + Map configs = configDao.getConfiguration(params); + + String value = configs.get(Config.DefaultMaxAccountProjects.key()); + _maxProjects = NumbersUtil.parseLong(value, 20); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_PROJECT_CREATE, eventDescription = "creating project") + public Project createProject(String name, String displayText, long zoneId, String accountName, Long domainId) { + Account caller = UserContext.current().getCaller(); + Account owner = caller; + + //Verify request parameters + if ((accountName != null && domainId == null) || (domainId != null && accountName == null)) { + throw new InvalidParameterValueException("Account name and domain id must be specified together"); + } + + if (accountName != null) { + owner = _accountMgr.finalizeOwner(caller, accountName, domainId); + } + + DataCenter zone = _configMgr.getZone(zoneId); + + if (zone == null) { + throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); + } + + //TODO - do resource limit check here + + Project project = _projectDao.persist(new ProjectVO(name, displayText, zoneId, owner.getAccountId(), owner.getDomainId())); + + if (project != null) { + UserContext.current().setEventDetails("Project id=" + project.getId()); + } + + return project; + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_PROJECT_DELETE, eventDescription = "deleting project", async = true) + public boolean deleteProject (long projectId) { + Account caller = UserContext.current().getCaller(); + + Project project= getProject(projectId); + //verify input parameters + if (project == null) { + throw new InvalidParameterValueException("Unable to find project by id " + projectId); + } + + _accountMgr.checkAccess(caller, project); + + //TODO - delete all project resources here + + return _projectDao.remove(projectId); + + } + + @Override + public Project getProject (long projectId) { + return _projectDao.findById(projectId); + } + + @Override + public List listProjects(Long id, String name, String displayText, Long zoneId, String accountName, Long domainId, String keyword, Long startIndex, Long pageSize) { + Account caller = UserContext.current().getCaller(); + Long accountId = null; + String path = null; + + Filter searchFilter = new Filter(ProjectVO.class, "id", false, startIndex, pageSize); + SearchBuilder sb = _projectDao.createSearchBuilder(); + + if (_accountMgr.isAdmin(caller.getType())) { + if (domainId != null) { + DomainVO domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Domain id=" + domainId + " doesn't exist in the system"); + } + + _accountMgr.checkAccess(caller, domain); + + if (accountName != null) { + Account owner = _accountMgr.getActiveAccount(accountName, domainId); + if (owner == null) { + throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); + } + accountId = owner.getId(); + } + } + + if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { + DomainVO domain = _domainDao.findById(caller.getDomainId()); + path = domain.getPath(); + } + } else { + if (accountName != null && !accountName.equals(caller.getAccountName())) { + throw new PermissionDeniedException("Can't list account " + accountName + " projects; unauthorized"); + } + + if (domainId != null && domainId.equals(caller.getDomainId())) { + throw new PermissionDeniedException("Can't list domain id= " + domainId + " projects; unauthorized"); + } + + accountId = caller.getId(); + } + + if (path != null) { + SearchBuilder domainSearch = _domainDao.createSearchBuilder(); + domainSearch.and("path", domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); + sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), domainSearch.entity().getId(), JoinBuilder.JoinType.INNER); + } + + SearchCriteria sc = sb.create(); + + if (id != null) { + sc.addAnd("id", Op.EQ, id); + } + + if (name != null) { + sc.addAnd("name", Op.EQ, name); + } + + if (displayText != null) { + sc.addAnd("displayText", Op.EQ, displayText); + } + + if (zoneId != null) { + sc.addAnd("dataCenterId", Op.EQ, zoneId); + } + + if (accountId != null) { + sc.addAnd("accountId", Op.EQ, accountId); + } + + if (keyword != null) { + SearchCriteria ssc = _projectDao.createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("displayText", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + sc.addAnd("name", SearchCriteria.Op.SC, ssc); + } + + if (path != null) { + sc.setJoinParameters("domainSearch", "path", path); + } + + return _projectDao.search(sc, searchFilter); + + } + +} diff --git a/server/src/com/cloud/projects/ProjectVO.java b/server/src/com/cloud/projects/ProjectVO.java new file mode 100644 index 00000000000..68cad0b2e09 --- /dev/null +++ b/server/src/com/cloud/projects/ProjectVO.java @@ -0,0 +1,140 @@ +package com.cloud.projects; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.cloud.utils.db.GenericDao; + +@Entity +@Table(name="projects") +public class ProjectVO implements Project{ + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="name") + private String name; + + @Column(name="display_text") + String displayText; + + @Column(name="domain_id") + long domainId; + + @Column(name="account_id") + long accountId; + + @Column(name="data_center_id") + long dataCenterId; + + @Column(name=GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name=GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name="cleanup_needed") + private boolean needsCleanup = false; + + protected ProjectVO(){ + } + + public ProjectVO(String name, String displayText, long dataCenterId, long accountId, long domainId) { + this.name = name; + this.displayText = displayText; + this.accountId = accountId; + this.domainId = domainId; + this.dataCenterId = dataCenterId; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDisplayText() { + return displayText; + } + + public void setDisplayText(String displayText) { + this.displayText = displayText; + } + + @Override + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + @Override + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + @Override + public long getId() { + return id; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public long getDataCenterId() { + return dataCenterId; + } + + public void setNeedsCleanup(boolean value) { + needsCleanup = value; + } + + public boolean getNeedsCleanup() { + return needsCleanup; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder("Project["); + buf.append(id).append("|").append(name).append("|domainid=").append(domainId).append("]"); + return buf.toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ProjectVO)) { + return false; + } + ProjectVO that = (ProjectVO)obj; + if (this.id != that.id) { + return false; + } + + return true; + } +} diff --git a/server/src/com/cloud/projects/dao/ProjectDao.java b/server/src/com/cloud/projects/dao/ProjectDao.java new file mode 100644 index 00000000000..666e151cf72 --- /dev/null +++ b/server/src/com/cloud/projects/dao/ProjectDao.java @@ -0,0 +1,8 @@ +package com.cloud.projects.dao; + +import com.cloud.projects.ProjectVO; +import com.cloud.utils.db.GenericDao; + +public interface ProjectDao extends GenericDao{ + +} diff --git a/server/src/com/cloud/projects/dao/ProjectDaoImpl.java b/server/src/com/cloud/projects/dao/ProjectDaoImpl.java new file mode 100644 index 00000000000..b9efa1c55a2 --- /dev/null +++ b/server/src/com/cloud/projects/dao/ProjectDaoImpl.java @@ -0,0 +1,11 @@ +package com.cloud.projects.dao; + +import javax.ejb.Local; + +import com.cloud.projects.ProjectVO; +import com.cloud.utils.db.GenericDaoBase; + +@Local(value={ProjectDao.class}) +public class ProjectDaoImpl extends GenericDaoBase implements ProjectDao { + +} diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 3c1f138ec13..0076e113393 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -53,10 +53,9 @@ import com.cloud.api.commands.UpdateResourceLimitCmd; import com.cloud.api.commands.UpdateUserCmd; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.ResourceCount; +import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.ResourceCountVO; import com.cloud.configuration.ResourceLimitVO; -import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceCountDao; import com.cloud.configuration.dao.ResourceLimitDao; @@ -71,7 +70,6 @@ import com.cloud.event.UsageEventVO; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; @@ -120,7 +118,6 @@ import com.cloud.vm.ReservationContextImpl; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.InstanceGroupDao; import com.cloud.vm.dao.UserVmDao; diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 3393b389772..688ba7d2d83 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -114,6 +114,7 @@ DROP TABLE IF EXISTS `cloud`.`vpn_users`; DROP TABLE IF EXISTS `cloud`.`data_center_details`; DROP TABLE IF EXISTS `cloud`.`network_tags`; DROP TABLE IF EXISTS `cloud`.`op_host_transfer`; +DROP TABLE IF EXISTS `cloud`.`projects`; CREATE TABLE `cloud`.`version` ( `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id', @@ -1544,4 +1545,23 @@ CREATE TABLE `cloud`.`op_host_transfer` ( CONSTRAINT `fk_op_host_transfer__future_mgmt_server_id` FOREIGN KEY `fk_op_host_transfer__future_mgmt_server_id`(`future_mgmt_server_id`) REFERENCES `mshost`(`msid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `cloud`.`projects` ( + `id` bigint unsigned NOT NULL auto_increment, + `name` varchar(255) COMMENT 'project name', + `display_text` varchar(255) COMMENT 'project name', + `account_id` bigint unsigned, + `domain_id` bigint unsigned, + `data_center_id` bigint unsigned, + `created` datetime COMMENT 'date created', + `removed` datetime COMMENT 'date removed', + `cleanup_needed` tinyint(1) NOT NULL default '0', + PRIMARY KEY (`id`), + CONSTRAINT `fk_projects__data_center_id` FOREIGN KEY (`data_center_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_projects__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`), + CONSTRAINT `fk_projects__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, + INDEX `i_projects__removed`(`removed`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + SET foreign_key_checks = 1;