CLOUDSTACK-7985: (1) allow migrate vm from/to project; (2) UI change for selecting account/project/network

This commit is contained in:
Wei Zhou 2015-09-16 14:43:55 +02:00
parent 0db4471be0
commit a1d2fba1d2
6 changed files with 239 additions and 20 deletions

View File

@ -29,6 +29,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.NetworkResponse;
import org.apache.cloudstack.api.response.ProjectResponse;
import org.apache.cloudstack.api.response.SecurityGroupResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
@ -58,12 +59,15 @@ public class AssignVMCmd extends BaseCmd {
description = "id of the VM to be moved")
private Long virtualMachineId;
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "account name of the new VM owner.")
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account name of the new VM owner.")
private String accountName;
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, required = true, description = "domain id of the new VM owner.")
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain id of the new VM owner.")
private Long domainId;
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the new VM owner.")
private Long projectId;
//Network information
@Parameter(name = ApiConstants.NETWORK_IDS,
type = CommandType.LIST,
@ -98,6 +102,10 @@ public class AssignVMCmd extends BaseCmd {
return domainId;
}
public Long getProjectId() {
return projectId;
}
public List<Long> getNetworkIds() {
return networkIds;
}

View File

@ -56,6 +56,10 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou
@Param(description = "the account name of the project's owner")
private String ownerName;
@SerializedName("projectaccountname")
@Param(description="the project account name of the project")
private String projectAccountName;
@SerializedName(ApiConstants.STATE)
@Param(description = "the state of the project")
private String state;
@ -228,6 +232,10 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou
ownerName = owner;
}
public void setProjectAccountName(String projectAccountName) {
this.projectAccountName = projectAccountName;
}
public void setState(String state) {
this.state = state;
}

View File

@ -93,6 +93,7 @@ public class ProjectJoinDaoImpl extends GenericDaoBase<ProjectJoinVO, Long> impl
Account account = _accountDao.findByIdIncludingRemoved(proj.getProjectAccountId());
AccountJoinVO accountJn = ApiDBUtils.newAccountView(account);
_accountJoinDao.setResourceLimits(accountJn, false, response);
response.setProjectAccountName(accountJn.getAccountName());
response.setObjectName("project");
return response;

View File

@ -5048,14 +5048,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (oldAccount == null) {
throw new InvalidParameterValueException("Invalid account for VM " + vm.getAccountId() + " in domain.");
}
// don't allow to move the vm from the project
if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) {
InvalidParameterValueException ex = new InvalidParameterValueException("Specified Vm id belongs to the project and can't be moved");
ex.addProxyObject(vm.getUuid(), "vmId");
throw ex;
}
final Account newAccount = _accountService.getActiveAccountByName(cmd.getAccountName(), cmd.getDomainId());
if (newAccount == null || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) {
final Account newAccount = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
if (newAccount == null) {
throw new InvalidParameterValueException("Invalid accountid=" + cmd.getAccountName() + " in domain " + cmd.getDomainId());
}
@ -5356,7 +5350,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
s_logger.debug("AssignVM: Advance virtual, adding networks no " + networks.size() + " to " + vm.getInstanceName());
} // END IF NON SEC GRP ENABLED
} // END IF ADVANCED
s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + cmd.getAccountName());
s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + newAccount.getAccountName());
return vm;
}

View File

@ -690,7 +690,7 @@ public class UserVmManagerTest {
when(_accountService.getActiveAccountById(anyLong())).thenReturn(oldAccount);
when(_accountService.getActiveAccountByName(anyString(), anyLong())).thenReturn(newAccount);
when(_accountMgr.finalizeOwner(any(Account.class), anyString(), anyLong(), anyLong())).thenReturn(newAccount);
doThrow(new PermissionDeniedException("Access check failed")).when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class),
any(ControlledEntity.class));

View File

@ -1905,7 +1905,32 @@
label: 'label.assign.instance.another',
createForm: {
title: 'label.assign.instance.another',
desc: 'Please specify the account type, domain, account name and network (optional) of the new account. <br> If the default nic of the vm is on a shared network, CloudStack will check if the network can be used by the new account if you do not specify one network. <br> If the default nic of the vm is on a isolated network, and the new account has more one isolated networks, you should specify one.',
fields: {
accountType: {
label: 'Account Type',
select: function(args) {
var items = [];
items.push({id: 'account', description: 'Account'});
items.push({id: 'project', description: 'Project'});
args.response.success({data: items});
args.$select.change(function() {
var $form = $(this).closest('form');
var $account = $form.find('.form-item[rel=account]');
var $project = $form.find('.form-item[rel=project]');
var accountType = $(this).val();
if (accountType == 'account') { // Account
$account.css('display', 'inline-block');
$project.hide();
} else if (accountType == 'project') { // Project
$project.css('display', 'inline-block');
$account.hide();
}
});
}
},
domainid: {
label: 'label.domain',
validation: {
@ -1941,20 +1966,203 @@
},
account: {
label: 'label.account',
dependsOn: 'domainid',
validation: {
required: true
}
}
},
select: function(args) {
var dataObj = {
domainId: args.domainid,
state: 'Enabled',
listAll: true,
};
$.ajax({
url: createURL('listAccounts', {
ignoreProject: true
}),
data: dataObj,
success: function(json) {
accountObjs = json.listaccountsresponse.account;
var items = [{
id: null,
description: ''
}];
$(accountObjs).each(function() {
items.push({
id: this.name,
description: this.name
});
})
args.response.success({
data: items
});
}
});
},
},
project: {
label: 'label.project',
dependsOn: 'domainid',
validation: {
required: true
},
select: function(args) {
var dataObj = {
domainId: args.domainid,
state: 'Active',
listAll: true,
};
$.ajax({
url: createURL('listProjects', {
ignoreProject: true
}),
data: dataObj,
success: function(json) {
projectObjs = json.listprojectsresponse.project;
var items = [{
id: null,
description: ''
}];
$(projectObjs).each(function() {
items.push({
id: this.id,
description: this.name
});
})
args.response.success({
data: items
});
}
});
},
},
network: {
label: 'label.network',
dependsOn: ['accountType', 'domainid', 'account', 'project'],
select: function(args) {
var dataObj = {
domainId: args.domainid,
listAll: true,
isrecursive: false
};
if (args.data.accountType == 'account' && args.data.account != null && args.data.account != '') {
$.extend(dataObj, {
account: args.data.account
});
} else if (args.data.accountType == 'project' && args.data.project != null && args.data.project != '') {
$.extend(dataObj, {
projectid: args.data.project
});
} else {
args.response.success({
data: null
});
return;
}
$.ajax({
url: createURL('listNetworks', {
ignoreProject: true
}),
data: dataObj,
success: function(json) {
var networkObjs = json.listnetworksresponse.network;
var items = [{
id: null,
description: ''
}];
$(networkObjs).each(function() {
items.push({
id: this.id,
description: this.name
});
})
args.response.success({
data: items
});
}
});
},
},
securitygroup: {
label: 'label.security.group',
dependsOn: ['accountType', 'domainid', 'account', 'project'],
select: function(args) {
var dataObj = {
domainId: args.domainid,
listAll: true,
isrecursive: false
};
if (args.data.accountType == 'account' && args.data.account != null && args.data.account != '') {
$.extend(dataObj, {
account: args.data.account
});
} else if (args.data.accountType == 'project' && args.data.project != null && args.data.project != '') {
$.extend(dataObj, {
projectid: args.data.project
});
} else {
args.response.success({
data: null
});
return;
}
$.ajax({
url: createURL('listSecurityGroups', {
ignoreProject: true
}),
data: dataObj,
success: function(json) {
var sgObjs = json.listsecuritygroupsresponse.securitygroup;
var items = [{
id: null,
description: ''
}];
$(sgObjs).each(function() {
items.push({
id: this.id,
description: this.name
});
})
args.response.success({
data: items
});
}
});
},
},
}
},
action: function(args) {
$.ajax({
url: createURL('assignVirtualMachine'),
data: {
virtualmachineid: args.context.instances[0].id,
domainid: args.data.domainid,
var dataObj = {
virtualmachineid: args.context.instances[0].id,
domainid: args.data.domainid,
};
var ignoreProject = false;
if (args.data.accountType == 'account') {
ignoreProject = true;
$.extend(dataObj, {
account: args.data.account
},
});
} else if (args.data.accountType == 'project') {
$.extend(dataObj, {
projectid: args.data.project
});
}
if (args.data.network != null && args.data.network != '') {
$.extend(dataObj, {
networkIds: args.data.network
});
}
$.ajax({
url: createURL('assignVirtualMachine', {
ignoreProject: ignoreProject
}),
data: dataObj,
success: function(json) {
var item = json.assignvirtualmachineresponse.virtualmachine;
args.response.success({