Merge pull request #1134 from pdube/CLOUDSTACK-6276

CLOUDSTACK-6276 Fixing affinity groups for projectsWith some contributions from @resmo and @ustcweizhou.
This closes https://github.com/apache/cloudstack/pull/508

To test manually (need at least 2 hosts):
Create a project
Create an affinity group in that project
Deploy a vm with that affinity group
Deploy a second vm with that affinity group
They should be on different hosts

Ran old and new tests for affinity groups on the simulator

Test create affinity group as admin in project ... === TestName: test_01_admin_create_aff_grp_for_project | Status : SUCCESS ===
ok
Test create affinity group as domain admin for projects ... === TestName: test_02_doadmin_create_aff_grp_for_project | Status : SUCCESS ===
ok
Test create affinity group as user for projects ... === TestName: test_03_user_create_aff_grp_for_project | Status : SUCCESS ===
ok
Test create affinity group that exists (same name) for projects ... === TestName: test_4_user_create_aff_grp_existing_name_for_project | Status : SUCCESS ===
ok
#Delete Affinity Group by id. ... === TestName: test_01_delete_aff_grp_by_id | Status : SUCCESS ===
ok
#Delete Affinity Group by id should fail for user not in project ... === TestName: test_02_delete_aff_grp_by_id_another_user | Status : SUCCESS ===
ok
test DeployVM in anti-affinity groups ... === TestName: test_01_deploy_vm_anti_affinity_group | Status : SUCCESS ===
ok
test DeployVM in anti-affinity groups with more vms than hosts. ... === TestName: test_02_deploy_vm_anti_affinity_group_fail_on_not_enough_hosts | Status : SUCCESS ===
ok
List affinity group for a vm for projects ... === TestName: test_01_list_aff_grps_for_vm | Status : SUCCESS ===
ok
List multiple affinity groups associated with a vm for projects ... === TestName: test_02_list_multiple_aff_grps_for_vm | Status : SUCCESS ===
ok
List affinity groups by id for projects ... === TestName: test_03_list_aff_grps_by_id | Status : SUCCESS ===
ok
List Affinity Groups by name for projects ... === TestName: test_04_list_aff_grps_by_name | Status : SUCCESS ===
ok
List Affinity Groups by non-existing id for projects ... === TestName: test_05_list_aff_grps_by_non_existing_id | Status : SUCCESS ===
ok
List Affinity Groups by non-existing name for projects ... === TestName: test_06_list_aff_grps_by_non_existing_name | Status : SUCCESS ===
ok
List affinity group should list all for a vms associated with that group for projects ... === TestName: test_07_list_all_vms_in_aff_grp | Status : SUCCESS ===
ok
Update the list of affinityGroups by using affinity groupids ... === TestName: test_01_update_aff_grp_by_ids | Status : SUCCESS ===
ok

----------------------------------------------------------------------
Ran 16 tests in 581.706s

OK

Deploy vm as Admin in Affinity Group belonging to regular user (should fail) ... === TestName: test_01_deploy_vm_another_user | Status : SUCCESS ===
ok
Create Affinity Group as admin for regular user ... === TestName: test_02_create_aff_grp_user | Status : SUCCESS ===
ok
List Affinity Groups as admin for all the users ... === TestName: test_03_list_aff_grp_all_users | Status : SUCCESS ===
ok
List Affinity Groups belonging to admin user ... === TestName: test_04_list_all_admin_aff_grp | Status : SUCCESS ===
ok
List Affinity Groups belonging to regular user passing account id and domain id ... === TestName: test_05_list_all_users_aff_grp | Status : SUCCESS ===
ok
List Affinity Groups belonging to regular user passing group id ... === TestName: test_06_list_all_users_aff_grp_by_id | Status : SUCCESS ===
ok
Delete Affinity Group belonging to regular user ... === TestName: test_07_delete_aff_grp_of_other_user | Status : SUCCESS ===
ok
Test create affinity group as admin ... === TestName: test_01_admin_create_aff_grp | Status : SUCCESS ===
ok
Test create affinity group as domain admin ... === TestName: test_02_doadmin_create_aff_grp | Status : SUCCESS ===
ok
Test create affinity group as user ... === TestName: test_03_user_create_aff_grp | Status : SUCCESS ===
ok
Test create affinity group that exists (same name) ... === TestName: test_04_user_create_aff_grp_existing_name | Status : SUCCESS ===
ok
Test create affinity group with existing name but within different account ... === TestName: test_05_create_aff_grp_same_name_diff_acc | Status : SUCCESS ===
ok
Test create affinity group of non-existing type ... === TestName: test_06_create_aff_grp_nonexisting_type | Status : SUCCESS ===
ok
Delete Affinity Group by name ... === TestName: test_01_delete_aff_grp_by_name | Status : SUCCESS ===
ok
Delete Affinity Group as admin for an account ... === TestName: test_02_delete_aff_grp_for_acc | Status : SUCCESS ===
ok
Delete Affinity Group which has vms in it ... === TestName: test_03_delete_aff_grp_with_vms | Status : SUCCESS ===
ok
Delete Affinity Group with id which does not belong to this user ... === TestName: test_05_delete_aff_grp_id | Status : SUCCESS ===
ok
Delete Affinity Group by name which does not belong to this user ... === TestName: test_06_delete_aff_grp_name | Status : SUCCESS ===
ok
Delete Affinity Group by id. ... === TestName: test_08_delete_aff_grp_by_id | Status : SUCCESS ===
ok
Root admin should be able to delete affinity group of other users ... === TestName: test_09_delete_aff_grp_root_admin | Status : SUCCESS ===
ok
Deploy VM without affinity group ... === TestName: test_01_deploy_vm_without_aff_grp | Status : SUCCESS ===
ok
Deploy VM by aff grp name ... === TestName: test_02_deploy_vm_by_aff_grp_name | Status : SUCCESS ===
ok
Deploy VM by aff grp id ... === TestName: test_03_deploy_vm_by_aff_grp_id | Status : SUCCESS ===
ok
test DeployVM in anti-affinity groups ... === TestName: test_04_deploy_vm_anti_affinity_group | Status : SUCCESS ===
ok
Deploy vms by affinity group id ... === TestName: test_05_deploy_vm_by_id | Status : SUCCESS ===
ok
Deploy vm in affinity group of another user by name ... === TestName: test_06_deploy_vm_aff_grp_of_other_user_by_name | Status : SUCCESS ===
ok
Deploy vm in affinity group of another user by id ... === TestName: test_07_deploy_vm_aff_grp_of_other_user_by_id | Status : SUCCESS ===
ok
Deploy vm in multiple affinity groups ... === TestName: test_08_deploy_vm_multiple_aff_grps | Status : SUCCESS ===
ok
Deploy multiple vms in multiple affinity groups ... === TestName: test_09_deploy_vm_multiple_aff_grps | Status : SUCCESS ===
ok
Deploy VM by aff grp name and id ... === TestName: test_10_deploy_vm_by_aff_grp_name_and_id | Status : SUCCESS ===
ok
List affinity group for a vm ... === TestName: test_01_list_aff_grps_for_vm | Status : SUCCESS ===
ok
List multiple affinity groups associated with a vm ... === TestName: test_02_list_multiple_aff_grps_for_vm | Status : SUCCESS ===
ok
List affinity groups by id ... === TestName: test_03_list_aff_grps_by_id | Status : SUCCESS ===
ok
List Affinity Groups by name ... === TestName: test_04_list_aff_grps_by_name | Status : SUCCESS ===
ok
List Affinity Groups by non-existing id ... === TestName: test_05_list_aff_grps_by_non_existing_id | Status : SUCCESS ===
ok
List Affinity Groups by non-existing name ... === TestName: test_06_list_aff_grps_by_non_existing_name | Status : SUCCESS ===
ok
List affinity group should list all for a vms associated with that group ... === TestName: test_07_list_all_vms_in_aff_grp | Status : SUCCESS ===
ok
Update the list of affinityGroups by using affinity groupids ... === TestName: test_01_update_aff_grp_by_ids | Status : SUCCESS ===
ok
Update the list of affinityGroups by using affinity groupnames ... === TestName: test_02_update_aff_grp_by_names | Status : SUCCESS ===
ok
Update the list of affinityGroups for vm which is not associated ... === TestName: test_03_update_aff_grp_for_vm_with_no_aff_grp | Status : SUCCESS ===
ok
Update the list of Affinity Groups to empty list ... SKIP: Skip - Failing - work in progress
Update the list of Affinity Groups on running vm ... === TestName: test_05_update_aff_grp_on_running_vm | Status : SUCCESS ===
ok

----------------------------------------------------------------------
Ran 42 tests in 976.432s

OK (SKIP=1)

* pr/1134:
  CLOUDSTACK-6276 Removing unused parameter in integration test for projects
  CLOUDSTACK-6276 Removing unused parameter in integration test
  CLOUDSTACK-6276 Fixing affinity groups for projects

Signed-off-by: Remi Bergsma <github@remi.nl>
This commit is contained in:
Remi Bergsma 2015-12-03 20:10:15 +01:00
commit 9a21873c4a
24 changed files with 1976 additions and 449 deletions

View File

@ -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<String> 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<String> vmIdList) {

View File

@ -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<List<? extends AffinityGroup>, 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);
}

View File

@ -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());

View File

@ -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);

View File

@ -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<AffinityGroupResponse> response = _queryService.listAffinityGroups(id, affinityGroupName,
affinityGroupType, virtualMachineId, getAccountName(), getDomainId(), isRecursive(),
listAll(), getStartIndex(), getPageSizeVal(), getKeyword());
ListResponse<AffinityGroupResponse> response = _queryService.searchForAffinityGroups(this);
response.setResponseName(getCommandName());
setResponseObject(response);
}
@Override

View File

@ -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<TemplateResponse> listIsos(ListIsosCmd cmd);
ListResponse<AffinityGroupResponse> listAffinityGroups(Long affinityGroupId, String affinityGroupName,
String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive,
boolean listAll, Long startIndex, Long pageSize, String keyword);
ListResponse<AffinityGroupResponse> searchForAffinityGroups(ListAffinityGroupsCmd cmd);
List<ResourceDetailResponse> listResourceDetails(ListResourceDetailsCmd cmd);
@ -142,5 +141,4 @@ public interface QueryService {
ListResponse<StorageTagResponse> searchForStorageTags(ListStorageTagsCmd cmd);
ListResponse<HostTagResponse> searchForHostTags(ListHostTagsCmd cmd);
}

View File

@ -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) {

View File

@ -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<AffinityGroupVO, Long> {
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);
}

View File

@ -23,7 +23,6 @@ import javax.ejb.Local;
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;
@ -136,7 +135,7 @@ public class AffinityGroupDaoImpl extends GenericDaoBase<AffinityGroupVO, Long>
}
@Override
public AffinityGroup findDomainLevelGroupByName(Long domainId, String affinityGroupName) {
public AffinityGroupVO findDomainLevelGroupByName(Long domainId, String affinityGroupName) {
SearchCriteria<AffinityGroupVO> sc = DomainLevelNameSearch.create();
sc.setParameters("aclType", ControlledEntity.ACLType.Domain);
sc.setParameters("name", affinityGroupName);
@ -145,7 +144,7 @@ public class AffinityGroupDaoImpl extends GenericDaoBase<AffinityGroupVO, Long>
}
@Override
public AffinityGroup findByAccountAndType(Long accountId, String type) {
public AffinityGroupVO findByAccountAndType(Long accountId, String type) {
SearchCriteria<AffinityGroupVO> sc = AccountIdTypeSearch.create();
sc.setParameters("accountId", accountId);
sc.setParameters("type", type);
@ -154,7 +153,7 @@ public class AffinityGroupDaoImpl extends GenericDaoBase<AffinityGroupVO, Long>
}
@Override
public AffinityGroup findDomainLevelGroupByType(Long domainId, String type) {
public AffinityGroupVO findDomainLevelGroupByType(Long domainId, String type) {
SearchCriteria<AffinityGroupVO> sc = DomainLevelTypeSearch.create();
sc.setParameters("aclType", ControlledEntity.ACLType.Domain);
sc.setParameters("type", type);

View File

@ -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<DedicatedResourceVO> 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);
}
}

View File

@ -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();

View File

@ -19,17 +19,19 @@ package com.cloud.acl;
import javax.ejb.Local;
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.acl.SecurityChecker;
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;
@ -44,6 +46,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 {
@ -51,8 +57,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);
}
@ -72,6 +77,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;

View File

@ -26,9 +26,6 @@ import java.util.Set;
import javax.ejb.Local;
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;
@ -57,6 +54,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;
@ -196,7 +194,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;
@ -207,6 +204,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;
@ -217,11 +215,12 @@ 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
@Local(value = {QueryService.class})
@ -229,6 +228,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi
public static final Logger s_logger = Logger.getLogger(QueryManagerImpl.class);
private static final String ID_FIELD = "id";
@Inject
private AccountManager _accountMgr;
@ -331,12 +332,6 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi
@Inject
private DomainRouterDao _routerDao;
@Inject
private VolumeDetailsDao _volumeDetailDao;
@Inject
private NicDetailsDao _nicDetailDao;
@Inject
UserVmDetailsDao _userVmDetailDao;
@ -375,11 +370,11 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi
@Inject
AffinityGroupDomainMapDao _affinityGroupDomainMapDao;
@Inject NetworkDetailsDao _networkDetailsDao;
@Inject
ResourceTagDao _resourceTagDao;
@Inject
DataStoreManager dataStoreManager;
@ -3395,55 +3390,60 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi
cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO);
}
@Override
public ListResponse<AffinityGroupResponse> listAffinityGroups(Long affinityGroupId, String affinityGroupName,
String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive,
boolean listAll, Long startIndex, Long pageSize, String keyword) {
Pair<List<AffinityGroupJoinVO>, Integer> result = listAffinityGroupsInternal(affinityGroupId,
affinityGroupName, affinityGroupType, vmId, accountName, domainId, isRecursive, listAll, startIndex,
pageSize, keyword);
public ListResponse<AffinityGroupResponse> searchForAffinityGroups(ListAffinityGroupsCmd cmd) {
Pair<List<AffinityGroupJoinVO>, Integer> result = searchForAffinityGroupsInternal(cmd);
ListResponse<AffinityGroupResponse> response = new ListResponse<AffinityGroupResponse>();
List<AffinityGroupResponse> agResponses = ViewResponseHelper.createAffinityGroupResponses(result.first());
response.setResponses(agResponses, result.second());
return response;
}
public Pair<List<AffinityGroupJoinVO>, 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<List<AffinityGroupJoinVO>, 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<Long> permittedAccounts = new ArrayList<Long>();
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(
domainId, isRecursive, null);
_accountMgr.buildACLSearchParameters(caller, affinityGroupId, accountName, null, permittedAccounts,
domainIdRecursiveListProject, listAll, true);
domainId = domainIdRecursiveListProject.first();
isRecursive = domainIdRecursiveListProject.second();
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
Ternary<Long, Boolean, ListProjectResourcesCriteria> ternary = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, isRecursive, null);
Filter searchFilter = new Filter(AffinityGroupJoinVO.class, "id", true, startIndex, pageSize);
SearchCriteria<AffinityGroupJoinVO> 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<AffinityGroupJoinVO> sc = buildAffinityGroupSearchCriteria(domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria, affinityGroupId,
affinityGroupName, affinityGroupType, keyword);
Pair<List<AffinityGroupJoinVO>, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, searchFilter);
Pair<List<AffinityGroupJoinVO>, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc,
searchFilter);
// search group details by ids
List<AffinityGroupJoinVO> vrs = new ArrayList<AffinityGroupJoinVO>();
List<AffinityGroupJoinVO> affinityGroups = new ArrayList<AffinityGroupJoinVO>();
Integer count = uniqueGroupsPair.second();
if (count.intValue() != 0) {
List<AffinityGroupJoinVO> uniqueGroups = uniqueGroupsPair.first();
@ -3452,36 +3452,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<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive,
new ArrayList<Long>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName,
affinityGroupType, keyword);
vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId));
SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList<Long>(), listProjectResourcesCriteria,
affinityGroupId, affinityGroupName, affinityGroupType, keyword);
affinityGroups.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId));
} else {
for (Long permAcctId : permittedAccounts) {
Account permittedAcct = _accountDao.findById(permAcctId);
SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(
null, isRecursive, new ArrayList<Long>(),
listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword);
SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList<Long>(), 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<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive,
new ArrayList<Long>(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName,
affinityGroupType, keyword);
vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId));
SearchCriteria<AffinityGroupJoinVO> scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList<Long>(), listProjectResourcesCriteria,
affinityGroupId, affinityGroupName, affinityGroupType, keyword);
affinityGroups.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId));
}
return new Pair<List<AffinityGroupJoinVO>, Integer>(vrs, vrs.size());
return new Pair<List<AffinityGroupJoinVO>, Integer>(affinityGroups, affinityGroups.size());
}
@ -3526,9 +3523,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi
}
}
private SearchCriteria<AffinityGroupJoinVO> buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive,
List<Long> permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria,
Long affinityGroupId, String affinityGroupName, String affinityGroupType, String keyword) {
private SearchCriteria<AffinityGroupJoinVO> buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive, List<Long> permittedAccounts,
ListProjectResourcesCriteria listProjectResourcesCriteria, Long affinityGroupId, String affinityGroupName, String affinityGroupType, String keyword) {
SearchBuilder<AffinityGroupJoinVO> groupSearch = _affinityGroupJoinDao.createSearchBuilder();
buildAffinityGroupViewSearchBuilder(groupSearch, domainId, isRecursive, permittedAccounts,
@ -3538,8 +3534,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService, Confi
// distinct
SearchCriteria<AffinityGroupJoinVO> 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);

View File

@ -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;
}

View File

@ -1484,7 +1484,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final List<DedicatedResourceVO> 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);
}
}
}
@ -1702,7 +1702,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
final List<DedicatedResourceVO> 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);
}
}
}
@ -1800,7 +1800,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;

View File

@ -2406,98 +2406,10 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
}
}
// @Override
// public void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List<Long>
// permittedAccounts, Ternary<Long, Boolean, ListProjectResourcesCriteria> 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<Long>
permittedAccounts, Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject,
boolean listAll, boolean forProjectInvitation) {
public void buildACLSearchParameters(Account caller, Long id, String accountName, Long projectId, List<Long> permittedAccounts,
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject, boolean listAll, boolean forProjectInvitation) {
Long domainId = domainIdRecursiveListProject.first();
if (domainId != null) {
Domain domain = _domainDao.findById(domainId);

View File

@ -27,8 +27,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;
@ -36,6 +36,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;
@ -55,8 +56,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;
@ -113,48 +112,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<String, AffinityGroupProcessor> 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<String, AffinityGroupProcessor> 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");
}
@ -163,72 +143,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<AffinityGroupVO>() {
@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<String, Object> params = new HashMap<String, Object>();
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);
@ -237,51 +169,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<AffinityGroupVO>() {
@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<String, Object> params = new HashMap<String, Object>();
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<Class<?>, Long> params = new Pair<Class<?>, 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<AffinityGroupVMMapVO> affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupIdFinal);
List<AffinityGroupVMMapVO> affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupId);
if (!affinityGroupVmMap.isEmpty()) {
SearchBuilder<AffinityGroupVMMapVO> listByAffinityGroup = _affinityGroupVMMapDao.createSearchBuilder();
listByAffinityGroup.and("affinityGroupId", listByAffinityGroup.entity().getAffinityGroupId(), SearchCriteria.Op.EQ);
listByAffinityGroup.done();
SearchCriteria<AffinityGroupVMMapVO> sc = listByAffinityGroup.create();
sc.setParameters("affinityGroupId", affinityGroupIdFinal);
sc.setParameters("affinityGroupId", affinityGroupId);
_affinityGroupVMMapDao.lockRows(sc, null, true);
_affinityGroupVMMapDao.remove(sc);
@ -293,77 +309,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<Class<?>, Long> params = new Pair<Class<?>, 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<List<? extends AffinityGroup>, 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<AffinityGroupVMMapVO> vmInstanceSearch = _affinityGroupVMMapDao.createSearchBuilder();
vmInstanceSearch.and("instanceId", vmInstanceSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
SearchBuilder<AffinityGroupVO> groupSearch = _affinityGroupDao.createSearchBuilder();
SearchCriteria<AffinityGroupVO> 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<List<AffinityGroupVO>, Integer> result = _affinityGroupDao.searchAndCount(sc, searchFilter);
return new Pair<List<? extends AffinityGroup>, Integer>(result.first(), result.second());
}
@Override
@ -460,7 +414,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

View File

@ -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)

View File

@ -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<AffinityGroupProcessor> affinityProcessors = new ArrayList<AffinityGroupProcessor>();
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<Long> affinityGroupIds = new ArrayList<Long>();
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);
}
}
}
}

View File

@ -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`;

View File

@ -64,7 +64,7 @@ class Services:
"type": "host anti-affinity",
},
"virtual_machine" : {
"hypervisor" : "KVM",
},
"new_domain": {
"name": "New Domain",

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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):