Skip to content

Commit a1d2fba

Browse files
committed
CLOUDSTACK-7985: (1) allow migrate vm from/to project; (2) UI change for selecting account/project/network
1 parent 0db4471 commit a1d2fba

6 files changed

Lines changed: 239 additions & 20 deletions

File tree

api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.cloudstack.api.ServerApiException;
3030
import org.apache.cloudstack.api.response.DomainResponse;
3131
import org.apache.cloudstack.api.response.NetworkResponse;
32+
import org.apache.cloudstack.api.response.ProjectResponse;
3233
import org.apache.cloudstack.api.response.SecurityGroupResponse;
3334
import org.apache.cloudstack.api.response.UserVmResponse;
3435

@@ -58,12 +59,15 @@ public class AssignVMCmd extends BaseCmd {
5859
description = "id of the VM to be moved")
5960
private Long virtualMachineId;
6061

61-
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "account name of the new VM owner.")
62+
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "account name of the new VM owner.")
6263
private String accountName;
6364

64-
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, required = true, description = "domain id of the new VM owner.")
65+
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "domain id of the new VM owner.")
6566
private Long domainId;
6667

68+
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the new VM owner.")
69+
private Long projectId;
70+
6771
//Network information
6872
@Parameter(name = ApiConstants.NETWORK_IDS,
6973
type = CommandType.LIST,
@@ -98,6 +102,10 @@ public Long getDomainId() {
98102
return domainId;
99103
}
100104

105+
public Long getProjectId() {
106+
return projectId;
107+
}
108+
101109
public List<Long> getNetworkIds() {
102110
return networkIds;
103111
}

api/src/org/apache/cloudstack/api/response/ProjectResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou
5656
@Param(description = "the account name of the project's owner")
5757
private String ownerName;
5858

59+
@SerializedName("projectaccountname")
60+
@Param(description="the project account name of the project")
61+
private String projectAccountName;
62+
5963
@SerializedName(ApiConstants.STATE)
6064
@Param(description = "the state of the project")
6165
private String state;
@@ -228,6 +232,10 @@ public void setOwner(String owner) {
228232
ownerName = owner;
229233
}
230234

235+
public void setProjectAccountName(String projectAccountName) {
236+
this.projectAccountName = projectAccountName;
237+
}
238+
231239
public void setState(String state) {
232240
this.state = state;
233241
}

server/src/com/cloud/api/query/dao/ProjectJoinDaoImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public ProjectResponse newProjectResponse(ProjectJoinVO proj) {
9393
Account account = _accountDao.findByIdIncludingRemoved(proj.getProjectAccountId());
9494
AccountJoinVO accountJn = ApiDBUtils.newAccountView(account);
9595
_accountJoinDao.setResourceLimits(accountJn, false, response);
96+
response.setProjectAccountName(accountJn.getAccountName());
9697

9798
response.setObjectName("project");
9899
return response;

server/src/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5048,14 +5048,8 @@ public UserVm moveVMToUser(final AssignVMCmd cmd) throws ResourceAllocationExcep
50485048
if (oldAccount == null) {
50495049
throw new InvalidParameterValueException("Invalid account for VM " + vm.getAccountId() + " in domain.");
50505050
}
5051-
// don't allow to move the vm from the project
5052-
if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) {
5053-
InvalidParameterValueException ex = new InvalidParameterValueException("Specified Vm id belongs to the project and can't be moved");
5054-
ex.addProxyObject(vm.getUuid(), "vmId");
5055-
throw ex;
5056-
}
5057-
final Account newAccount = _accountService.getActiveAccountByName(cmd.getAccountName(), cmd.getDomainId());
5058-
if (newAccount == null || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) {
5051+
final Account newAccount = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
5052+
if (newAccount == null) {
50595053
throw new InvalidParameterValueException("Invalid accountid=" + cmd.getAccountName() + " in domain " + cmd.getDomainId());
50605054
}
50615055

@@ -5356,7 +5350,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
53565350
s_logger.debug("AssignVM: Advance virtual, adding networks no " + networks.size() + " to " + vm.getInstanceName());
53575351
} // END IF NON SEC GRP ENABLED
53585352
} // END IF ADVANCED
5359-
s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + cmd.getAccountName());
5353+
s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + newAccount.getAccountName());
53605354
return vm;
53615355
}
53625356

server/test/com/cloud/vm/UserVmManagerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ public void testMoveVmToUser2() throws Exception {
690690

691691
when(_accountService.getActiveAccountById(anyLong())).thenReturn(oldAccount);
692692

693-
when(_accountService.getActiveAccountByName(anyString(), anyLong())).thenReturn(newAccount);
693+
when(_accountMgr.finalizeOwner(any(Account.class), anyString(), anyLong(), anyLong())).thenReturn(newAccount);
694694

695695
doThrow(new PermissionDeniedException("Access check failed")).when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class),
696696
any(ControlledEntity.class));

ui/scripts/instances.js

Lines changed: 216 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,7 +1905,32 @@
19051905
label: 'label.assign.instance.another',
19061906
createForm: {
19071907
title: 'label.assign.instance.another',
1908+
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.',
19081909
fields: {
1910+
accountType: {
1911+
label: 'Account Type',
1912+
select: function(args) {
1913+
var items = [];
1914+
items.push({id: 'account', description: 'Account'});
1915+
items.push({id: 'project', description: 'Project'});
1916+
args.response.success({data: items});
1917+
1918+
args.$select.change(function() {
1919+
var $form = $(this).closest('form');
1920+
var $account = $form.find('.form-item[rel=account]');
1921+
var $project = $form.find('.form-item[rel=project]');
1922+
1923+
var accountType = $(this).val();
1924+
if (accountType == 'account') { // Account
1925+
$account.css('display', 'inline-block');
1926+
$project.hide();
1927+
} else if (accountType == 'project') { // Project
1928+
$project.css('display', 'inline-block');
1929+
$account.hide();
1930+
}
1931+
});
1932+
}
1933+
},
19091934
domainid: {
19101935
label: 'label.domain',
19111936
validation: {
@@ -1941,20 +1966,203 @@
19411966
},
19421967
account: {
19431968
label: 'label.account',
1969+
dependsOn: 'domainid',
19441970
validation: {
19451971
required: true
1946-
}
1947-
}
1972+
},
1973+
select: function(args) {
1974+
var dataObj = {
1975+
domainId: args.domainid,
1976+
state: 'Enabled',
1977+
listAll: true,
1978+
};
1979+
$.ajax({
1980+
url: createURL('listAccounts', {
1981+
ignoreProject: true
1982+
}),
1983+
data: dataObj,
1984+
success: function(json) {
1985+
accountObjs = json.listaccountsresponse.account;
1986+
var items = [{
1987+
id: null,
1988+
description: ''
1989+
}];
1990+
$(accountObjs).each(function() {
1991+
items.push({
1992+
id: this.name,
1993+
description: this.name
1994+
});
1995+
})
1996+
1997+
args.response.success({
1998+
data: items
1999+
});
2000+
}
2001+
});
2002+
},
2003+
},
2004+
project: {
2005+
label: 'label.project',
2006+
dependsOn: 'domainid',
2007+
validation: {
2008+
required: true
2009+
},
2010+
select: function(args) {
2011+
var dataObj = {
2012+
domainId: args.domainid,
2013+
state: 'Active',
2014+
listAll: true,
2015+
};
2016+
$.ajax({
2017+
url: createURL('listProjects', {
2018+
ignoreProject: true
2019+
}),
2020+
data: dataObj,
2021+
success: function(json) {
2022+
projectObjs = json.listprojectsresponse.project;
2023+
var items = [{
2024+
id: null,
2025+
description: ''
2026+
}];
2027+
$(projectObjs).each(function() {
2028+
items.push({
2029+
id: this.id,
2030+
description: this.name
2031+
});
2032+
})
2033+
2034+
args.response.success({
2035+
data: items
2036+
});
2037+
}
2038+
});
2039+
},
2040+
},
2041+
network: {
2042+
label: 'label.network',
2043+
dependsOn: ['accountType', 'domainid', 'account', 'project'],
2044+
select: function(args) {
2045+
var dataObj = {
2046+
domainId: args.domainid,
2047+
listAll: true,
2048+
isrecursive: false
2049+
};
2050+
if (args.data.accountType == 'account' && args.data.account != null && args.data.account != '') {
2051+
$.extend(dataObj, {
2052+
account: args.data.account
2053+
});
2054+
} else if (args.data.accountType == 'project' && args.data.project != null && args.data.project != '') {
2055+
$.extend(dataObj, {
2056+
projectid: args.data.project
2057+
});
2058+
} else {
2059+
args.response.success({
2060+
data: null
2061+
});
2062+
return;
2063+
}
2064+
$.ajax({
2065+
url: createURL('listNetworks', {
2066+
ignoreProject: true
2067+
}),
2068+
data: dataObj,
2069+
success: function(json) {
2070+
var networkObjs = json.listnetworksresponse.network;
2071+
var items = [{
2072+
id: null,
2073+
description: ''
2074+
}];
2075+
$(networkObjs).each(function() {
2076+
items.push({
2077+
id: this.id,
2078+
description: this.name
2079+
});
2080+
})
2081+
2082+
args.response.success({
2083+
data: items
2084+
});
2085+
}
2086+
});
2087+
},
2088+
},
2089+
securitygroup: {
2090+
label: 'label.security.group',
2091+
dependsOn: ['accountType', 'domainid', 'account', 'project'],
2092+
select: function(args) {
2093+
var dataObj = {
2094+
domainId: args.domainid,
2095+
listAll: true,
2096+
isrecursive: false
2097+
};
2098+
if (args.data.accountType == 'account' && args.data.account != null && args.data.account != '') {
2099+
$.extend(dataObj, {
2100+
account: args.data.account
2101+
});
2102+
} else if (args.data.accountType == 'project' && args.data.project != null && args.data.project != '') {
2103+
$.extend(dataObj, {
2104+
projectid: args.data.project
2105+
});
2106+
} else {
2107+
args.response.success({
2108+
data: null
2109+
});
2110+
return;
2111+
}
2112+
$.ajax({
2113+
url: createURL('listSecurityGroups', {
2114+
ignoreProject: true
2115+
}),
2116+
data: dataObj,
2117+
success: function(json) {
2118+
var sgObjs = json.listsecuritygroupsresponse.securitygroup;
2119+
var items = [{
2120+
id: null,
2121+
description: ''
2122+
}];
2123+
$(sgObjs).each(function() {
2124+
items.push({
2125+
id: this.id,
2126+
description: this.name
2127+
});
2128+
})
2129+
2130+
args.response.success({
2131+
data: items
2132+
});
2133+
}
2134+
});
2135+
},
2136+
},
19482137
}
19492138
},
19502139
action: function(args) {
1951-
$.ajax({
1952-
url: createURL('assignVirtualMachine'),
1953-
data: {
1954-
virtualmachineid: args.context.instances[0].id,
1955-
domainid: args.data.domainid,
2140+
var dataObj = {
2141+
virtualmachineid: args.context.instances[0].id,
2142+
domainid: args.data.domainid,
2143+
};
2144+
var ignoreProject = false;
2145+
if (args.data.accountType == 'account') {
2146+
ignoreProject = true;
2147+
$.extend(dataObj, {
19562148
account: args.data.account
1957-
},
2149+
});
2150+
} else if (args.data.accountType == 'project') {
2151+
$.extend(dataObj, {
2152+
projectid: args.data.project
2153+
});
2154+
}
2155+
if (args.data.network != null && args.data.network != '') {
2156+
$.extend(dataObj, {
2157+
networkIds: args.data.network
2158+
});
2159+
}
2160+
2161+
$.ajax({
2162+
url: createURL('assignVirtualMachine', {
2163+
ignoreProject: ignoreProject
2164+
}),
2165+
data: dataObj,
19582166
success: function(json) {
19592167
var item = json.assignvirtualmachineresponse.virtualmachine;
19602168
args.response.success({

0 commit comments

Comments
 (0)