diff --git a/api/src/org/apache/cloudstack/affinity/AffinityGroupResponse.java b/api/src/org/apache/cloudstack/affinity/AffinityGroupResponse.java index aa0b4e6133a..22842b834fe 100644 --- a/api/src/org/apache/cloudstack/affinity/AffinityGroupResponse.java +++ b/api/src/org/apache/cloudstack/affinity/AffinityGroupResponse.java @@ -56,12 +56,20 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie @Param(description = "the domain name of the affinity group") private String domainName; + @SerializedName(ApiConstants.PROJECT_ID) + @Param(description = "the project ID of the affinity group") + private String projectId; + + @SerializedName(ApiConstants.PROJECT) + @Param(description = "the project name of the affinity group") + private String projectName; + @SerializedName(ApiConstants.TYPE) @Param(description = "the type of the affinity group") private String type; @SerializedName("virtualmachineIds") - @Param(description = "virtual machine Ids associated with this affinity group ") + @Param(description = "virtual machine IDs associated with this affinity group") private List vmIdList; public AffinityGroupResponse() { @@ -134,14 +142,12 @@ public class AffinityGroupResponse extends BaseResponse implements ControlledVie @Override public void setProjectId(String projectId) { - // TODO Auto-generated method stub - + this.projectId = projectId; } @Override public void setProjectName(String projectName) { - // TODO Auto-generated method stub - + this.projectName = projectName; } public void setVMIdList(List vmIdList) { diff --git a/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java b/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java index 8ffc3b29612..018e5f5bab5 100644 --- a/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java +++ b/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java @@ -18,47 +18,36 @@ package org.apache.cloudstack.affinity; import java.util.List; +import org.apache.cloudstack.api.command.user.affinitygroup.CreateAffinityGroupCmd; + import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; public interface AffinityGroupService { /** * Creates an affinity/anti-affinity group for the given account/domain. * - * @param account + * @param accountName + * @param projectId * @param domainId - * @param name - * @param type + * @param affinityGroupName + * @param affinityGroupType * @param description * @return AffinityGroup */ + AffinityGroup createAffinityGroup(String accountName, Long projectId, Long domainId, String affinityGroupName, String affinityGroupType, String description); - AffinityGroup createAffinityGroup(String account, Long domainId, String affinityGroupName, String affinityGroupType, String description); + AffinityGroup createAffinityGroup(CreateAffinityGroupCmd createAffinityGroupCmd); /** * Creates an affinity/anti-affinity group. * * @param affinityGroupId - * @param account + * @param accountName * @param domainId * @param affinityGroupName */ - boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName); - - /** Lists Affinity Groups in your account - * @param account - * @param domainId - * @param affinityGroupId - * @param affinityGroupName - * @param affinityGroupType - * @param vmId - * @param startIndex - * @param pageSize - * @return - */ - Pair, Integer> listAffinityGroups(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, Long startIndex, - Long pageSize); + boolean deleteAffinityGroup(Long affinityGroupId, String accountName, Long projectId, Long domainId, String affinityGroupName); /** * List group types available in deployment @@ -77,6 +66,5 @@ public interface AffinityGroupService { boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId); - AffinityGroup createAffinityGroupInternal(String account, Long domainId, String affinityGroupName, String affinityGroupType, String description); } diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java index 96d8bec96d8..8cc3d69bb6b 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/CreateAffinityGroupCmd.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.api.command.user.affinitygroup; import org.apache.log4j.Logger; - import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.APICommand; @@ -28,6 +27,7 @@ import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; @@ -54,6 +54,12 @@ public class CreateAffinityGroupCmd extends BaseAsyncCreateCmd { entityType = DomainResponse.class) private Long domainId; + @Parameter(name = ApiConstants.PROJECT_ID, + type = CommandType.UUID, + entityType = ProjectResponse.class, + description = "create affinity group for project") + private Long projectId; + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "optional description of the affinity group") private String description; @@ -90,6 +96,10 @@ public class CreateAffinityGroupCmd extends BaseAsyncCreateCmd { return affinityGroupType; } + public Long getProjectId() { + return projectId; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -101,23 +111,17 @@ public class CreateAffinityGroupCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - Account account = CallContext.current().getCallingAccount(); - if ((account == null) || _accountService.isAdmin(account.getId())) { - if ((domainId != null) && (accountName != null)) { - Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); - if (userAccount != null) { - return userAccount.getId(); - } - } - } + Account caller = CallContext.current().getCallingAccount(); - if (account != null) { - return account.getId(); + //For domain wide affinity groups (if the affinity group processor type allows it) + if(projectId == null && domainId != null && accountName == null && _accountService.isRootAdmin(caller.getId())){ + return Account.ACCOUNT_ID_SYSTEM; } - - return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this - // command to SYSTEM so ERROR events - // are tracked + Account owner = _accountService.finalizeOwner(caller, accountName, domainId, projectId); + if(owner == null){ + return caller.getAccountId(); + } + return owner.getAccountId(); } @Override @@ -134,7 +138,7 @@ public class CreateAffinityGroupCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { - AffinityGroup result = _affinityGroupService.createAffinityGroup(accountName, domainId, affinityGroupName, affinityGroupType, description); + AffinityGroup result = _affinityGroupService.createAffinityGroup(this); if (result != null) { setEntityId(result.getId()); setEntityUuid(result.getUuid()); diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java index 4eeff06a00f..e8884b4f65c 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java @@ -17,8 +17,8 @@ package org.apache.cloudstack.api.command.user.affinitygroup; +import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -35,7 +35,6 @@ import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.user.Account; @APICommand(name = "deleteAffinityGroup", description = "Deletes affinity group", responseObject = SuccessResponse.class, entityType = {AffinityGroup.class}, @@ -67,6 +66,9 @@ public class DeleteAffinityGroupCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the affinity group. Mutually exclusive with ID parameter") private String name; + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, description = "the project of the affinity group", entityType = ProjectResponse.class) + private Long projectId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -79,22 +81,11 @@ public class DeleteAffinityGroupCmd extends BaseAsyncCmd { return domainId; } + public Long getProjectId() { + return projectId; + } + public Long getId() { - if (id != null && name != null) { - throw new InvalidParameterValueException("name and id parameters are mutually exclusive"); - } - - if (name != null) { - id = _responseGenerator.getAffinityGroupId(name, getEntityOwnerId()); - if (id == null) { - throw new InvalidParameterValueException("Unable to find affinity group by name " + name + " for the account ID=" + getEntityOwnerId()); - } - } - - if (id == null) { - throw new InvalidParameterValueException("Either ID or name parameter is required by deleteAffinityGroup command"); - } - return id; } @@ -109,29 +100,22 @@ public class DeleteAffinityGroupCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Account account = CallContext.current().getCallingAccount(); - if ((account == null) || _accountService.isAdmin(account.getId())) { - if ((domainId != null) && (accountName != null)) { - Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); - if (userAccount != null) { - return userAccount.getId(); - } - } + Account caller = CallContext.current().getCallingAccount(); + + //For domain wide affinity groups (if the affinity group processor type allows it) + if(projectId == null && domainId != null && accountName == null && _accountService.isRootAdmin(caller.getId())){ + return Account.ACCOUNT_ID_SYSTEM; } - - if (account != null) { - return account.getId(); + Account owner = _accountService.finalizeOwner(caller, accountName, domainId, projectId); + if(owner == null){ + return caller.getAccountId(); } - - return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this - // command to SYSTEM so ERROR events - // are tracked - + return owner.getAccountId(); } @Override public void execute() { - boolean result = _affinityGroupService.deleteAffinityGroup(id, accountName, domainId, name); + boolean result = _affinityGroupService.deleteAffinityGroup(id, accountName, projectId, domainId, name); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java index 41ed6fb01c7..fa7935b0b66 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java @@ -23,14 +23,14 @@ import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseListAccountResourcesCmd; +import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserVmResponse; @APICommand(name = "listAffinityGroups", description = "Lists affinity groups", responseObject = AffinityGroupResponse.class, entityType = {AffinityGroup.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ListAffinityGroupsCmd extends BaseListAccountResourcesCmd { +public class ListAffinityGroupsCmd extends BaseListProjectAndAccountResourcesCmd { public static final Logger s_logger = Logger.getLogger(ListAffinityGroupsCmd.class.getName()); private static final String s_name = "listaffinitygroupsresponse"; @@ -69,6 +69,10 @@ public class ListAffinityGroupsCmd extends BaseListAccountResourcesCmd { return id; } + public String getAffinityGroupType() { + return affinityGroupType; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -80,13 +84,9 @@ public class ListAffinityGroupsCmd extends BaseListAccountResourcesCmd { @Override public void execute() { - - ListResponse response = _queryService.listAffinityGroups(id, affinityGroupName, - affinityGroupType, virtualMachineId, getAccountName(), getDomainId(), isRecursive(), - listAll(), getStartIndex(), getPageSizeVal(), getKeyword()); + ListResponse response = _queryService.searchForAffinityGroups(this); response.setResponseName(getCommandName()); setResponseObject(response); - } @Override diff --git a/api/src/org/apache/cloudstack/query/QueryService.java b/api/src/org/apache/cloudstack/query/QueryService.java index 1a5ac11b299..de9fbb55a26 100644 --- a/api/src/org/apache/cloudstack/query/QueryService.java +++ b/api/src/org/apache/cloudstack/query/QueryService.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.api.command.admin.storage.ListStorageTagsCmd; import org.apache.cloudstack.api.command.admin.user.ListUsersCmd; import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; +import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; @@ -131,9 +132,7 @@ public interface QueryService { ListResponse listIsos(ListIsosCmd cmd); - ListResponse listAffinityGroups(Long affinityGroupId, String affinityGroupName, - String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive, - boolean listAll, Long startIndex, Long pageSize, String keyword); + ListResponse searchForAffinityGroups(ListAffinityGroupsCmd cmd); List listResourceDetails(ListResourceDetailsCmd cmd); @@ -142,5 +141,4 @@ public interface QueryService { ListResponse searchForStorageTags(ListStorageTagsCmd cmd); ListResponse searchForHostTags(ListHostTagsCmd cmd); - } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java index b1ac2f85337..829ed107022 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java @@ -192,7 +192,7 @@ public class VMEntityManagerImpl implements VMEntityManager { try { dest = _dpMgr.planDeployment(vmProfile, plan, exclude, plannerToUse); } catch (AffinityConflictException e) { - throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); + throw new CloudRuntimeException("Unable to create deployment, affinity rules associated to the VM conflict"); } if (dest != null) { diff --git a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java index 76a81440c70..010720ba33a 100644 --- a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java +++ b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDao.java @@ -18,7 +18,6 @@ package org.apache.cloudstack.affinity.dao; import java.util.List; -import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupVO; import com.cloud.utils.db.GenericDao; @@ -34,9 +33,9 @@ public interface AffinityGroupDao extends GenericDao { int removeByAccountId(long accountId); - AffinityGroup findDomainLevelGroupByName(Long domainId, String affinityGroupName); + AffinityGroupVO findDomainLevelGroupByName(Long domainId, String affinityGroupName); - AffinityGroup findByAccountAndType(Long accountId, String string); + AffinityGroupVO findByAccountAndType(Long accountId, String string); - AffinityGroup findDomainLevelGroupByType(Long domainId, String string); + AffinityGroupVO findDomainLevelGroupByType(Long domainId, String string); } diff --git a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java index d3f11633e00..3bd7c6d082b 100644 --- a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java +++ b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupDaoImpl.java @@ -22,7 +22,6 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import org.apache.cloudstack.affinity.AffinityGroupVO; @@ -134,7 +133,7 @@ public class AffinityGroupDaoImpl extends GenericDaoBase } @Override - public AffinityGroup findDomainLevelGroupByName(Long domainId, String affinityGroupName) { + public AffinityGroupVO findDomainLevelGroupByName(Long domainId, String affinityGroupName) { SearchCriteria sc = DomainLevelNameSearch.create(); sc.setParameters("aclType", ControlledEntity.ACLType.Domain); sc.setParameters("name", affinityGroupName); @@ -143,7 +142,7 @@ public class AffinityGroupDaoImpl extends GenericDaoBase } @Override - public AffinityGroup findByAccountAndType(Long accountId, String type) { + public AffinityGroupVO findByAccountAndType(Long accountId, String type) { SearchCriteria sc = AccountIdTypeSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("type", type); @@ -152,7 +151,7 @@ public class AffinityGroupDaoImpl extends GenericDaoBase } @Override - public AffinityGroup findDomainLevelGroupByType(Long domainId, String type) { + public AffinityGroupVO findDomainLevelGroupByType(Long domainId, String type) { SearchCriteria sc = DomainLevelTypeSearch.create(); sc.setParameters("aclType", ControlledEntity.ACLType.Domain); sc.setParameters("type", type); diff --git a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java index 1e584c38bd6..35bcfc6ae44 100644 --- a/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java +++ b/plugins/dedicated-resources/src/org/apache/cloudstack/dedicated/DedicatedResourceManagerImpl.java @@ -639,7 +639,7 @@ public class DedicatedResourceManagerImpl implements DedicatedService { affinityGroupName = "DedicatedGrp-domain-" + domainName; } - group = _affinityGroupService.createAffinityGroupInternal(accountName, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group"); + group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group"); return group; @@ -948,7 +948,7 @@ public class DedicatedResourceManagerImpl implements DedicatedService { List resourcesInGroup = _dedicatedDao.listByAffinityGroupId(resource.getAffinityGroupId()); if (resourcesInGroup.isEmpty()) { // delete the group - _affinityGroupService.deleteAffinityGroup(resource.getAffinityGroupId(), null, null, null); + _affinityGroupService.deleteAffinityGroup(resource.getAffinityGroupId(), null, null, null, null); } } diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java index 954a37e1fe6..77abb65b1dd 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/IntegrationTestConfiguration.java @@ -389,7 +389,7 @@ public class IntegrationTestConfiguration { } }); Mockito.when( - mock.createAffinityGroupInternal(Matchers.any(String.class), Matchers.any(Long.class), Matchers.any(String.class), Matchers.any(String.class), + mock.createAffinityGroup(Matchers.any(String.class), Matchers.any(Long.class), Matchers.any(Long.class), Matchers.any(String.class), Matchers.any(String.class), Matchers.any(String.class))).thenReturn(gmock); } catch (Exception e) { e.printStackTrace(); diff --git a/server/src/com/cloud/acl/AffinityGroupAccessChecker.java b/server/src/com/cloud/acl/AffinityGroupAccessChecker.java index 6e5e55b85ec..1e9097327c6 100644 --- a/server/src/com/cloud/acl/AffinityGroupAccessChecker.java +++ b/server/src/com/cloud/acl/AffinityGroupAccessChecker.java @@ -18,16 +18,18 @@ package com.cloud.acl; import javax.inject.Inject; -import org.springframework.stereotype.Component; - import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; +import org.springframework.stereotype.Component; import com.cloud.domain.DomainVO; import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.ProjectVO; +import com.cloud.projects.dao.ProjectAccountDao; +import com.cloud.projects.dao.ProjectDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.utils.exception.CloudRuntimeException; @@ -41,6 +43,10 @@ public class AffinityGroupAccessChecker extends DomainChecker { AccountManager _accountMgr; @Inject AffinityGroupDomainMapDao _affinityGroupDomainMapDao; + @Inject + ProjectDao _projectDao; + @Inject + ProjectAccountDao _projectAccountDao; @Override public boolean checkAccess(Account caller, ControlledEntity entity, AccessType accessType) throws PermissionDeniedException { @@ -48,8 +54,7 @@ public class AffinityGroupAccessChecker extends DomainChecker { AffinityGroup group = (AffinityGroup)entity; if (_affinityGroupService.isAdminControlledGroup(group)) { - if (accessType != null && accessType == AccessType.OperateEntry - && !_accountMgr.isRootAdmin(caller.getId())) { + if (accessType == AccessType.OperateEntry && !_accountMgr.isRootAdmin(caller.getId())) { throw new PermissionDeniedException(caller + " does not have permission to operate with resource " + entity); } @@ -69,6 +74,15 @@ public class AffinityGroupAccessChecker extends DomainChecker { } else { //acl_type account if (caller.getId() != group.getAccountId()) { + //check if the group belongs to a project + ProjectVO project = _projectDao.findByProjectAccountId(group.getAccountId()); + if (project != null) { + if (AccessType.ModifyProject.equals(accessType) && _projectAccountDao.canModifyProjectAccount(caller.getId(), group.getAccountId())) { + return true; + } else if (!AccessType.ModifyProject.equals(accessType) && _projectAccountDao.canAccessProjectAccount(caller.getId(), group.getAccountId())) { + return true; + } + } throw new PermissionDeniedException(caller + " does not have permission to operate with resource " + entity); } else { return true; diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 009518194c9..0c72710bd02 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -25,9 +25,6 @@ import java.util.Set; import javax.inject.Inject; -import com.cloud.utils.StringUtils; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -56,6 +53,7 @@ import org.apache.cloudstack.api.command.admin.volume.ListVolumesCmdByAdmin; import org.apache.cloudstack.api.command.admin.zone.ListZonesCmdByAdmin; import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; +import org.apache.cloudstack.api.command.user.affinitygroup.ListAffinityGroupsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; @@ -195,7 +193,6 @@ import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.dao.VMTemplateDao; -import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate.State; @@ -206,6 +203,7 @@ import com.cloud.user.DomainManager; import com.cloud.user.dao.AccountDao; import com.cloud.utils.DateUtil; import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.Filter; @@ -216,17 +214,20 @@ import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.DomainRouterDao; -import com.cloud.vm.dao.NicDetailsDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDao; @Component public class QueryManagerImpl extends ManagerBase implements QueryService, Configurable { public static final Logger s_logger = Logger.getLogger(QueryManagerImpl.class); + private static final String ID_FIELD = "id"; + @Inject private AccountManager _accountMgr; @@ -329,12 +330,6 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi @Inject private DomainRouterDao _routerDao; - @Inject - private VolumeDetailsDao _volumeDetailDao; - - @Inject - private NicDetailsDao _nicDetailDao; - @Inject UserVmDetailsDao _userVmDetailDao; @@ -373,11 +368,11 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi @Inject AffinityGroupDomainMapDao _affinityGroupDomainMapDao; - @Inject NetworkDetailsDao _networkDetailsDao; @Inject ResourceTagDao _resourceTagDao; + @Inject DataStoreManager dataStoreManager; @@ -3393,55 +3388,60 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO); } - @Override - public ListResponse listAffinityGroups(Long affinityGroupId, String affinityGroupName, - String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive, - boolean listAll, Long startIndex, Long pageSize, String keyword) { - Pair, Integer> result = listAffinityGroupsInternal(affinityGroupId, - affinityGroupName, affinityGroupType, vmId, accountName, domainId, isRecursive, listAll, startIndex, - pageSize, keyword); + public ListResponse searchForAffinityGroups(ListAffinityGroupsCmd cmd) { + Pair, Integer> result = searchForAffinityGroupsInternal(cmd); ListResponse response = new ListResponse(); List agResponses = ViewResponseHelper.createAffinityGroupResponses(result.first()); response.setResponses(agResponses, result.second()); return response; } - public Pair, Integer> listAffinityGroupsInternal(Long affinityGroupId, - String affinityGroupName, String affinityGroupType, Long vmId, String accountName, Long domainId, - boolean isRecursive, boolean listAll, Long startIndex, Long pageSize, String keyword) { + public Pair, Integer> searchForAffinityGroupsInternal(ListAffinityGroupsCmd cmd) { + + final Long affinityGroupId = cmd.getId(); + final String affinityGroupName = cmd.getAffinityGroupName(); + final String affinityGroupType = cmd.getAffinityGroupType(); + final Long vmId = cmd.getVirtualMachineId(); + final String accountName = cmd.getAccountName(); + Long domainId = cmd.getDomainId(); + final Long projectId = cmd.getProjectId(); + Boolean isRecursive = cmd.isRecursive(); + final Boolean listAll = cmd.listAll(); + final Long startIndex = cmd.getStartIndex(); + final Long pageSize = cmd.getPageSizeVal(); + final String keyword = cmd.getKeyword(); Account caller = CallContext.current().getCallingAccount(); - caller.getAccountId(); - if (vmId != null) { UserVmVO userVM = _userVmDao.findById(vmId); if (userVM == null) { - throw new InvalidParameterValueException("Unable to list affinity groups for virtual machine instance " - + vmId + "; instance not found."); + throw new InvalidParameterValueException("Unable to list affinity groups for virtual machine instance " + vmId + "; instance not found."); } _accountMgr.checkAccess(caller, null, true, userVM); return listAffinityGroupsByVM(vmId.longValue(), startIndex, pageSize); } List permittedAccounts = new ArrayList(); - Ternary domainIdRecursiveListProject = new Ternary( - domainId, isRecursive, null); - _accountMgr.buildACLSearchParameters(caller, affinityGroupId, accountName, null, permittedAccounts, - domainIdRecursiveListProject, listAll, true); - domainId = domainIdRecursiveListProject.first(); - isRecursive = domainIdRecursiveListProject.second(); - ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + Ternary ternary = new Ternary(domainId, isRecursive, null); - Filter searchFilter = new Filter(AffinityGroupJoinVO.class, "id", true, startIndex, pageSize); - SearchCriteria sc = buildAffinityGroupSearchCriteria(domainId, isRecursive, - permittedAccounts, listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); + _accountMgr.buildACLSearchParameters(caller, affinityGroupId, accountName, projectId, permittedAccounts, ternary, listAll, false); + + domainId = ternary.first(); + isRecursive = ternary.second(); + ListProjectResourcesCriteria listProjectResourcesCriteria = ternary.third(); + + Filter searchFilter = new Filter(AffinityGroupJoinVO.class, ID_FIELD, true, startIndex, pageSize); + + SearchCriteria sc = buildAffinityGroupSearchCriteria(domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria, affinityGroupId, + affinityGroupName, affinityGroupType, keyword); + + Pair, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, searchFilter); - Pair, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, - searchFilter); // search group details by ids - List vrs = new ArrayList(); + List affinityGroups = new ArrayList(); + Integer count = uniqueGroupsPair.second(); if (count.intValue() != 0) { List uniqueGroups = uniqueGroupsPair.first(); @@ -3450,36 +3450,33 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi for (AffinityGroupJoinVO v : uniqueGroups) { vrIds[i++] = v.getId(); } - vrs = _affinityGroupJoinDao.searchByIds(vrIds); + affinityGroups = _affinityGroupJoinDao.searchByIds(vrIds); } if (!permittedAccounts.isEmpty()) { // add domain level affinity groups if (domainId != null) { - SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, - new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, - affinityGroupType, keyword); - vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, + affinityGroupId, affinityGroupName, affinityGroupType, keyword); + affinityGroups.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); } else { for (Long permAcctId : permittedAccounts) { Account permittedAcct = _accountDao.findById(permAcctId); - SearchCriteria scDomain = buildAffinityGroupSearchCriteria( - null, isRecursive, new ArrayList(), - listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, + affinityGroupId, affinityGroupName, affinityGroupType, keyword); - vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, permittedAcct.getDomainId())); + affinityGroups.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, permittedAcct.getDomainId())); } } } else if (((permittedAccounts.isEmpty()) && (domainId != null) && isRecursive)) { // list all domain level affinity groups for the domain admin case - SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, - new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, - affinityGroupType, keyword); - vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, + affinityGroupId, affinityGroupName, affinityGroupType, keyword); + affinityGroups.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); } - return new Pair, Integer>(vrs, vrs.size()); + return new Pair, Integer>(affinityGroups, affinityGroups.size()); } @@ -3524,9 +3521,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi } } - private SearchCriteria buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive, - List permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria, - Long affinityGroupId, String affinityGroupName, String affinityGroupType, String keyword) { + private SearchCriteria buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive, List permittedAccounts, + ListProjectResourcesCriteria listProjectResourcesCriteria, Long affinityGroupId, String affinityGroupName, String affinityGroupType, String keyword) { SearchBuilder groupSearch = _affinityGroupJoinDao.createSearchBuilder(); buildAffinityGroupViewSearchBuilder(groupSearch, domainId, isRecursive, permittedAccounts, @@ -3536,8 +3532,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi // distinct SearchCriteria sc = groupSearch.create(); - buildAffinityGroupViewSearchCriteria(sc, domainId, isRecursive, permittedAccounts, - listProjectResourcesCriteria); + buildAffinityGroupViewSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (affinityGroupId != null) { sc.addAnd("id", SearchCriteria.Op.EQ, affinityGroupId); diff --git a/server/src/com/cloud/api/query/vo/AffinityGroupJoinVO.java b/server/src/com/cloud/api/query/vo/AffinityGroupJoinVO.java index bcd0fdcbb92..380ad6625e1 100644 --- a/server/src/com/cloud/api/query/vo/AffinityGroupJoinVO.java +++ b/server/src/com/cloud/api/query/vo/AffinityGroupJoinVO.java @@ -72,6 +72,15 @@ public class AffinityGroupJoinVO extends BaseViewVO implements ControlledViewEnt @Column(name = "domain_path") private String domainPath = null; + @Column(name = "project_id") + private long projectId; + + @Column(name = "project_uuid") + private String projectUuid; + + @Column(name = "project_name") + private String projectName; + @Column(name = "vm_id") private long vmId; @@ -153,6 +162,20 @@ public class AffinityGroupJoinVO extends BaseViewVO implements ControlledViewEnt return domainPath; } + public long getProjectId() { + return projectId; + } + + @Override + public String getProjectUuid() { + return projectUuid; + } + + @Override + public String getProjectName() { + return projectName; + } + public String getDescription() { return description; } @@ -177,18 +200,6 @@ public class AffinityGroupJoinVO extends BaseViewVO implements ControlledViewEnt return vmState; } - @Override - public String getProjectUuid() { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getProjectName() { - // TODO Auto-generated method stub - return null; - } - public ControlledEntity.ACLType getAclType() { return aclType; } diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 566f46e91b4..1c74f824841 100644 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -1495,7 +1495,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final List resourcesInGroup = _dedicatedDao.listByAffinityGroupId(dr.getAffinityGroupId()); if (resourcesInGroup.isEmpty()) { // delete the group - _affinityGroupService.deleteAffinityGroup(dr.getAffinityGroupId(), null, null, null); + _affinityGroupService.deleteAffinityGroup(dr.getAffinityGroupId(), null, null, null, null); } } } @@ -1713,7 +1713,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati final List resourcesInGroup = _dedicatedDao.listByAffinityGroupId(resource.getAffinityGroupId()); if (resourcesInGroup.isEmpty()) { // delete the group - _affinityGroupService.deleteAffinityGroup(resource.getAffinityGroupId(), null, null, null); + _affinityGroupService.deleteAffinityGroup(resource.getAffinityGroupId(), null, null, null, null); } } } @@ -1811,7 +1811,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - group = _affinityGroupService.createAffinityGroupInternal(accountName, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group"); + group = _affinityGroupService.createAffinityGroup(accountName, null, domainId, affinityGroupName, "ExplicitDedication", "dedicated resources group"); return group; diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index da097378e16..a0e661bac9b 100644 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -2404,98 +2404,10 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M } } -// @Override -// public void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List -// permittedAccounts, Ternary domainIdRecursiveListProject, -// boolean listAll, boolean forProjectInvitation) { -// Long domainId = domainIdRecursiveListProject.first(); -// if (domainId != null) { -// Domain domain = _domainDao.findById(domainId); -// if (domain == null) { -// throw new InvalidParameterValueException("Unable to find domain by id " + domainId); -// } -// // check permissions -// checkAccess(caller, domain); -// } -// -// if (accountName != null) { -// if (projectId != null) { -// throw new InvalidParameterValueException("Account and projectId can't be specified together"); -// } -// -// Account userAccount = null; -// Domain domain = null; -// if (domainId != null) { -// userAccount = _accountDao.findActiveAccount(accountName, domainId); -// domain = _domainDao.findById(domainId); -// } else { -// userAccount = _accountDao.findActiveAccount(accountName, caller.getDomainId()); -// domain = _domainDao.findById(caller.getDomainId()); -// } -// -// if (userAccount != null) { -// checkAccess(caller, null, false, userAccount); -// //check permissions -// permittedAccounts.add(userAccount.getId()); -// } else { -// throw new InvalidParameterValueException("could not find account " + accountName + " in domain " + domain.getUuid()); -// } -// } -// -// // set project information -// if (projectId != null) { -// if (!forProjectInvitation) { -// if (projectId.longValue() == -1) { -// if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { -// permittedAccounts.addAll(_projectMgr.listPermittedProjectAccounts(caller.getId())); -// } else { -// domainIdRecursiveListProject.third(Project.ListProjectResourcesCriteria.ListProjectResourcesOnly); -// } -// } else { -// 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 PermissionDeniedException("Account " + caller + " can't access project id=" + projectId); -// } -// permittedAccounts.add(project.getProjectAccountId()); -// } -// } -// } else { -// if (id == null) { -// domainIdRecursiveListProject.third(Project.ListProjectResourcesCriteria.SkipProjectResources); -// } -// if (permittedAccounts.isEmpty() && domainId == null) { -// if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { -// permittedAccounts.add(caller.getId()); -// } else if (!listAll) { -// if (id == null) { -// permittedAccounts.add(caller.getId()); -// } else if (!isRootAdmin(caller.getId())) { -// domainIdRecursiveListProject.first(caller.getDomainId()); -// domainIdRecursiveListProject.second(true); -// } -// } else if (domainId == null) { -// if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) { -// domainIdRecursiveListProject.first(caller.getDomainId()); -// domainIdRecursiveListProject.second(true); -// } -// } -// } else if (domainId != null) { -// if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) { -// permittedAccounts.add(caller.getId()); -// } -// } -// -// } -// } - //TODO: deprecate this to use the new buildACLSearchParameters with permittedDomains, permittedAccounts, and permittedResources as return @Override - public void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List - permittedAccounts, Ternary domainIdRecursiveListProject, - boolean listAll, boolean forProjectInvitation) { + public void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List permittedAccounts, + Ternary domainIdRecursiveListProject, boolean listAll, boolean forProjectInvitation) { Long domainId = domainIdRecursiveListProject.first(); if (domainId != null) { Domain domain = _domainDao.findById(domainId); diff --git a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java index 85658e5d7d4..f2502442c18 100644 --- a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java +++ b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java @@ -26,8 +26,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.utils.fsm.StateMachine2; -import org.apache.log4j.Logger; +import org.apache.log4j.Logger; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -35,6 +35,7 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.command.user.affinitygroup.CreateAffinityGroupCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; @@ -54,8 +55,6 @@ import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.EntityManager; -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.Transaction; @@ -111,48 +110,29 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro @DB @Override @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_CREATE, eventDescription = "Creating Affinity Group", create = true) - public AffinityGroup createAffinityGroup(String account, Long domainId, String affinityGroupName, String affinityGroupType, String description) { + public AffinityGroup createAffinityGroup(CreateAffinityGroupCmd createAffinityGroupCmd) { + return createAffinityGroup(createAffinityGroupCmd.getAccountName(), createAffinityGroupCmd.getProjectId(), createAffinityGroupCmd.getDomainId(), createAffinityGroupCmd.getAffinityGroupName(), createAffinityGroupCmd.getAffinityGroupType(), createAffinityGroupCmd.getDescription()); + } - Account caller = CallContext.current().getCallingAccount(); + @DB + @Override + public AffinityGroup createAffinityGroup(final String accountName, final Long projectId, final Long domainId, final String affinityGroupName, final String affinityGroupType, + final String description) { - //validate the affinityGroupType + // validate the affinityGroupType Map typeProcessorMap = getAffinityTypeToProcessorMap(); - if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { - if (!typeProcessorMap.containsKey(affinityGroupType)) { - throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" + affinityGroupType); - } - } else { + + if (typeProcessorMap == null || typeProcessorMap.isEmpty()) { throw new InvalidParameterValueException("Unable to create affinity group, no Affinity Group Types configured"); } AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); - if (processor.isAdminControlledGroup()) { - throw new PermissionDeniedException("Cannot create the affinity group"); + if(processor == null){ + throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" + affinityGroupType); } - return createAffinityGroupInternal(account, domainId, affinityGroupName, affinityGroupType, description); - } - - @DB - @Override - public AffinityGroup createAffinityGroupInternal(String account, final Long domainId, final String affinityGroupName, final String affinityGroupType, - final String description) { - Account caller = CallContext.current().getCallingAccount(); - - // validate the affinityGroupType - Map typeProcessorMap = getAffinityTypeToProcessorMap(); - if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { - if (!typeProcessorMap.containsKey(affinityGroupType)) { - throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" + affinityGroupType); - } - } else { - throw new InvalidParameterValueException("Unable to create affinity group, no Affinity Group Types configured"); - } - - final AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); - if (processor.isAdminControlledGroup() && !_accountMgr.isRootAdmin(caller.getId())) { throw new PermissionDeniedException("Cannot create the affinity group"); } @@ -161,72 +141,24 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro Account owner = null; boolean domainLevel = false; - if (account != null && domainId != null) { - - owner = _accountMgr.finalizeOwner(caller, account, domainId, null); - aclType = ControlledEntity.ACLType.Account; - - } else if (domainId != null && account == null) { - - if (!_accountMgr.isRootAdmin(caller.getId())) { - // non root admin need to pass both account and domain - throw new InvalidParameterValueException("Unable to create affinity group, account name must be passed with the domainId"); - } else if (!processor.canBeSharedDomainWide()) { - // cannot be domain level - throw new InvalidParameterValueException("Unable to create affinity group, account name is needed"); - } - - DomainVO domain = _domainDao.findById(domainId); - if (domain == null) { - throw new InvalidParameterValueException("Unable to find domain by specified id"); - } + if (projectId == null && domainId != null && accountName == null) { + verifyAccessToDomainWideProcessor(caller, processor); + DomainVO domain = getDomain(domainId); _accountMgr.checkAccess(caller, domain); // domain level group, owner is SYSTEM. owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); aclType = ControlledEntity.ACLType.Domain; domainLevel = true; - } else { - owner = caller; + owner = _accountMgr.finalizeOwner(caller, accountName, domainId, projectId); aclType = ControlledEntity.ACLType.Account; } - if (_affinityGroupDao.isNameInUse(owner.getAccountId(), owner.getDomainId(), affinityGroupName)) { - throw new InvalidParameterValueException("Unable to create affinity group, a group with name " + affinityGroupName + " already exists."); - } - if (domainLevel && _affinityGroupDao.findDomainLevelGroupByName(domainId, affinityGroupName) != null) { - throw new InvalidParameterValueException("Unable to create affinity group, a group with name " + affinityGroupName + " already exists under the domain."); - } + verifyAffinityGroupNameInUse(owner.getAccountId(), owner.getDomainId(), affinityGroupName); + verifyDomainLevelAffinityGroupName(domainLevel, owner.getDomainId(), affinityGroupName); - final Account ownerFinal = owner; - final ControlledEntity.ACLType aclTypeFinal = aclType; - AffinityGroupVO group = Transaction.execute(new TransactionCallback() { - @Override - public AffinityGroupVO doInTransaction(TransactionStatus status) { - AffinityGroupVO group = - new AffinityGroupVO(affinityGroupName, affinityGroupType, description, ownerFinal.getDomainId(), ownerFinal.getId(), aclTypeFinal); - _affinityGroupDao.persist(group); - - if (domainId != null && aclTypeFinal == ACLType.Domain) { - boolean subDomainAccess = false; - subDomainAccess = processor.subDomainAccess(); - AffinityGroupDomainMapVO domainMap = new AffinityGroupDomainMapVO(group.getId(), domainId, - subDomainAccess); - _affinityGroupDomainMapDao.persist(domainMap); - //send event for storing the domain wide resource access - Map params = new HashMap(); - params.put(ApiConstants.ENTITY_TYPE, AffinityGroup.class); - params.put(ApiConstants.ENTITY_ID, group.getId()); - params.put(ApiConstants.DOMAIN_ID, domainId); - params.put(ApiConstants.SUBDOMAIN_ACCESS, subDomainAccess); - _messageBus.publish(_name, EntityManager.MESSAGE_ADD_DOMAIN_WIDE_ENTITY_EVENT, PublishScope.LOCAL, - params); - } - - return group; - } - }); + AffinityGroupVO group = createAffinityGroup(processor, owner, aclType, affinityGroupName, affinityGroupType, description); if (s_logger.isDebugEnabled()) { s_logger.debug("Created affinity group =" + affinityGroupName); @@ -235,51 +167,135 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro return group; } + private void verifyAccessToDomainWideProcessor(Account caller, AffinityGroupProcessor processor) { + if (!_accountMgr.isRootAdmin(caller.getId())) { + throw new InvalidParameterValueException("Unable to create affinity group, account name must be passed with the domainId"); + } + if (!processor.canBeSharedDomainWide()) { + throw new InvalidParameterValueException("Unable to create affinity group, account name is needed. Affinity group type "+ processor.getType() +" cannot be shared domain wide"); + } + } + + private AffinityGroupVO createAffinityGroup(final AffinityGroupProcessor processor, final Account owner, final ACLType aclType, final String affinityGroupName, final String affinityGroupType, final String description) { + return Transaction.execute(new TransactionCallback() { + @Override + public AffinityGroupVO doInTransaction(TransactionStatus status) { + AffinityGroupVO group = + new AffinityGroupVO(affinityGroupName, affinityGroupType, description, owner.getDomainId(), owner.getId(), aclType); + _affinityGroupDao.persist(group); + + if (aclType == ACLType.Domain) { + boolean subDomainAccess = false; + subDomainAccess = processor.subDomainAccess(); + AffinityGroupDomainMapVO domainMap = new AffinityGroupDomainMapVO(group.getId(), owner.getDomainId(), + subDomainAccess); + _affinityGroupDomainMapDao.persist(domainMap); + //send event for storing the domain wide resource access + Map params = new HashMap(); + params.put(ApiConstants.ENTITY_TYPE, AffinityGroup.class); + params.put(ApiConstants.ENTITY_ID, group.getId()); + params.put(ApiConstants.DOMAIN_ID, owner.getDomainId()); + params.put(ApiConstants.SUBDOMAIN_ACCESS, subDomainAccess); + _messageBus.publish(_name, EntityManager.MESSAGE_ADD_DOMAIN_WIDE_ENTITY_EVENT, PublishScope.LOCAL, + params); + } + + return group; + } + }); + } + + private DomainVO getDomain(Long domainId) { + DomainVO domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Unable to find domain by specified id"); + } + return domain; + } + + private void verifyAffinityGroupNameInUse(long accountId, long domainId, String affinityGroupName) { + if (_affinityGroupDao.isNameInUse(accountId, domainId, affinityGroupName)) { + throw new InvalidParameterValueException("Unable to create affinity group, a group with name " + affinityGroupName + " already exists."); + } + } + + private void verifyDomainLevelAffinityGroupName(boolean domainLevel, long domainId, String affinityGroupName) { + if (domainLevel && _affinityGroupDao.findDomainLevelGroupByName(domainId, affinityGroupName) != null) { + throw new InvalidParameterValueException("Unable to create affinity group, a group with name " + affinityGroupName + " already exists under the domain."); + } + } + @DB - @Override @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_DELETE, eventDescription = "Deleting affinity group") - public boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName) { + public boolean deleteAffinityGroup(Long affinityGroupId, String account, Long projectId, Long domainId, String affinityGroupName) { + AffinityGroupVO group = getAffinityGroup(affinityGroupId, account, projectId, domainId, affinityGroupName); + + // check permissions Account caller = CallContext.current().getCallingAccount(); - Account owner = _accountMgr.finalizeOwner(caller, account, domainId, null); + _accountMgr.checkAccess(caller, AccessType.OperateEntry, true, group); + final Long affinityGroupIdFinal = group.getId(); + deleteAffinityGroup(affinityGroupIdFinal); + + // remove its related ACL permission + Pair, Long> params = new Pair, Long>(AffinityGroup.class, affinityGroupIdFinal); + _messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, params); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Deleted affinity group id=" + affinityGroupIdFinal); + } + return true; + } + + private AffinityGroupVO getAffinityGroup(Long affinityGroupId, String account, Long projectId, Long domainId, String affinityGroupName) { AffinityGroupVO group = null; if (affinityGroupId != null) { group = _affinityGroupDao.findById(affinityGroupId); - if (group == null) { - throw new InvalidParameterValueException("Unable to find affinity group: " + affinityGroupId + "; failed to delete group."); - } } else if (affinityGroupName != null) { - group = _affinityGroupDao.findByAccountAndName(owner.getAccountId(), affinityGroupName); - if (group == null) { - throw new InvalidParameterValueException("Unable to find affinity group: " + affinityGroupName + "; failed to delete group."); - } + group = getAffinityGroupByName(account, projectId, domainId, affinityGroupName); } else { throw new InvalidParameterValueException("Either the affinity group Id or group name must be specified to delete the group"); } - if (affinityGroupId == null) { - affinityGroupId = group.getId(); + if (group == null) { + throw new InvalidParameterValueException("Unable to find affinity group " + (affinityGroupId == null ? affinityGroupName : affinityGroupId)); } - // check permissions - _accountMgr.checkAccess(caller, AccessType.OperateEntry, true, group); + return group; + } - final Long affinityGroupIdFinal = affinityGroupId; + private AffinityGroupVO getAffinityGroupByName(String account, Long projectId, Long domainId, String affinityGroupName) { + AffinityGroupVO group = null; + if(account == null && domainId != null){ + group = _affinityGroupDao.findDomainLevelGroupByName(domainId, affinityGroupName); + }else{ + Long accountId = _accountMgr.finalyzeAccountId(account, domainId, projectId, true); + if(accountId == null){ + Account caller = CallContext.current().getCallingAccount(); + group = _affinityGroupDao.findByAccountAndName(caller.getAccountId(), affinityGroupName); + }else{ + group = _affinityGroupDao.findByAccountAndName(accountId, affinityGroupName); + } + } + return group; + } + + private void deleteAffinityGroup(final Long affinityGroupId) { Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - AffinityGroupVO group = _affinityGroupDao.lockRow(affinityGroupIdFinal, true); + AffinityGroupVO group = _affinityGroupDao.lockRow(affinityGroupId, true); if (group == null) { - throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupIdFinal); + throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupId); } - List affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupIdFinal); + List affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupId); if (!affinityGroupVmMap.isEmpty()) { SearchBuilder listByAffinityGroup = _affinityGroupVMMapDao.createSearchBuilder(); listByAffinityGroup.and("affinityGroupId", listByAffinityGroup.entity().getAffinityGroupId(), SearchCriteria.Op.EQ); listByAffinityGroup.done(); SearchCriteria sc = listByAffinityGroup.create(); - sc.setParameters("affinityGroupId", affinityGroupIdFinal); + sc.setParameters("affinityGroupId", affinityGroupId); _affinityGroupVMMapDao.lockRows(sc, null, true); _affinityGroupVMMapDao.remove(sc); @@ -291,77 +307,15 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro processor.handleDeleteGroup(group); } - if(_affinityGroupDao.expunge(affinityGroupIdFinal)){ + if(_affinityGroupDao.expunge(affinityGroupId)){ AffinityGroupDomainMapVO groupDomain = _affinityGroupDomainMapDao - .findByAffinityGroup(affinityGroupIdFinal); + .findByAffinityGroup(affinityGroupId); if (groupDomain != null) { _affinityGroupDomainMapDao.remove(groupDomain.getId()); } } } }); - - // remove its related ACL permission - Pair, Long> params = new Pair, Long>(AffinityGroup.class, affinityGroupIdFinal); - _messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, params); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Deleted affinity group id=" + affinityGroupId); - } - return true; - } - - @Override - public Pair, Integer> listAffinityGroups(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, - Long startIndex, Long pageSize) { - Filter searchFilter = new Filter(AffinityGroupVO.class, "id", Boolean.TRUE, startIndex, pageSize); - - Account caller = CallContext.current().getCallingAccount(); - - Long accountId = caller.getAccountId(); - Long domainId = caller.getDomainId(); - - SearchBuilder vmInstanceSearch = _affinityGroupVMMapDao.createSearchBuilder(); - vmInstanceSearch.and("instanceId", vmInstanceSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - - SearchBuilder groupSearch = _affinityGroupDao.createSearchBuilder(); - - SearchCriteria sc = groupSearch.create(); - - if (accountId != null) { - sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); - } - - if (domainId != null) { - sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); - } - - if (affinityGroupId != null) { - sc.addAnd("id", SearchCriteria.Op.EQ, affinityGroupId); - } - - if (affinityGroupName != null) { - sc.addAnd("name", SearchCriteria.Op.EQ, affinityGroupName); - } - - if (affinityGroupType != null) { - sc.addAnd("type", SearchCriteria.Op.EQ, affinityGroupType); - } - - if (vmId != null) { - UserVmVO userVM = _userVmDao.findById(vmId); - if (userVM == null) { - throw new InvalidParameterValueException("Unable to list affinity groups for virtual machine instance " + vmId + "; instance not found."); - } - _accountMgr.checkAccess(caller, null, true, userVM); - // add join to affinity_groups_vm_map - groupSearch.join("vmInstanceSearch", vmInstanceSearch, groupSearch.entity().getId(), vmInstanceSearch.entity().getAffinityGroupId(), - JoinBuilder.JoinType.INNER); - sc.setJoinParameters("vmInstanceSearch", "instanceId", vmId); - } - - Pair, Integer> result = _affinityGroupDao.searchAndCount(sc, searchFilter); - return new Pair, Integer>(result.first(), result.second()); } @Override @@ -458,7 +412,7 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro // Verify input parameters UserVmVO vmInstance = _userVmDao.findById(vmId); if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + throw new InvalidParameterValueException("Unable to find a virtual machine with id " + vmId); } // Check that the VM is stopped diff --git a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java index 8cf4cdc7d23..354054a4115 100644 --- a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java +++ b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java @@ -167,38 +167,39 @@ public class AffinityApiUnitTest { @Test public void createAffinityGroupTest() { when(_groupDao.isNameInUse(anyLong(), anyLong(), eq("group1"))).thenReturn(false); - AffinityGroup group = _affinityService.createAffinityGroup("user", domainId, "group1", "mock", "affinity group one"); + AffinityGroup group = _affinityService.createAffinityGroup("user", null, domainId, "group1", "mock", "affinity group one"); assertNotNull("Affinity group 'group1' of type 'mock' failed to create ", group); } @Test(expected = InvalidParameterValueException.class) public void invalidAffinityTypeTest() { - AffinityGroup group = _affinityService.createAffinityGroup("user", domainId, "group1", "invalid", "affinity group one"); + AffinityGroup group = _affinityService.createAffinityGroup("user", null, domainId, "group1", "invalid", "affinity group one"); } @Test(expected = InvalidParameterValueException.class) public void uniqueAffinityNameTest() { when(_groupDao.isNameInUse(anyLong(), anyLong(), eq("group1"))).thenReturn(true); - AffinityGroup group2 = _affinityService.createAffinityGroup("user", domainId, "group1", "mock", "affinity group two"); + AffinityGroup group2 = _affinityService.createAffinityGroup("user", null, domainId, "group1", "mock", "affinity group two"); } @Test(expected = InvalidParameterValueException.class) public void deleteAffinityGroupInvalidIdTest() throws ResourceInUseException { when(_groupDao.findById(20L)).thenReturn(null); - _affinityService.deleteAffinityGroup(20L, "user", domainId, "group1"); + _affinityService.deleteAffinityGroup(20L, "user", null, domainId, "group1"); } @Test(expected = InvalidParameterValueException.class) public void deleteAffinityGroupInvalidIdName() throws ResourceInUseException { + when(_acctMgr.finalyzeAccountId("user", domainId, null, true)).thenReturn(200L); when(_groupDao.findByAccountAndName(200L, "group1")).thenReturn(null); - _affinityService.deleteAffinityGroup(null, "user", domainId, "group1"); + _affinityService.deleteAffinityGroup(null, "user", null, domainId, "group1"); } @Test(expected = InvalidParameterValueException.class) public void deleteAffinityGroupNullIdName() throws ResourceInUseException { - _affinityService.deleteAffinityGroup(null, "user", domainId, null); + _affinityService.deleteAffinityGroup(null, "user", null, domainId, null); } @Test(expected = InvalidParameterValueException.class) diff --git a/server/test/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java b/server/test/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java new file mode 100644 index 00000000000..448424716d5 --- /dev/null +++ b/server/test/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java @@ -0,0 +1,358 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.affinity; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import com.cloud.utils.db.EntityManager; +import com.cloud.event.ActionEventUtils; +import com.cloud.user.User; + +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.test.utils.SpringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; +import org.apache.cloudstack.api.command.user.affinitygroup.CreateAffinityGroupCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.messagebus.MessageBus; + +import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.EventVO; +import com.cloud.event.dao.EventDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountService; +import com.cloud.user.AccountVO; +import com.cloud.user.DomainManager; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.user.dao.UserDao; +import com.cloud.utils.component.ComponentContext; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.projects.dao.ProjectDao; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +public class AffinityGroupServiceImplTest { + + @Inject + AffinityGroupServiceImpl _affinityService; + + @Inject + AccountManager _acctMgr; + + @Inject + AffinityGroupProcessor _processor; + + @Inject + AffinityGroupDao _groupDao; + + @Inject + UserVmDao _vmDao; + + @Inject + AffinityGroupVMMapDao _affinityGroupVMMapDao; + + @Inject + AffinityGroupDao _affinityGroupDao; + + @Inject + ActionEventUtils _eventUtils; + + @Inject + AccountDao _accountDao; + + @Inject + ProjectDao _projectDao; + + @Inject + EventDao _eventDao; + + @Inject + DedicatedResourceDao _dedicatedDao; + + private static final long DOMAIN_ID = 5L; + private static final long PROJECT_ID = 10L; + private static final String ACCOUNT_NAME = "user"; + private static final String AFFINITY_GROUP_NAME = "group1"; + + private AccountVO acct; + + @BeforeClass + public static void setUpClass() throws ConfigurationException { + } + + @Before + public void setUp() { + ComponentContext.initComponentsLifeCycle(); + acct = new AccountVO(200L); + acct.setType(Account.ACCOUNT_TYPE_NORMAL); + acct.setAccountName(ACCOUNT_NAME); + acct.setDomainId(DOMAIN_ID); + + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); + + CallContext.register(user, acct); + + when(_processor.getType()).thenReturn("mock"); + when(_accountDao.findByIdIncludingRemoved(0L)).thenReturn(acct); + + List affinityProcessors = new ArrayList(); + affinityProcessors.add(_processor); + _affinityService.setAffinityGroupProcessors(affinityProcessors); + + AffinityGroupVO group = new AffinityGroupVO(AFFINITY_GROUP_NAME, "mock", "mock group", DOMAIN_ID, 200L, ControlledEntity.ACLType.Account); + Mockito.when(_affinityGroupDao.persist(Matchers.any(AffinityGroupVO.class))).thenReturn(group); + Mockito.when(_affinityGroupDao.findById(Matchers.anyLong())).thenReturn(group); + Mockito.when(_affinityGroupDao.findByAccountAndName(Matchers.anyLong(), Matchers.anyString())).thenReturn(group); + Mockito.when(_affinityGroupDao.lockRow(Matchers.anyLong(), anyBoolean())).thenReturn(group); + Mockito.when(_affinityGroupDao.expunge(Matchers.anyLong())).thenReturn(true); + Mockito.when(_eventDao.persist(Matchers.any(EventVO.class))).thenReturn(new EventVO()); + } + + @After + public void tearDown() { + CallContext.unregister(); + } + + @Test + public void createAffinityGroupFromCmdTest() { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + when(_groupDao.isNameInUse(anyLong(), anyLong(), eq(AFFINITY_GROUP_NAME))).thenReturn(false); + CreateAffinityGroupCmd mockCreateAffinityGroupCmd = Mockito.mock(CreateAffinityGroupCmd.class); + when(mockCreateAffinityGroupCmd.getProjectId()).thenReturn(PROJECT_ID); + when(mockCreateAffinityGroupCmd.getAffinityGroupName()).thenReturn(AFFINITY_GROUP_NAME); + when(mockCreateAffinityGroupCmd.getAffinityGroupType()).thenReturn("mock"); + when(mockCreateAffinityGroupCmd.getDescription()).thenReturn("affinity group one"); + AffinityGroup group = _affinityService.createAffinityGroup(mockCreateAffinityGroupCmd); + assertNotNull("Affinity group 'group1' of type 'mock' failed to create ", group); + } + + @Test + public void createAffinityGroupTest() { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + when(_groupDao.isNameInUse(anyLong(), anyLong(), eq(AFFINITY_GROUP_NAME))).thenReturn(false); + AffinityGroup group = _affinityService.createAffinityGroup(ACCOUNT_NAME, null, DOMAIN_ID, AFFINITY_GROUP_NAME, "mock", "affinity group one"); + assertNotNull("Affinity group 'group1' of type 'mock' failed to create ", group); + + } + + @Test + public void shouldDeleteDomainLevelAffinityGroup() { + AffinityGroupVO mockGroup = Mockito.mock(AffinityGroupVO.class); + when(mockGroup.getId()).thenReturn(2L); + when(_affinityGroupDao.findById(Matchers.anyLong())).thenReturn(mockGroup); + _affinityService.deleteAffinityGroup(2L, null, null, DOMAIN_ID, null); + Mockito.verify(_affinityGroupDao).expunge(2L); + } + + @Test + public void shouldDeleteAffintyGroupById() { + AffinityGroupVO mockGroup = Mockito.mock(AffinityGroupVO.class); + when(mockGroup.getId()).thenReturn(1L); + when(_affinityGroupDao.findById(Matchers.anyLong())).thenReturn(mockGroup); + _affinityService.deleteAffinityGroup(1L, ACCOUNT_NAME, null, DOMAIN_ID, null); + Mockito.verify(_affinityGroupDao).expunge(1L); + } + + @Test(expected = InvalidParameterValueException.class) + public void invalidAffinityTypeTest() { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + _affinityService.createAffinityGroup(ACCOUNT_NAME, null, DOMAIN_ID, AFFINITY_GROUP_NAME, "invalid", "affinity group one"); + + } + + @Test(expected = InvalidParameterValueException.class) + public void uniqueAffinityNameTest() { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + when(_groupDao.isNameInUse(anyLong(), anyLong(), eq(AFFINITY_GROUP_NAME))).thenReturn(true); + _affinityService.createAffinityGroup(ACCOUNT_NAME, null, DOMAIN_ID, AFFINITY_GROUP_NAME, "mock", "affinity group two"); + } + + @Test(expected = InvalidParameterValueException.class) + public void deleteAffinityGroupInvalidIdTest() throws ResourceInUseException { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + when(_groupDao.findById(20L)).thenReturn(null); + _affinityService.deleteAffinityGroup(20L, ACCOUNT_NAME, null, DOMAIN_ID, AFFINITY_GROUP_NAME); + } + + @Test(expected = InvalidParameterValueException.class) + public void deleteAffinityGroupInvalidIdName() throws ResourceInUseException { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + when(_acctMgr.finalyzeAccountId(ACCOUNT_NAME, DOMAIN_ID, null, true)).thenReturn(200L); + when(_groupDao.findByAccountAndName(200L, AFFINITY_GROUP_NAME)).thenReturn(null); + _affinityService.deleteAffinityGroup(null, ACCOUNT_NAME, null, DOMAIN_ID, AFFINITY_GROUP_NAME); + } + + @Test(expected = InvalidParameterValueException.class) + public void deleteAffinityGroupNullIdName() throws ResourceInUseException { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + _affinityService.deleteAffinityGroup(null, ACCOUNT_NAME, null, DOMAIN_ID, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void updateAffinityGroupVMRunning() throws ResourceInUseException { + when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); + UserVmVO vm = new UserVmVO(10L, "test", "test", 101L, HypervisorType.Any, 21L, false, false, DOMAIN_ID, 200L, 1, 5L, "", "test", 1L); + vm.setState(VirtualMachine.State.Running); + when(_vmDao.findById(10L)).thenReturn(vm); + + List affinityGroupIds = new ArrayList(); + affinityGroupIds.add(20L); + + _affinityService.updateVMAffinityGroups(10L, affinityGroupIds); + } + + @Configuration + @ComponentScan(basePackageClasses = {AffinityGroupServiceImpl.class, ActionEventUtils.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) + public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { + + @Bean + public AccountDao accountDao() { + return Mockito.mock(AccountDao.class); + } + + @Bean + public ProjectDao projectDao() { + return Mockito.mock(ProjectDao.class); + } + + @Bean + public AccountService accountService() { + return Mockito.mock(AccountService.class); + } + + @Bean + public AffinityGroupProcessor affinityGroupProcessor() { + return Mockito.mock(AffinityGroupProcessor.class); + } + + @Bean + public AffinityGroupDao affinityGroupDao() { + return Mockito.mock(AffinityGroupDao.class); + } + + @Bean + public AffinityGroupVMMapDao affinityGroupVMMapDao() { + return Mockito.mock(AffinityGroupVMMapDao.class); + } + + @Bean + public DedicatedResourceDao dedicatedResourceDao() { + return Mockito.mock(DedicatedResourceDao.class); + } + + @Bean + public AccountManager accountManager() { + return Mockito.mock(AccountManager.class); + } + + @Bean + public DomainManager domainManager() { + return Mockito.mock(DomainManager.class); + } + + @Bean + public EventDao eventDao() { + return Mockito.mock(EventDao.class); + } + + @Bean + public UserVmDao userVMDao() { + return Mockito.mock(UserVmDao.class); + } + + @Bean + public UserDao userDao() { + return Mockito.mock(UserDao.class); + } + + @Bean + public AffinityGroupDomainMapDao affinityGroupDomainMapDao() { + return Mockito.mock(AffinityGroupDomainMapDao.class); + } + + @Bean + public EntityManager entityManager() { + return Mockito.mock(EntityManager.class); + } + + @Bean + public DomainDao domainDao() { + return Mockito.mock(DomainDao.class); + } + + @Bean + public MessageBus messageBus() { + return Mockito.mock(MessageBus.class); + } + + @Bean + public ConfigurationDao configDao() { + return Mockito.mock(ConfigurationDao.class); + } + + public static class Library implements TypeFilter { + + @Override + public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { + ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); + return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); + } + } + } +} diff --git a/setup/db/db/schema-460to461.sql b/setup/db/db/schema-460to461.sql index e42d0b2e9ad..4e073442759 100644 --- a/setup/db/db/schema-460to461.sql +++ b/setup/db/db/schema-460to461.sql @@ -18,3 +18,35 @@ --; -- Schema upgrade from 4.6.0 to 4.6.1; --; +DROP VIEW IF EXISTS `cloud`.`affinity_group_view`; +CREATE VIEW `affinity_group_view` + AS SELECT + `affinity_group`.`id` AS `id`, + `affinity_group`.`name` AS `name`, + `affinity_group`.`type` AS `type`, + `affinity_group`.`description` AS `description`, + `affinity_group`.`uuid` AS `uuid`, + `affinity_group`.`acl_type` AS `acl_type`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `vm_instance`.`id` AS `vm_id`, + `vm_instance`.`uuid` AS `vm_uuid`, + `vm_instance`.`name` AS `vm_name`, + `vm_instance`.`state` AS `vm_state`, + `user_vm`.`display_name` AS `vm_display_name` +FROM `affinity_group` + JOIN `account` ON`affinity_group`.`account_id` = `account`.`id` + JOIN `domain` ON`affinity_group`.`domain_id` = `domain`.`id` + LEFT JOIN `projects` ON`projects`.`project_account_id` = `account`.`id` + LEFT JOIN `affinity_group_vm_map` ON`affinity_group`.`id` = `affinity_group_vm_map`.`affinity_group_id` + LEFT JOIN `vm_instance` ON`vm_instance`.`id` = `affinity_group_vm_map`.`instance_id` + LEFT JOIN `user_vm` ON`user_vm`.`id` = `vm_instance`.`id`; diff --git a/systemvm/patches/debian/config/opt/cloud/bin/configure.py b/systemvm/patches/debian/config/opt/cloud/bin/configure.py index ad3705acc7d..deb4a74e042 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/configure.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/configure.py @@ -74,6 +74,27 @@ class CsPassword(CsDataBag): logging.debug("Update password server result ==> %s" % result) +class CsStaticRoutes(CsDataBag): + + def process(self): + logging.debug("Processing CsStaticRoutes file ==> %s" % self.dbag) + for item in self.dbag: + if item == "id": + continue + self.__update(self.dbag[item]) + + def __update(self, route): + if route['revoke']: + command = "route del -net %s gw %s" % (route['network'], route['gateway']) + result = CsHelper.execute(command) + else: + command = "ip route show | grep %s | awk '{print $1, $3}'" % route['network'] + result = CsHelper.execute(command) + if not result: + route_command = "route add -net %s gw %s" % (route['network'], route['gateway']) + result = CsHelper.execute(route_command) + + class CsAcl(CsDataBag): """ Deal with Network acls @@ -932,13 +953,17 @@ def main(argv): mon = CsMonitor("monitorservice", config) mon.process() - logging.debug("Configuring iptables rules .....") + logging.debug("Configuring iptables rules") nf = CsNetfilters() nf.compare(config.get_fw()) - + red = CsRedundant(config) red.set() + logging.debug("Configuring static routes") + static_routes = CsStaticRoutes("staticroutes", config) + static_routes.process() + logging.debug("Configuring iptables rules done ...saving rules") # Save iptables configuration - will be loaded on reboot by the iptables-restore that is configured on /etc/rc.local diff --git a/systemvm/patches/debian/config/opt/cloud/bin/cs_staticroutes.py b/systemvm/patches/debian/config/opt/cloud/bin/cs_staticroutes.py new file mode 100755 index 00000000000..98244db03c8 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/cs_staticroutes.py @@ -0,0 +1,30 @@ +# -- coding: utf-8 -- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +from pprint import pprint + + +def merge(dbag, staticroutes): + for route in staticroutes['routes']: + key = route['ip_address'] + revoke = route['revoke'] + if revoke: + del dbag[key] + else: + dbag[key] = route + + return dbag diff --git a/systemvm/patches/debian/config/opt/cloud/bin/merge.py b/systemvm/patches/debian/config/opt/cloud/bin/merge.py index cc14d6aca8d..374cf2ccb4f 100755 --- a/systemvm/patches/debian/config/opt/cloud/bin/merge.py +++ b/systemvm/patches/debian/config/opt/cloud/bin/merge.py @@ -34,6 +34,7 @@ import cs_forwardingrules import cs_site2sitevpn import cs_remoteaccessvpn import cs_vpnusers +import cs_staticroutes from pprint import pprint @@ -126,6 +127,8 @@ class updateDataBag: dbag = self.process_remoteaccessvpn(self.db.getDataBag()) elif self.qFile.type == 'vpnuserlist': dbag = self.process_vpnusers(self.db.getDataBag()) + elif self.qFile.type == 'staticroutes': + dbag = self.process_staticroutes(self.db.getDataBag()) else: logging.error("Error I do not know what to do with file of type %s", self.qFile.type) return @@ -172,6 +175,9 @@ class updateDataBag: def process_monitorservice(self, dbag): return cs_monitorservice.merge(dbag, self.qFile.data) + def process_staticroutes(self, dbag): + return cs_staticroutes.merge(dbag, self.qFile.data) + def processVMpassword(self, dbag): return cs_vmp.merge(dbag, self.qFile.data) diff --git a/test/integration/component/test_affinity_groups.py b/test/integration/component/test_affinity_groups.py index b071e04cd2e..c0c552973ae 100644 --- a/test/integration/component/test_affinity_groups.py +++ b/test/integration/component/test_affinity_groups.py @@ -64,7 +64,7 @@ class Services: "type": "host anti-affinity", }, "virtual_machine" : { - "hypervisor" : "KVM", + }, "new_domain": { "name": "New Domain", diff --git a/test/integration/component/test_affinity_groups_projects.py b/test/integration/component/test_affinity_groups_projects.py new file mode 100644 index 00000000000..dbcc92b849b --- /dev/null +++ b/test/integration/component/test_affinity_groups_projects.py @@ -0,0 +1,1082 @@ +#!/usr/bin/env python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +from marvin.cloudstackTestCase import cloudstackTestCase, unittest +from marvin.cloudstackAPI import deleteAffinityGroup +from marvin.lib.utils import (cleanup_resources, + random_gen) +from marvin.lib.base import (Account, + Project, + ServiceOffering, + VirtualMachine, + AffinityGroup, + Domain) +from marvin.lib.common import (get_zone, + get_domain, + get_template, + list_hosts, + list_virtual_machines, + wait_for_cleanup) +from nose.plugins.attrib import attr + +class Services: + """Test Account Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "NonRootDomain" + }, + "domain_admin_account": { + "email": "newtest@test.com", + "firstname": "Test", + "lastname": "User", + "username": "doadmintest", + "password": "password" + }, + "account": { + "email": "newtest@test.com", + "firstname": "Test", + "lastname": "User", + "username": "acc", + "password": "password" + }, + "account_not_in_project": { + "email": "newtest@test.com", + "firstname": "Test", + "lastname": "User", + "username": "account_not_in_project", + "password": "password" + }, + "project": { + "name": "Project", + "displaytext": "Project" + }, + "project2": { + "name": "Project2", + "displaytext": "Project2" + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + "memory": 64 + }, + "ostype": 'CentOS 5.3 (64-bit)', + "host_anti_affinity": { + "name": "", + "type": "host anti-affinity" + }, + "virtual_machine" : { + } + } + +class TestCreateAffinityGroup(cloudstackTestCase): + """ + Test various scenarios for Create Affinity Group API for projects + """ + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestCreateAffinityGroup, cls).getClsTestClient() + cls.api_client = cls.testClient.getApiClient() + cls.services = Services().services + + #Get Zone, Domain and templates + cls.rootdomain = get_domain(cls.api_client) + cls.domain = Domain.create(cls.api_client, cls.services["domain"]) + + cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + cls.domain_admin_account = Account.create( + cls.api_client, + cls.services["domain_admin_account"], + domainid=cls.domain.id, + admin=True + ) + + cls.domain_api_client = cls.testClient.getUserApiClient(cls.domain_admin_account.name, cls.domain.name, 2) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.account_api_client = cls.testClient.getUserApiClient(cls.account.name, cls.domain.name, 0) + + cls.account_not_in_project = Account.create( + cls.api_client, + cls.services["account_not_in_project"], + domainid=cls.domain.id + ) + + cls.account_not_in_project_api_client = cls.testClient.getUserApiClient(cls.account_not_in_project.name, cls.domain.name, 0) + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.project2 = Project.create( + cls.api_client, + cls.services["project2"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.debug("Created project with ID: %s" % cls.project.id) + cls.debug("Created project2 with ID: %s" % cls.project2.id) + + # Add user to the project + cls.project.addAccount( + cls.api_client, + cls.account.name + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.account.domainid + ) + + cls._cleanup = [] + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: +# #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def tearDownClass(cls): + try: + #Clean up, terminate the created templates + cls.domain.delete(cls.api_client, cleanup=True) + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None): + + if not api_client: + api_client = self.api_client + if aff_grp is None: + aff_grp = self.services["host_anti_affinity"] + if aff_grp_name is None: + aff_grp["name"] = "aff_grp_" + random_gen(size=6) + else: + aff_grp["name"] = aff_grp_name + if projectid is None: + projectid = self.project.id + try: + return AffinityGroup.create(api_client, aff_grp, None, None, projectid) + except Exception as e: + raise Exception("Error: Creation of Affinity Group failed : %s" % e) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_01_admin_create_aff_grp_for_project(self): + """ + Test create affinity group as admin in project + @return: + """ + aff_grp = self.create_aff_grp() + self.debug("Created Affinity Group: %s" % aff_grp.name) + list_aff_grps = AffinityGroup.list(self.api_client, id=aff_grp.id) + self.assert_(isinstance(list_aff_grps, list) and len(list_aff_grps) > 0) + self.assert_(list_aff_grps[0].id == aff_grp.id) + self.assert_(list_aff_grps[0].projectid == self.project.id) + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_02_doadmin_create_aff_grp_for_project(self): + """ + Test create affinity group as domain admin for projects + @return: + """ + aff_grp = self.create_aff_grp(api_client=self.domain_api_client) + list_aff_grps = AffinityGroup.list(self.domain_api_client, id=aff_grp.id) + self.assert_(isinstance(list_aff_grps, list) and len(list_aff_grps) > 0) + self.assert_(list_aff_grps[0].id == aff_grp.id) + self.assert_(list_aff_grps[0].projectid == self.project.id) + self.cleanup.append(aff_grp) + + @attr(tags=["vogxn", "simulator", "basic", "advanced"], required_hardware="false") + def test_03_user_create_aff_grp_for_project(self): + """ + Test create affinity group as user for projects + @return: + """ + aff_grp = self.create_aff_grp(api_client=self.account_api_client) + list_aff_grps = AffinityGroup.list(self.api_client, id=aff_grp.id) + self.assert_(isinstance(list_aff_grps, list) and len(list_aff_grps) > 0) + self.assert_(list_aff_grps[0].id == aff_grp.id) + self.assert_(list_aff_grps[0].projectid == self.project.id) + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_4_user_create_aff_grp_existing_name_for_project(self): + """ + Test create affinity group that exists (same name) for projects + @return: + """ + + failed_aff_grp = None + aff_grp = self.create_aff_grp(api_client=self.account_api_client) + with self.assertRaises(Exception): + failed_aff_grp = self.create_aff_grp(api_client=self.account_api_client,aff_grp_name = aff_grp.name) + + if failed_aff_grp: + self.cleanup.append(failed_aff_grp) + self.cleanup.append(aff_grp) + +class TestListAffinityGroups(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestListAffinityGroups, cls).getClsTestClient() + cls.api_client = cls.testClient.getApiClient() + cls.services = Services().services + + #Get Zone, Domain and templates + cls.rootdomain = get_domain(cls.api_client) + cls.domain = Domain.create(cls.api_client, cls.services["domain"]) + + cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + cls.domain_admin_account = Account.create( + cls.api_client, + cls.services["domain_admin_account"], + domainid=cls.domain.id, + admin=True + ) + + cls.domain_api_client = cls.testClient.getUserApiClient(cls.domain_admin_account.name, cls.domain.name, 2) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.account_api_client = cls.testClient.getUserApiClient(cls.account.name, cls.domain.name, 0) + + cls.account_not_in_project = Account.create( + cls.api_client, + cls.services["account_not_in_project"], + domainid=cls.domain.id + ) + + cls.account_not_in_project_api_client = cls.testClient.getUserApiClient(cls.account_not_in_project.name, cls.domain.name, 0) + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.project2 = Project.create( + cls.api_client, + cls.services["project2"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.debug("Created project with ID: %s" % cls.project.id) + cls.debug("Created project2 with ID: %s" % cls.project2.id) + + # Add user to the project + cls.project.addAccount( + cls.api_client, + cls.account.name + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.account.domainid + ) + + cls._cleanup = [] + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: +# #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.api_client, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def tearDownClass(cls): + try: + cls.domain.delete(cls.api_client, cleanup=True) + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None): + + if not api_client: + api_client = self.api_client + if aff_grp is None: + aff_grp = self.services["host_anti_affinity"] + if aff_grp_name is None: + aff_grp["name"] = "aff_grp_" + random_gen(size=6) + else: + aff_grp["name"] = aff_grp_name + if projectid is None: + projectid = self.project.id + try: + return AffinityGroup.create(api_client, aff_grp, None, None, projectid) + except Exception as e: + raise Exception("Error: Creation of Affinity Group failed : %s" % e) + + def create_vm_in_aff_grps(self, api_client=None, ag_list=[], projectid=None): + self.debug('Creating VM in AffinityGroups=%s' % ag_list) + + if api_client is None: + api_client = self.api_client + if projectid is None: + projectid = self.project.id + + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + projectid=projectid, + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + affinitygroupnames=ag_list + ) + self.debug('Created VM=%s in Affinity Group=%s' % (vm.id, tuple(ag_list))) + + list_vm = list_virtual_machines(api_client, id=vm.id, projectid=projectid) + self.assertEqual(isinstance(list_vm, list), True,"Check list response returns a valid list") + self.assertNotEqual(len(list_vm),0, "Check VM available in List Virtual Machines") + vm_response = list_vm[0] + self.assertEqual(vm_response.state, 'Running',msg="VM is not in Running state") + self.assertEqual(vm_response.projectid, projectid,msg="VM is not in project") + return vm, vm_response.hostid + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_01_list_aff_grps_for_vm(self): + """ + List affinity group for a vm for projects + """ + aff_grps = [] + aff_grps.append(self.create_aff_grp(self.domain_api_client, projectid=self.project.id)) + + vm, hostid = self.create_vm_in_aff_grps(self.account_api_client,ag_list=[aff_grps[0].name]) + list_aff_grps = AffinityGroup.list(self.api_client,virtualmachineid=vm.id) + + self.assertEqual(list_aff_grps[0].name, aff_grps[0].name,"Listing Affinity Group by VM id failed") + self.assertEqual(list_aff_grps[0].projectid, self.project.id,"Listing Affinity Group by VM id failed, vm was not in project") + + vm.delete(self.api_client) + #Wait for expunge interval to cleanup VM + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + self.cleanup.append(aff_grps[0]) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_02_list_multiple_aff_grps_for_vm(self): + """ + List multiple affinity groups associated with a vm for projects + """ + + aff_grp_01 = self.create_aff_grp(self.account_api_client) + aff_grp_02 = self.create_aff_grp(self.account_api_client) + + aff_grps_names = [aff_grp_01.name, aff_grp_02.name] + vm, hostid = self.create_vm_in_aff_grps(ag_list=aff_grps_names) + list_aff_grps = AffinityGroup.list(self.api_client, + virtualmachineid=vm.id) + + list_aff_grps_names = [list_aff_grps[0].name, list_aff_grps[1].name] + + aff_grps_names.sort() + list_aff_grps_names.sort() + self.assertEqual(aff_grps_names, list_aff_grps_names,"One of the Affinity Groups is missing %s" % list_aff_grps_names) + + vm.delete(self.api_client) + #Wait for expunge interval to cleanup VM + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + self.cleanup.append(aff_grp_01) + self.cleanup.append(aff_grp_02) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_03_list_aff_grps_by_id(self): + """ + List affinity groups by id for projects + """ + aff_grp = self.create_aff_grp(self.account_api_client) + list_aff_grps = AffinityGroup.list(self.account_api_client, id=aff_grp.id, projectid=self.project.id) + self.assertEqual(list_aff_grps[0].id, aff_grp.id,"Listing Affinity Group by id failed") + with self.assertRaises(Exception): + AffinityGroup.list(self.account_not_in_project_api_client, id=aff_grp.id, projectid=self.project.id) + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_04_list_aff_grps_by_name(self): + """ + List Affinity Groups by name for projects + """ + aff_grp = self.create_aff_grp(self.account_api_client) + list_aff_grps = AffinityGroup.list(self.account_api_client, name=aff_grp.name, projectid=self.project.id) + self.assertEqual(list_aff_grps[0].name, aff_grp.name,"Listing Affinity Group by name failed") + with self.assertRaises(Exception): + AffinityGroup.list(self.account_not_in_project_api_client, id=aff_grp.id, projectid=self.project.id) + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_05_list_aff_grps_by_non_existing_id(self): + """ + List Affinity Groups by non-existing id for projects + """ + aff_grp = self.create_aff_grp(self.account_api_client) + list_aff_grps = AffinityGroup.list(self.account_api_client, id=1234, projectid=self.project.id) + self.assertEqual(list_aff_grps, None, "Listing Affinity Group by non-existing id succeeded.") + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_06_list_aff_grps_by_non_existing_name(self): + """ + List Affinity Groups by non-existing name for projects + """ + + aff_grp = self.create_aff_grp(self.account_api_client) + list_aff_grps = AffinityGroup.list(self.account_api_client, name="inexistantName", projectid=self.project.id) + self.assertEqual(list_aff_grps, None, "Listing Affinity Group by non-existing name succeeded.") + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_07_list_all_vms_in_aff_grp(self): + """ + List affinity group should list all for a vms associated with that group for projects + """ + + aff_grp = self.create_aff_grp(self.account_api_client) + + vm1, hostid1 = self.create_vm_in_aff_grps(ag_list=[aff_grp.name]) + vm2, hostid2 = self.create_vm_in_aff_grps(ag_list=[aff_grp.name]) + list_aff_grps = AffinityGroup.list(self.api_client, id=aff_grp.id, projectid=self.project.id) + + self.assertEqual(list_aff_grps[0].name, aff_grp.name, "Listing Affinity Group by id failed") + + self.assertEqual(list_aff_grps[0].virtualmachineIds[0], vm1.id, "List affinity group response.virtualmachineIds for group: %s doesn't contain vmid : %s" % (aff_grp.name, vm1.id)) + self.assertEqual(list_aff_grps[0].virtualmachineIds[1], vm2.id, "List affinity group response.virtualmachineIds for group: %s doesn't contain vmid : %s" % (aff_grp.name, vm2.id)) + + + vm1.delete(self.api_client) + vm2.delete(self.api_client) + #Wait for expunge interval to cleanup VM + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + self.cleanup.append(aff_grp) + +class TestDeleteAffinityGroups(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestDeleteAffinityGroups, cls).getClsTestClient() + cls.api_client = cls.testClient.getApiClient() + cls.services = Services().services + + #Get Zone, Domain and templates + cls.rootdomain = get_domain(cls.api_client) + cls.domain = Domain.create(cls.api_client, cls.services["domain"]) + + cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + cls.domain_admin_account = Account.create( + cls.api_client, + cls.services["domain_admin_account"], + domainid=cls.domain.id, + admin=True + ) + + cls.domain_api_client = cls.testClient.getUserApiClient(cls.domain_admin_account.name, cls.domain.name, 2) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.account_api_client = cls.testClient.getUserApiClient(cls.account.name, cls.domain.name, 0) + + cls.account_not_in_project = Account.create( + cls.api_client, + cls.services["account_not_in_project"], + domainid=cls.domain.id + ) + + cls.account_not_in_project_api_client = cls.testClient.getUserApiClient(cls.account_not_in_project.name, cls.domain.name, 0) + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.project2 = Project.create( + cls.api_client, + cls.services["project2"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.debug("Created project with ID: %s" % cls.project.id) + cls.debug("Created project2 with ID: %s" % cls.project2.id) + + # Add user to the project + cls.project.addAccount( + cls.api_client, + cls.account.name + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.account.domainid + ) + + cls._cleanup = [] + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: +# #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.api_client, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def tearDownClass(cls): + try: + cls.domain.delete(cls.api_client, cleanup=True) + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None): + + if not api_client: + api_client = self.api_client + if aff_grp is None: + aff_grp = self.services["host_anti_affinity"] + if aff_grp_name is None: + aff_grp["name"] = "aff_grp_" + random_gen(size=6) + else: + aff_grp["name"] = aff_grp_name + if projectid is None: + projectid = self.project.id + try: + return AffinityGroup.create(api_client, aff_grp, None, None, projectid) + except Exception as e: + raise Exception("Error: Creation of Affinity Group failed : %s" % e) + + def create_vm_in_aff_grps(self, api_client=None, ag_list=[], projectid=None): + self.debug('Creating VM in AffinityGroups=%s' % ag_list) + + if api_client is None: + api_client = self.api_client + if projectid is None: + projectid = self.project.id + + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + projectid=projectid, + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + affinitygroupnames=ag_list + ) + self.debug('Created VM=%s in Affinity Group=%s' % (vm.id, tuple(ag_list))) + list_vm = list_virtual_machines(self.api_client, id=vm.id, projectid=projectid) + self.assertEqual(isinstance(list_vm, list), True,"Check list response returns an invalid list %s" % list_vm) + self.assertNotEqual(len(list_vm),0, "Check VM available in TestDeployVMAffinityGroups") + self.assertEqual(list_vm[0].id, vm.id,"Listed vm does not have the same ids") + vm_response = list_vm[0] + self.assertEqual(vm.state, 'Running',msg="VM is not in Running state") + self.assertEqual(vm.projectid, projectid,msg="VM is not in project") + self.assertNotEqual(vm_response.hostid, None, "Host id was null for vm %s" % vm_response) + return vm, vm_response.hostid + + def delete_aff_group(self, apiclient, **kwargs): + cmd = deleteAffinityGroup.deleteAffinityGroupCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return apiclient.deleteAffinityGroup(cmd) + + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_01_delete_aff_grp_by_id(self): + """ + #Delete Affinity Group by id. + """ + + aff_grp1 = self.create_aff_grp(self.account_api_client) + aff_grp2 = self.create_aff_grp(self.account_api_client) + + aff_grp1.delete(self.account_api_client) + + with self.assertRaises(Exception): + list_aff_grps = AffinityGroup.list(self.api_client, id=aff_grp1.id) + + self.cleanup.append(aff_grp2) + + @attr(tags=["simulator", "basic", "advanced"], required_hardware="false") + def test_02_delete_aff_grp_by_id_another_user(self): + """ + #Delete Affinity Group by id should fail for user not in project + """ + + aff_grp1 = self.create_aff_grp(self.account_api_client) + aff_grp2 = self.create_aff_grp(self.account_api_client) + + with self.assertRaises(Exception): + aff_grp1.delete(self.account_not_in_project_api_client) + + self.cleanup.append(aff_grp1) + self.cleanup.append(aff_grp2) + +class TestUpdateVMAffinityGroups(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestUpdateVMAffinityGroups, cls).getClsTestClient() + cls.api_client = cls.testClient.getApiClient() + cls.services = Services().services + + #Get Zone, Domain and templates + cls.rootdomain = get_domain(cls.api_client) + cls.domain = Domain.create(cls.api_client, cls.services["domain"]) + + cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + cls.domain_admin_account = Account.create( + cls.api_client, + cls.services["domain_admin_account"], + domainid=cls.domain.id, + admin=True + ) + + cls.domain_api_client = cls.testClient.getUserApiClient(cls.domain_admin_account.name, cls.domain.name, 2) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.account_api_client = cls.testClient.getUserApiClient(cls.account.name, cls.domain.name, 0) + + cls.account_not_in_project = Account.create( + cls.api_client, + cls.services["account_not_in_project"], + domainid=cls.domain.id + ) + + cls.account_not_in_project_api_client = cls.testClient.getUserApiClient(cls.account_not_in_project.name, cls.domain.name, 0) + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.project2 = Project.create( + cls.api_client, + cls.services["project2"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.debug("Created project with ID: %s" % cls.project.id) + cls.debug("Created project2 with ID: %s" % cls.project2.id) + + # Add user to the project + cls.project.addAccount( + cls.api_client, + cls.account.name + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.account.domainid + ) + + cls._cleanup = [] + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: +# #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.api_client, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def tearDownClass(cls): + try: + cls.domain.delete(cls.api_client, cleanup=True) + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None): + + if not api_client: + api_client = self.api_client + if aff_grp is None: + aff_grp = self.services["host_anti_affinity"] + if aff_grp_name is None: + aff_grp["name"] = "aff_grp_" + random_gen(size=6) + else: + aff_grp["name"] = aff_grp_name + if projectid is None: + projectid = self.project.id + try: + return AffinityGroup.create(api_client, aff_grp, None, None, projectid) + except Exception as e: + raise Exception("Error: Creation of Affinity Group failed : %s" % e) + + def create_vm_in_aff_grps(self, api_client=None, ag_list=[], projectid=None): + self.debug('Creating VM in AffinityGroups=%s' % ag_list) + + if api_client is None: + api_client = self.api_client + if projectid is None: + projectid = self.project.id + + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + projectid=projectid, + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + affinitygroupnames=ag_list + ) + self.debug('Created VM=%s in Affinity Group=%s' % (vm.id, tuple(ag_list))) + list_vm = list_virtual_machines(self.api_client, id=vm.id, projectid=projectid) + self.assertEqual(isinstance(list_vm, list), True,"Check list response returns an invalid list %s" % list_vm) + self.assertNotEqual(len(list_vm),0, "Check VM available in TestDeployVMAffinityGroups") + self.assertEqual(list_vm[0].id, vm.id,"Listed vm does not have the same ids") + vm_response = list_vm[0] + self.assertEqual(vm.state, 'Running',msg="VM is not in Running state") + self.assertEqual(vm.projectid, projectid,msg="VM is not in project") + self.assertNotEqual(vm_response.hostid, None, "Host id was null for vm %s" % vm_response) + return vm, vm_response.hostid + + @attr(tags=["simulator", "basic", "advanced", "multihost"], required_hardware="false") + def test_01_update_aff_grp_by_ids(self): + """ + Update the list of affinityGroups by using affinity groupids + + """ + aff_grp1 = self.create_aff_grp(self.account_api_client) + aff_grp2 = self.create_aff_grp(self.account_api_client) + + vm1, hostid1 = self.create_vm_in_aff_grps(ag_list=[aff_grp1.name]) + vm2, hostid2 = self.create_vm_in_aff_grps(ag_list=[aff_grp1.name]) + + vm1.stop(self.api_client) + + list_aff_grps = AffinityGroup.list(self.api_client, projectid=self.project.id) + + self.assertEqual(len(list_aff_grps), 2 , "2 affinity groups should be present") + + vm1.update_affinity_group(self.api_client,affinitygroupids=[list_aff_grps[0].id,list_aff_grps[1].id]) + + list_aff_grps = AffinityGroup.list(self.api_client,virtualmachineid=vm1.id) + + list_aff_grps_names = [list_aff_grps[0].name, list_aff_grps[1].name] + + aff_grps_names = [aff_grp1.name, aff_grp2.name] + aff_grps_names.sort() + list_aff_grps_names.sort() + self.assertEqual(aff_grps_names, list_aff_grps_names,"One of the Affinity Groups is missing %s" % list_aff_grps_names) + + vm1.start(self.api_client) + + vm_status = VirtualMachine.list(self.api_client, id=vm1.id) + self.assertNotEqual(vm_status[0].hostid, hostid2, "The virtual machine started on host %s violating the host anti-affinity rule" %vm_status[0].hostid) + + vm1.delete(self.api_client) + vm2.delete(self.api_client) + #Wait for expunge interval to cleanup VM + wait_for_cleanup(self.apiclient, ["expunge.delay", "expunge.interval"]) + aff_grp1.delete(self.api_client) + aff_grp2.delete(self.api_client) + + +class TestDeployVMAffinityGroups(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestDeployVMAffinityGroups, cls).getClsTestClient() + cls.api_client = cls.testClient.getApiClient() + cls.services = Services().services + + #Get Zone, Domain and templates + cls.rootdomain = get_domain(cls.api_client) + cls.domain = Domain.create(cls.api_client, cls.services["domain"]) + + cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + cls.domain_admin_account = Account.create( + cls.api_client, + cls.services["domain_admin_account"], + domainid=cls.domain.id, + admin=True + ) + + cls.domain_api_client = cls.testClient.getUserApiClient(cls.domain_admin_account.name, cls.domain.name, 2) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.account_api_client = cls.testClient.getUserApiClient(cls.account.name, cls.domain.name, 0) + + cls.account_not_in_project = Account.create( + cls.api_client, + cls.services["account_not_in_project"], + domainid=cls.domain.id + ) + + cls.account_not_in_project_api_client = cls.testClient.getUserApiClient(cls.account_not_in_project.name, cls.domain.name, 0) + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.project2 = Project.create( + cls.api_client, + cls.services["project2"], + account=cls.domain_admin_account.name, + domainid=cls.domain_admin_account.domainid + ) + + cls.debug("Created project with ID: %s" % cls.project.id) + cls.debug("Created project2 with ID: %s" % cls.project2.id) + + # Add user to the project + cls.project.addAccount( + cls.api_client, + cls.account.name + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.account.domainid + ) + + cls._cleanup = [] + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: +# #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.api_client, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def tearDownClass(cls): + try: + cls.domain.delete(cls.api_client, cleanup=True) + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None): + + if not api_client: + api_client = self.api_client + if aff_grp is None: + aff_grp = self.services["host_anti_affinity"] + if aff_grp_name is None: + aff_grp["name"] = "aff_grp_" + random_gen(size=6) + else: + aff_grp["name"] = aff_grp_name + if projectid is None: + projectid = self.project.id + try: + return AffinityGroup.create(api_client, aff_grp, None, None, projectid) + except Exception as e: + raise Exception("Error: Creation of Affinity Group failed : %s" % e) + + def create_vm_in_aff_grps(self, api_client=None, ag_list=[], projectid=None): + self.debug('Creating VM in AffinityGroups=%s' % ag_list) + + if api_client is None: + api_client = self.api_client + if projectid is None: + projectid = self.project.id + + vm = VirtualMachine.create( + api_client, + self.services["virtual_machine"], + projectid=projectid, + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + affinitygroupnames=ag_list + ) + self.debug('Created VM=%s in Affinity Group=%s' % (vm.id, tuple(ag_list))) + list_vm = list_virtual_machines(self.api_client, id=vm.id, projectid=projectid) + self.assertEqual(isinstance(list_vm, list), True,"Check list response returns an invalid list %s" % list_vm) + self.assertNotEqual(len(list_vm),0, "Check VM available in TestDeployVMAffinityGroups") + self.assertEqual(list_vm[0].id, vm.id,"Listed vm does not have the same ids") + vm_response = list_vm[0] + self.assertEqual(vm.state, 'Running',msg="VM is not in Running state") + self.assertEqual(vm.projectid, projectid,msg="VM is not in project") + self.assertNotEqual(vm_response.hostid, None, "Host id was null for vm %s" % vm_response) + return vm, vm_response.hostid + + @attr(tags=["simulator", "basic", "advanced", "multihost"], required_hardware="false") + def test_01_deploy_vm_anti_affinity_group(self): + """ + test DeployVM in anti-affinity groups + + deploy VM1 and VM2 in the same host-anti-affinity groups + Verify that the vms are deployed on separate hosts + """ + aff_grp = self.create_aff_grp(self.account_api_client) + vm1, hostid1 = self.create_vm_in_aff_grps(self.account_api_client,ag_list=[aff_grp.name]) + vm2, hostid2 = self.create_vm_in_aff_grps(self.account_api_client, ag_list=[aff_grp.name]) + + self.assertNotEqual(hostid1, hostid2, msg="Both VMs of affinity group %s are on the same host: %s , %s, %s, %s" % (aff_grp.name, vm1, hostid1, vm2, hostid2)) + + vm1.delete(self.api_client) + vm2.delete(self.api_client) + wait_for_cleanup(self.api_client, ["expunge.delay", "expunge.interval"]) + self.cleanup.append(aff_grp) + + @attr(tags=["simulator", "basic", "advanced", "multihost"], required_hardware="false") + def test_02_deploy_vm_anti_affinity_group_fail_on_not_enough_hosts(self): + """ + test DeployVM in anti-affinity groups with more vms than hosts. + """ + hosts = list_hosts(self.api_client, type="routing") + aff_grp = self.create_aff_grp(self.account_api_client) + vms = [] + for host in hosts: + vms.append(self.create_vm_in_aff_grps(self.account_api_client,ag_list=[aff_grp.name])) + + vm_failed = None + with self.assertRaises(Exception): + vm_failed = self.create_vm_in_aff_grps(self.account_api_client,ag_list=[aff_grp.name]) + + self.assertEqual(len(hosts), len(vms), "Received %s and %s " % (hosts, vms)) + + if vm_failed: + vm_failed.expunge(self.api_client) + + self.cleanup.append(aff_grp) + + + diff --git a/test/integration/smoke/test_affinity_groups_projects.py b/test/integration/smoke/test_affinity_groups_projects.py new file mode 100644 index 00000000000..2e971c5033f --- /dev/null +++ b/test/integration/smoke/test_affinity_groups_projects.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from marvin.codes import FAILED +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.lib.utils import * +from marvin.lib.base import * +from marvin.lib.common import * +from marvin.sshClient import SshClient +from nose.plugins.attrib import attr + +class TestDeployVmWithAffinityGroup(cloudstackTestCase): + """ + This test deploys a virtual machine for a project + using the small service offering and builtin template + """ + + @classmethod + def setUpClass(cls): + cls.testClient = super(TestDeployVmWithAffinityGroup, cls).getClsTestClient() + zone_name = cls.testClient.getZoneForTests() + cls.apiclient = cls.testClient.getApiClient() + cls.domain = get_domain(cls.apiclient) + cls.services = cls.testClient.getParsedTestDataConfig() + # Get Zone, Domain and templates + cls.zone = get_zone(cls.apiclient, cls.testClient.getZoneForTests()) + + cls.template = get_template( + cls.apiclient, + cls.zone.id, + cls.services["ostype"] + ) + + if cls.template == FAILED: + assert False, "get_template() failed to return template with description %s" % cls.services["ostype"] + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + domainid=cls.domain.id + ) + + projectData = { + "name": "Project", + "displaytext": "Test project", + } + + cls.project = Project.create( + cls.apiclient, + projectData, + account=cls.account.name, + domainid=cls.account.domainid + ) + + # Add user to the project + cls.project.addAccount( + cls.apiclient, + cls.account.name + ) + + cls.service_offering = ServiceOffering.create( + cls.apiclient, + cls.services["service_offerings"]["tiny"] + ) + + cls.ag = AffinityGroup.create(cls.apiclient, cls.services["virtual_machine"]["affinity"],projectid=cls.project.id) + + cls._cleanup = [ + cls.service_offering, + cls.ag, + cls.project, + cls.account, + ] + return + + @attr(tags=["basic", "advanced", "multihost"], required_hardware="false") + def test_DeployVmAntiAffinityGroup_in_project(self): + """ + test DeployVM in anti-affinity groups for project + + deploy VM1 and VM2 in the same host-anti-affinity groups + Verify that the vms are deployed on separate hosts + """ + #deploy VM1 in affinity group created in setUp + vm1 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=self.project.id, + serviceofferingid=self.service_offering.id, + affinitygroupnames=[self.ag.name] + ) + + list_vm1 = list_virtual_machines( + self.apiclient, + id=vm1.id + ) + self.assertEqual( + isinstance(list_vm1, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vm1), + 0, + "Check VM available in List Virtual Machines" + ) + vm1_response = list_vm1[0] + self.assertEqual( + vm1_response.state, + 'Running', + msg="VM is not in Running state" + ) + self.assertEqual( + vm1_response.projectid, + self.project.id, + msg="VM1 is not deployed in project" + ) + host_of_vm1 = vm1_response.hostid + + #deploy VM2 in affinity group created in setUp + vm2 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + projectid=self.project.id, + serviceofferingid=self.service_offering.id, + affinitygroupnames=[self.ag.name] + ) + list_vm2 = list_virtual_machines( + self.apiclient, + id=vm2.id + ) + self.assertEqual( + isinstance(list_vm2, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vm2), + 0, + "Check VM available in List Virtual Machines" + ) + vm2_response = list_vm2[0] + self.assertEqual( + vm2_response.state, + 'Running', + msg="VM is not in Running state" + ) + self.assertEqual( + vm2_response.projectid, + self.project.id, + msg="VM2 is not deployed in project" + ) + host_of_vm2 = vm2_response.hostid + + self.assertNotEqual(host_of_vm1, host_of_vm2, + msg="Both VMs of affinity group %s are on the same host" % self.ag.name) + + + @classmethod + def tearDownClass(cls): + try: + #Clean up, terminate the created templates + cleanup_resources(cls.apiclient, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) diff --git a/test/integration/smoke/test_privategw_acl.py b/test/integration/smoke/test_privategw_acl.py index e1dabc48741..9b85fe8732d 100644 --- a/test/integration/smoke/test_privategw_acl.py +++ b/test/integration/smoke/test_privategw_acl.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +from marvin.cloudstackAPI.createStaticRoute import createStaticRouteCmd """ Tests for Network ACLs in VPC """ #Import Local Modules @@ -87,6 +88,27 @@ class Services: "NetworkACL": 'VpcVirtualRouter' }, }, + "redundant_vpc_offering": { + "name": 'Redundant VPC off', + "displaytext": 'Redundant VPC off', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Lb,UserData,StaticNat', + "serviceProviderList": { + "Vpn": 'VpcVirtualRouter', + "Dhcp": 'VpcVirtualRouter', + "Dns": 'VpcVirtualRouter', + "SourceNat": 'VpcVirtualRouter', + "PortForwarding": 'VpcVirtualRouter', + "Lb": 'VpcVirtualRouter', + "UserData": 'VpcVirtualRouter', + "StaticNat": 'VpcVirtualRouter', + "NetworkACL": 'VpcVirtualRouter' + }, + "serviceCapabilityList": { + "SourceNat": { + "RedundantRouter": 'true' + } + }, + }, "vpc_offering": { "name": "VPC off", "displaytext": "VPC off", @@ -112,6 +134,14 @@ class Services: "publicport": 22, "protocol": 'TCP', }, + "natrule": { + "privateport": 22, + "publicport": 22, + "startport": 22, + "endport": 22, + "protocol": "TCP", + "cidrlist": '0.0.0.0/0', + }, "ostype": 'CentOS 5.3 (64-bit)', "timeout": 10, } @@ -128,10 +158,12 @@ class TestPrivateGwACL(cloudstackTestCase): # Get Zone, Domain and templates cls.domain = get_domain(cls.api_client) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype cls.template = get_template( cls.api_client, cls.zone.id, cls.services["ostype"]) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id cls.services["virtual_machine"]["template"] = cls.template.id @@ -156,32 +188,14 @@ class TestPrivateGwACL(cloudstackTestCase): def setUp(self): self.apiclient = self.testClient.getApiClient() - self.logger.debug("Creating Admin Account for Domain ID ==> %s" %self.domain.id) + self.logger.debug("Creating Admin Account for Domain ID ==> %s" % self.domain.id) self.account = Account.create( self.apiclient, self.services["account"], admin=True, domainid=self.domain.id) - self.logger.debug("Creating a VPC offering..") - self.vpc_off = VpcOffering.create( - self.apiclient, - self.services["vpc_offering"]) - - self.logger.debug("Enabling the VPC offering created") - self.vpc_off.update(self.apiclient, state='Enabled') - - self.logger.debug("Creating a VPC network in the account: %s" % self.account.name) - self.services["vpc"]["cidr"] = '10.1.1.1/16' - self.vpc = VPC.create( - self.apiclient, - self.services["vpc"], - vpcofferingid=self.vpc_off.id, - zoneid=self.zone.id, - account=self.account.name, - domainid=self.account.domainid) - - self.cleanup = [self.vpc, self.vpc_off, self.account] + self.cleanup = [] return def tearDown(self): @@ -191,41 +205,203 @@ class TestPrivateGwACL(cloudstackTestCase): raise Exception("Warning: Exception during cleanup : %s" % e) return - @attr(tags=["advanced"], required_hardware="false") - def test_privategw_acl(self): - self.createACL() - self.createACLItem() - self.createNetwork() - self.createPvtGw() - self.replaceacl() + def _replaceAcl(self, command): + try: + successResponse = self.apiclient.replaceNetworkACLList(command); + except Exception as e: + self.fail("Failed to replace ACL list due to %s" % e) - def createACL(self): + self.assertTrue(successResponse.success, "Failed to replace ACL list.") + + @attr(tags=["advanced"], required_hardware="true") + def test_01_vpc_privategw_acl(self): + self.logger.debug("Creating a VPC offering..") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"]) + + self.logger.debug("Enabling the VPC offering created") + vpc_off.update(self.apiclient, state='Enabled') + + vpc = self.createVPC(vpc_off) + + self.cleanup = [vpc, vpc_off, self.account] + + physical_networks = get_physical_networks(self.apiclient, self.zone.id) + if not physical_networks: + self.fail("No Physical Networks found!") + + vlans = physical_networks[0].vlan.split('-') + vlan_1 = int(vlans[0]) + + acl = self.createACL(vpc) + self.createACLItem(acl.id) + self.createNetwork(vpc) + privateGw = self.createPvtGw(vpc, "10.0.3.99", "10.0.3.100", acl.id, vlan_1) + self.replacePvtGwACL(acl.id, privateGw.id) + + @attr(tags=["advanced"], required_hardware="true") + def test_02_vpc_privategw_static_routes(self): + + self.logger.debug("Creating a VPC offering..") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["vpc_offering"]) + + self.logger.debug("Enabling the VPC offering created") + vpc_off.update(self.apiclient, state='Enabled') + + self.performVPCTests(vpc_off) + + @attr(tags=["advanced"], required_hardware="true") + def test_03_rvpc_privategw_static_routes(self): + self.skipTest("Redundant VPC Routers have to be fixed. Private Gateway not working yet.") + + self.logger.debug("Creating a Redundant VPC offering..") + vpc_off = VpcOffering.create( + self.apiclient, + self.services["redundant_vpc_offering"]) + + self.logger.debug("Enabling the Redundant VPC offering created") + vpc_off.update(self.apiclient, state='Enabled') + + self.performVPCTests(vpc_off) + + def performVPCTests(self, vpc_off): + + self.logger.debug("Creating VPCs with offering ID %s" % vpc_off.id) + vpc_1 = self.createVPC(vpc_off, cidr = '10.0.1.0/24') + vpc_2 = self.createVPC(vpc_off, cidr = '10.0.2.0/24') + + self.cleanup = [vpc_1, vpc_2, vpc_off, self.account] + + physical_networks = get_physical_networks(self.apiclient, self.zone.id) + if not physical_networks: + self.fail("No Physical Networks found!") + + vlans = physical_networks[0].vlan.split('-') + vlan_1 = int(vlans[0]) + + network_1 = self.createNetwork(vpc_1, gateway = '10.0.1.1') + network_2 = self.createNetwork(vpc_2, gateway = '10.0.2.1') + + vm1 = self.createVM(network_1) + vm2 = self.createVM(network_2) + + self.cleanup.insert(0, vm1) + self.cleanup.insert(0, vm2) + + acl1 = self.createACL(vpc_1) + self.createACLItem(acl1.id, cidr = "0.0.0.0/0") + privateGw_1 = self.createPvtGw(vpc_1, "10.0.3.100", "10.0.3.101", acl1.id, vlan_1) + self.replacePvtGwACL(acl1.id, privateGw_1.id) + + acl2 = self.createACL(vpc_2) + self.createACLItem(acl2.id, cidr = "0.0.0.0/0") + privateGw_2 = self.createPvtGw(vpc_2, "10.0.3.101", "10.0.3.100", acl2.id, vlan_1) + self.replacePvtGwACL(acl2.id, privateGw_2.id) + + self.replaceNetworkAcl(acl1.id, network_1) + self.replaceNetworkAcl(acl2.id, network_2) + + staticRoute_1 = self.createStaticRoute(privateGw_1.id, cidr = '10.0.2.0/24') + staticRoute_2 = self.createStaticRoute(privateGw_2.id, cidr = '10.0.1.0/24') + + public_ip_1 = self.acquire_publicip(vpc_1, network_1) + public_ip_2 = self.acquire_publicip(vpc_2, network_2) + + nat_rule_1 = self.create_natrule(vpc_1, vm1, public_ip_1, network_1) + nat_rule_2 = self.create_natrule(vpc_2, vm2, public_ip_2, network_2) + + self.check_pvt_gw_connectivity(vm1, public_ip_1, vm2.nic[0].ipaddress) + self.check_pvt_gw_connectivity(vm2, public_ip_2, vm1.nic[0].ipaddress) + + def createVPC(self, vpc_offering, cidr = '10.1.1.1/16'): + try: + self.logger.debug("Creating a VPC network in the account: %s" % self.account.name) + self.services["vpc"]["cidr"] = cidr + + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=vpc_offering.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid) + + self.logger.debug("Created VPC with ID: %s" % vpc.id) + except Exception, e: + self.fail('Unable to create VPC due to %s ' % e) + + return vpc + + def createVM(self, network): + try: + self.logger.debug('Creating VM in network=%s' % network.name) + vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)] + ) + self.logger.debug("Created VM with ID: %s" % vm.id) + except Exception, e: + self.fail('Unable to create virtual machine due to %s ' % e) + + return vm + + def createStaticRoute(self, privateGwId, cidr = '10.0.0.0/16'): + staticRouteCmd = createStaticRoute.createStaticRouteCmd() + staticRouteCmd.cidr = cidr + staticRouteCmd.gatewayid = privateGwId + + try: + staticRoute = self.apiclient.createStaticRoute(staticRouteCmd) + self.assertIsNotNone(staticRoute.id, "Failed to create static route.") + + self.logger.debug("Created staticRoute with ID: %s" % staticRoute.id) + except Exception, e: + self.fail('Unable to create static route due to %s ' % e) + + return staticRoute + + def createACL(self, vpc): createAclCmd = createNetworkACLList.createNetworkACLListCmd() - createAclCmd.name = "acl1" - createAclCmd.description = "new acl" - createAclCmd.vpcid = self.vpc.id - createAclResponse = self.apiclient.createNetworkACLList(createAclCmd) + createAclCmd.name = "ACL-Test-%s" % vpc.id + createAclCmd.description = createAclCmd.name + createAclCmd.vpcid = vpc.id + try: + acl = self.apiclient.createNetworkACLList(createAclCmd) + self.assertIsNotNone(acl.id, "Failed to create ACL.") - self.aclId = createAclResponse.id + self.logger.debug("Created ACL with ID: %s" % acl.id) + except Exception, e: + self.fail('Unable to create ACL due to %s ' % e) - self.assertIsNotNone(self.aclId, "Failed to create ACL.") + return acl - def createACLItem(self): + def createACLItem(self, aclId, cidr = "0.0.0.0/0"): createAclItemCmd = createNetworkACL.createNetworkACLCmd() - createAclItemCmd.cidr = "0.0.0.0/0" - createAclItemCmd.protocol = "TCP" + createAclItemCmd.cidr = cidr + createAclItemCmd.protocol = "All" createAclItemCmd.number = "1" - createAclItemCmd.action = "Deny" - createAclItemCmd.aclid = self.aclId - createAclItemResponse = self.apiclient.createNetworkACL(createAclItemCmd) + createAclItemCmd.action = "Allow" + createAclItemCmd.aclid = aclId + try: + aclItem = self.apiclient.createNetworkACL(createAclItemCmd) + self.assertIsNotNone(aclItem.id, "Failed to create ACL item.") - self.assertIsNotNone(createAclItemResponse.id, "Failed to create ACL item.") + self.logger.debug("Created ACL Item ID: %s" % aclItem.id) + except Exception, e: + self.fail('Unable to create ACL Item due to %s ' % e) - def createNetwork(self): + def createNetwork(self, vpc, gateway = '10.1.1.1'): try: self.logger.debug('Create NetworkOffering') net_offerring = self.services["network_offering"] - net_offerring["name"] = "NET_OFF-10.1.1.1" + net_offerring["name"] = "NET_OFF-%s" % gateway nw_off = NetworkOffering.create( self.apiclient, net_offerring, @@ -235,7 +411,7 @@ class TestPrivateGwACL(cloudstackTestCase): self.logger.debug('Created and Enabled NetworkOffering') - self.services["network"]["name"] = "NETWORK-10.1.1.1" + self.services["network"]["name"] = "NETWORK-%s" % gateway self.logger.debug('Adding Network=%s' % self.services["network"]) obj_network = Network.create( @@ -245,43 +421,125 @@ class TestPrivateGwACL(cloudstackTestCase): domainid=self.account.domainid, networkofferingid=nw_off.id, zoneid=self.zone.id, - gateway="10.1.1.1", - vpcid=self.vpc.id + gateway=gateway, + vpcid=vpc.id ) self.logger.debug("Created network with ID: %s" % obj_network.id) except Exception, e: self.fail('Unable to create a Network with offering=%s because of %s ' % (net_offerring, e)) - self.network = obj_network - self.cleanup.insert(0, nw_off) self.cleanup.insert(0, obj_network) - def createPvtGw(self): + return obj_network + + def createPvtGw(self, vpc, ip_address, gateway, aclId, vlan): + physical_networks = get_physical_networks(self.apiclient, self.zone.id) + if not physical_networks: + self.fail("No Physical Networks found!") + + self.logger.debug('::: Physical Networks ::: ==> %s' % physical_networks) + createPrivateGatewayCmd = createPrivateGateway.createPrivateGatewayCmd() - createPrivateGatewayCmd.physicalnetworkid = 200 - createPrivateGatewayCmd.gateway = "10.147.30.1" + createPrivateGatewayCmd.physicalnetworkid = physical_networks[0].id + createPrivateGatewayCmd.gateway = gateway createPrivateGatewayCmd.netmask = "255.255.255.0" - createPrivateGatewayCmd.ipaddress = "10.147.30.200" - createPrivateGatewayCmd.vlan = "30" - createPrivateGatewayCmd.vpcid = self.vpc.id - createPrivateGatewayCmd.sourcenatsupported = "true" - createPrivateGatewayCmd.aclid = self.aclId + createPrivateGatewayCmd.ipaddress = ip_address + createPrivateGatewayCmd.vlan = vlan + createPrivateGatewayCmd.vpcid = vpc.id + createPrivateGatewayCmd.sourcenatsupported = "false" + createPrivateGatewayCmd.aclid = aclId try: - privateGatewayResponse = self.apiclient.createPrivateGateway(createPrivateGatewayCmd) + privateGw = self.apiclient.createPrivateGateway(createPrivateGatewayCmd) except Exception as e: self.fail("Failed to create Private Gateway ==> %s" % e) - self.privateGwId = privateGatewayResponse.id + self.assertIsNotNone(privateGw.id, "Failed to create ACL.") + + return privateGw - self.assertIsNotNone(self.privateGwId, "Failed to create ACL.") + def replaceNetworkAcl(self, aclId, network): + self.logger.debug("Replacing Network ACL with ACL ID ==> %s" % aclId) - def replaceacl(self): replaceNetworkACLListCmd = replaceNetworkACLList.replaceNetworkACLListCmd() - replaceNetworkACLListCmd.aclid = self.aclId - replaceNetworkACLListCmd.gatewayid = self.privateGwId - successResponse = self.apiclient.replaceNetworkACLList(replaceNetworkACLListCmd); + replaceNetworkACLListCmd.aclid = aclId + replaceNetworkACLListCmd.networkid = network.id - self.assertTrue(successResponse.success, "Failed to replace ACL list.") + self._replaceAcl(replaceNetworkACLListCmd) + + def replacePvtGwACL(self, aclId, privateGwId): + self.logger.debug("Replacing Private GW ACL with ACL ID ==> %s" % aclId) + + replaceNetworkACLListCmd = replaceNetworkACLList.replaceNetworkACLListCmd() + replaceNetworkACLListCmd.aclid = aclId + replaceNetworkACLListCmd.gatewayid = privateGwId + + self._replaceAcl(replaceNetworkACLListCmd) + + def acquire_publicip(self, vpc, network): + self.logger.debug("Associating public IP for network: %s" % network.name) + public_ip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + networkid=network.id, + vpcid=vpc.id + ) + self.logger.debug("Associated %s with network %s" % ( + public_ip.ipaddress.ipaddress, + network.id + )) + + return public_ip + + def create_natrule(self, vpc, virtual_machine, public_ip, network): + self.logger.debug("Creating NAT rule in network for vm with public IP") + + nat_service = self.services["natrule"] + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + nat_service, + ipaddressid=public_ip.ipaddress.id, + openfirewall=False, + networkid=network.id, + vpcid=vpc.id) + + self.logger.debug("Adding NetworkACL rules to make NAT rule accessible") + nwacl_nat = NetworkACL.create( + self.apiclient, + networkid=network.id, + services=nat_service, + traffictype='Ingress' + ) + self.logger.debug('nwacl_nat=%s' % nwacl_nat.__dict__) + + return nat_rule + + def check_pvt_gw_connectivity(self, virtual_machine, public_ip, vm_ip): + ssh_command = "ping -c 3 %s" % vm_ip + + # Should be able to SSH VM + result = 'failed' + try: + self.logger.debug("SSH into VM: %s" % public_ip.ipaddress.ipaddress) + + ssh = virtual_machine.get_ssh_client(ipaddress=public_ip.ipaddress.ipaddress) + + self.logger.debug("Ping to VM inside another VPC") + result = str(ssh.execute(ssh_command)) + + self.logger.debug("SSH result: %s; COUNT is ==> %s" % (result, result.count("3 packets received"))) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (vmObj.get_ip(), e) + ) + + self.assertEqual( + result.count("3 packets received"), + 1, + "Ping to outside world from VM should be successful" + ) diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 32e2d5915db..4b04e778bd4 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -4220,7 +4220,7 @@ class AffinityGroup: self.__dict__.update(items) @classmethod - def create(cls, apiclient, aff_grp, account=None, domainid=None): + def create(cls, apiclient, aff_grp, account=None, domainid=None, projectid=None): cmd = createAffinityGroup.createAffinityGroupCmd() cmd.name = aff_grp['name'] cmd.displayText = aff_grp['name'] @@ -4229,6 +4229,8 @@ class AffinityGroup: cmd.account = account if domainid: cmd.domainid = domainid + if projectid: + cmd.projectid = projectid return AffinityGroup(apiclient.createAffinityGroup(cmd).__dict__) def update(self, apiclient): diff --git a/tools/marvin/marvin/lib/common.py b/tools/marvin/marvin/lib/common.py index fb21073658d..fa45299030b 100644 --- a/tools/marvin/marvin/lib/common.py +++ b/tools/marvin/marvin/lib/common.py @@ -259,6 +259,17 @@ def get_zone(apiclient, zone_name=None, zone_id=None): ''' return cmd_out[0] +def get_physical_networks(apiclient, zoneid): + ''' + @name : get_physical_networks + @Desc :Returns A list of the Physical Networks in the given Zone + @Input : zoneid: The Zone ID + @Output : 1. A list containing the Physical Networks + ''' + cmd = listPhysicalNetworks.listPhysicalNetworksCmd() + cmd.zoneid = zoneid + physical_networks = apiclient.listPhysicalNetworks(cmd) + return physical_networks def get_pod(apiclient, zone_id=None, pod_id=None, pod_name=None): '''