Skip to content

Commit e02003d

Browse files
committed
Merge pull request #844 from ustcweizhou/assignvm-master
[4.10] CLOUDSTACK-7985: assignVM in Advanced zone with Security GroupsThis commit contains the following changes: (1) implementation of assignVM in Advanced zone with Security Groups (2) keep the default nic on shared network when assignVM (3) allow migrate vm from/to project; (4) UI change for selecting account/project/network * pr/844: CLOUDSTACK-7985: assignVM in Advanced zone with Security Groups CLOUDSTACK-7985: keep the default nic on shared network when assignVM CLOUDSTACK-7985: (1) allow migrate vm from/to project; (2) UI change for selecting account/project/network Signed-off-by: Rajani Karuturi <rajani.karuturi@accelerite.com>
2 parents 2d5054f + ab0d04d commit e02003d

7 files changed

Lines changed: 413 additions & 25 deletions

File tree

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
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

36+
import com.cloud.exception.InvalidParameterValueException;
3537
import com.cloud.user.Account;
3638
import com.cloud.uservm.UserVm;
3739
import com.cloud.vm.VirtualMachine;
@@ -58,12 +60,15 @@ public class AssignVMCmd extends BaseCmd {
5860
description = "id of the VM to be moved")
5961
private Long virtualMachineId;
6062

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

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

69+
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the new VM owner.")
70+
private Long projectId;
71+
6772
//Network information
6873
@Parameter(name = ApiConstants.NETWORK_IDS,
6974
type = CommandType.LIST,
@@ -98,6 +103,10 @@ public Long getDomainId() {
98103
return domainId;
99104
}
100105

106+
public Long getProjectId() {
107+
return projectId;
108+
}
109+
101110
public List<Long> getNetworkIds() {
102111
return networkIds;
103112
}
@@ -125,6 +134,9 @@ public void execute() {
125134
UserVmResponse response = _responseGenerator.createUserVmResponse(ResponseView.Full, "virtualmachine", userVm).get(0);
126135
response.setResponseName(getCommandName());
127136
setResponseObject(response);
137+
} catch (InvalidParameterValueException e){
138+
e.printStackTrace();
139+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
128140
} catch (Exception e) {
129141
e.printStackTrace();
130142
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move vm " + e.getMessage());

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: 146 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5065,14 +5065,8 @@ public UserVm moveVMToUser(final AssignVMCmd cmd) throws ResourceAllocationExcep
50655065
if (oldAccount == null) {
50665066
throw new InvalidParameterValueException("Invalid account for VM " + vm.getAccountId() + " in domain.");
50675067
}
5068-
// don't allow to move the vm from the project
5069-
if (oldAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) {
5070-
InvalidParameterValueException ex = new InvalidParameterValueException("Specified Vm id belongs to the project and can't be moved");
5071-
ex.addProxyObject(vm.getUuid(), "vmId");
5072-
throw ex;
5073-
}
5074-
final Account newAccount = _accountService.getActiveAccountByName(cmd.getAccountName(), cmd.getDomainId());
5075-
if (newAccount == null || newAccount.getType() == Account.ACCOUNT_TYPE_PROJECT) {
5068+
final Account newAccount = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
5069+
if (newAccount == null) {
50765070
throw new InvalidParameterValueException("Invalid accountid=" + cmd.getAccountName() + " in domain " + cmd.getDomainId());
50775071
}
50785072

@@ -5263,18 +5257,156 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
52635257

52645258
s_logger.debug("AssignVM: Basic zone, adding security groups no " + securityGroupIdList.size() + " to " + vm.getInstanceName());
52655259
} else {
5266-
if (zone.isSecurityGroupEnabled()) {
5267-
throw new InvalidParameterValueException("Not yet implemented for SecurityGroupEnabled advanced networks.");
5260+
if (zone.isSecurityGroupEnabled()) { // advanced zone with security groups
5261+
// cleanup the old security groups
5262+
_securityGroupMgr.removeInstanceFromGroups(cmd.getVmId());
5263+
5264+
Set<NetworkVO> applicableNetworks = new HashSet<NetworkVO>();
5265+
String requestedIPv4ForDefaultNic = null;
5266+
String requestedIPv6ForDefaultNic = null;
5267+
// if networkIdList is null and the first network of vm is shared network, then keep it if possible
5268+
if (networkIdList == null || networkIdList.isEmpty()) {
5269+
NicVO defaultNicOld = _nicDao.findDefaultNicForVM(vm.getId());
5270+
if (defaultNicOld != null) {
5271+
NetworkVO defaultNetworkOld = _networkDao.findById(defaultNicOld.getNetworkId());
5272+
if (defaultNetworkOld != null && defaultNetworkOld.getGuestType() == Network.GuestType.Shared && defaultNetworkOld.getAclType() == ACLType.Domain) {
5273+
try {
5274+
_networkModel.checkNetworkPermissions(newAccount, defaultNetworkOld);
5275+
applicableNetworks.add(defaultNetworkOld);
5276+
requestedIPv4ForDefaultNic = defaultNicOld.getIPv4Address();
5277+
requestedIPv6ForDefaultNic = defaultNicOld.getIPv6Address();
5278+
s_logger.debug("AssignVM: use old shared network " + defaultNetworkOld.getName() + " with old ip " + requestedIPv4ForDefaultNic + " on default nic of vm:" + vm.getInstanceName());
5279+
} catch (PermissionDeniedException e) {
5280+
s_logger.debug("AssignVM: the shared network on old default nic can not be applied to new account");
5281+
}
5282+
}
5283+
}
5284+
}
5285+
// cleanup the network for the oldOwner
5286+
_networkMgr.cleanupNics(vmOldProfile);
5287+
_networkMgr.expungeNics(vmOldProfile);
5288+
5289+
if (networkIdList != null && !networkIdList.isEmpty()) {
5290+
// add any additional networks
5291+
for (Long networkId : networkIdList) {
5292+
NetworkVO network = _networkDao.findById(networkId);
5293+
if (network == null) {
5294+
InvalidParameterValueException ex = new InvalidParameterValueException(
5295+
"Unable to find specified network id");
5296+
ex.addProxyObject(networkId.toString(), "networkId");
5297+
throw ex;
5298+
}
5299+
5300+
_networkModel.checkNetworkPermissions(newAccount, network);
5301+
5302+
// don't allow to use system networks
5303+
NetworkOffering networkOffering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
5304+
if (networkOffering.isSystemOnly()) {
5305+
InvalidParameterValueException ex = new InvalidParameterValueException(
5306+
"Specified Network id is system only and can't be used for vm deployment");
5307+
ex.addProxyObject(network.getUuid(), "networkId");
5308+
throw ex;
5309+
}
5310+
applicableNetworks.add(network);
5311+
}
5312+
}
5313+
5314+
// add the new nics
5315+
LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<Network, List<? extends NicProfile>>();
5316+
int toggle = 0;
5317+
NetworkVO defaultNetwork = null;
5318+
for (NetworkVO appNet : applicableNetworks) {
5319+
NicProfile defaultNic = new NicProfile();
5320+
if (toggle == 0) {
5321+
defaultNic.setDefaultNic(true);
5322+
defaultNic.setRequestedIPv4(requestedIPv4ForDefaultNic);
5323+
defaultNic.setRequestedIPv6(requestedIPv6ForDefaultNic);
5324+
defaultNetwork = appNet;
5325+
toggle++;
5326+
}
5327+
networks.put(appNet, new ArrayList<NicProfile>(Arrays.asList(defaultNic)));
5328+
5329+
}
5330+
5331+
boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware);
5332+
if (securityGroupIdList != null && isVmWare) {
5333+
throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor");
5334+
} else if (!isVmWare && (defaultNetwork == null || _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork)) && _networkModel.canAddDefaultSecurityGroup()) {
5335+
if (securityGroupIdList == null) {
5336+
securityGroupIdList = new ArrayList<Long>();
5337+
}
5338+
SecurityGroup defaultGroup = _securityGroupMgr
5339+
.getDefaultSecurityGroup(newAccount.getId());
5340+
if (defaultGroup != null) {
5341+
// check if security group id list already contains Default
5342+
// security group, and if not - add it
5343+
boolean defaultGroupPresent = false;
5344+
for (Long securityGroupId : securityGroupIdList) {
5345+
if (securityGroupId.longValue() == defaultGroup.getId()) {
5346+
defaultGroupPresent = true;
5347+
break;
5348+
}
5349+
}
5350+
5351+
if (!defaultGroupPresent) {
5352+
securityGroupIdList.add(defaultGroup.getId());
5353+
}
5354+
5355+
} else {
5356+
// create default security group for the account
5357+
if (s_logger.isDebugEnabled()) {
5358+
s_logger.debug("Couldn't find default security group for the account "
5359+
+ newAccount + " so creating a new one");
5360+
}
5361+
defaultGroup = _securityGroupMgr.createSecurityGroup(
5362+
SecurityGroupManager.DEFAULT_GROUP_NAME,
5363+
SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION,
5364+
newAccount.getDomainId(), newAccount.getId(),
5365+
newAccount.getAccountName());
5366+
securityGroupIdList.add(defaultGroup.getId());
5367+
}
5368+
}
5369+
5370+
VirtualMachine vmi = _itMgr.findById(vm.getId());
5371+
VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmi);
5372+
5373+
if (applicableNetworks.isEmpty()) {
5374+
throw new InvalidParameterValueException("No network is specified, please specify one when you move the vm. For now, please add a network to VM on NICs tab.");
5375+
} else {
5376+
_networkMgr.allocate(vmProfile, networks);
5377+
}
5378+
5379+
_securityGroupMgr.addInstanceToGroups(vm.getId(),
5380+
securityGroupIdList);
5381+
s_logger.debug("AssignVM: Advanced zone, adding security groups no "
5382+
+ securityGroupIdList.size() + " to "
5383+
+ vm.getInstanceName());
5384+
52685385
} else {
52695386
if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) {
52705387
throw new InvalidParameterValueException("Can't move vm with security groups; security group feature is not enabled in this zone");
52715388
}
5389+
Set<NetworkVO> applicableNetworks = new HashSet<NetworkVO>();
5390+
// if networkIdList is null and the first network of vm is shared network, then keep it if possible
5391+
if (networkIdList == null || networkIdList.isEmpty()) {
5392+
NicVO defaultNicOld = _nicDao.findDefaultNicForVM(vm.getId());
5393+
if (defaultNicOld != null) {
5394+
NetworkVO defaultNetworkOld = _networkDao.findById(defaultNicOld.getNetworkId());
5395+
if (defaultNetworkOld != null && defaultNetworkOld.getGuestType() == Network.GuestType.Shared && defaultNetworkOld.getAclType() == ACLType.Domain) {
5396+
try {
5397+
_networkModel.checkNetworkPermissions(newAccount, defaultNetworkOld);
5398+
applicableNetworks.add(defaultNetworkOld);
5399+
} catch (PermissionDeniedException e) {
5400+
s_logger.debug("AssignVM: the shared network on old default nic can not be applied to new account");
5401+
}
5402+
}
5403+
}
5404+
}
5405+
52725406
// cleanup the network for the oldOwner
52735407
_networkMgr.cleanupNics(vmOldProfile);
52745408
_networkMgr.expungeNics(vmOldProfile);
52755409

5276-
Set<NetworkVO> applicableNetworks = new HashSet<NetworkVO>();
5277-
52785410
if (networkIdList != null && !networkIdList.isEmpty()) {
52795411
// add any additional networks
52805412
for (Long networkId : networkIdList) {
@@ -5296,7 +5428,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
52965428
}
52975429
applicableNetworks.add(network);
52985430
}
5299-
} else {
5431+
} else if (applicableNetworks.isEmpty()) {
53005432
NetworkVO defaultNetwork = null;
53015433
List<NetworkOfferingVO> requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false);
53025434
if (requiredOfferings.size() < 1) {
@@ -5373,7 +5505,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
53735505
s_logger.debug("AssignVM: Advance virtual, adding networks no " + networks.size() + " to " + vm.getInstanceName());
53745506
} // END IF NON SEC GRP ENABLED
53755507
} // END IF ADVANCED
5376-
s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + cmd.getAccountName());
5508+
s_logger.info("AssignVM: vm " + vm.getInstanceName() + " now belongs to account " + newAccount.getAccountName());
53775509
return vm;
53785510
}
53795511

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/l10n/en.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,6 +1909,7 @@ var dictionary = {"ICMP.code":"ICMP Code",
19091909
"message.alert.state.detected":"Alert state detected",
19101910
"message.allow.vpn.access":"Please enter a username and password of the user that you want to allow VPN access.",
19111911
"message.apply.snapshot.policy":"You have successfully updated your current snapshot policy.",
1912+
"message.assign.instance.another":"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.",
19121913
"message.attach.iso.confirm":"Please confirm that you want to attach the ISO to this virtual instance.",
19131914
"message.attach.volume":"Please fill in the following data to attach a new volume. If you are attaching a disk volume to a Windows based virtual machine, you will need to reboot the instance to see the attached disk.",
19141915
"message.basic.mode.desc":"Choose this network model if you do <b>*<u>not</u>*</b> want to enable any VLAN support. All virtual instances created under this network model will be assigned an IP directly from the network and security groups are used to provide security and segregation.",

0 commit comments

Comments
 (0)