diff --git a/api/src/com/cloud/api/commands/MoveUserVMCmd.java b/api/src/com/cloud/api/commands/MoveUserVMCmd.java
new file mode 100644
index 00000000000..0ef59bd417d
--- /dev/null
+++ b/api/src/com/cloud/api/commands/MoveUserVMCmd.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.api.commands;
+
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiConstants;
+import com.cloud.api.BaseAsyncCmd;
+import com.cloud.api.BaseCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.ServerApiException;
+import com.cloud.api.response.UserVmResponse;
+import com.cloud.api.response.ZoneResponse;
+import com.cloud.async.AsyncJob;
+import com.cloud.event.EventTypes;
+import com.cloud.user.Account;
+import com.cloud.uservm.UserVm;
+
+@Implementation(description="Move a user VM to another user under same domain.", responseObject=UserVmResponse.class)
+public class MoveUserVMCmd extends BaseAsyncCmd {
+ public static final Logger s_logger = Logger.getLogger(MoveUserVMCmd.class.getName());
+
+ private static final String s_name = "moveuservmresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+
+ @Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, description="the host ID of the system VM to be moved")
+ private Long hostId;
+
+ @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.LONG, description="the accopunt id of the new owner account")
+ private Long accountId;
+
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getHostId() {
+ return hostId;
+ }
+
+ public Long getAccountId() {
+ return accountId;
+ }
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getCommandName() {
+ return s_name;
+ }
+
+ @Override
+ public void execute(){
+ try {
+ UserVm userVm = _userVmService.moveVMToUser(this);
+ if (userVm == null){
+ throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to move vm");
+ }
+ UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", userVm).get(0);
+ response.setResponseName(DeployVMCmd.getResultObjectName());
+ this.setResponseObject(response);
+ }catch (Exception e){
+ e.printStackTrace();
+ throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to move vm " + e.getMessage());
+ }
+
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ UserVm vm = _responseGenerator.findUserVmById(getHostId());
+ if (vm != null) {
+ return vm.getAccountId();
+ }
+
+ return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
+ }
+
+ @Override
+ public String getEventDescription() {
+ return "moving user vm: " + getHostId();
+ }
+
+ @Override
+ public String getEventType() {
+ return EventTypes.EVENT_VM_MOVE;
+ }
+}
diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java
index 096c56c6601..3b6bb30c4a6 100755
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -28,8 +28,9 @@ public class EventTypes {
public static final String EVENT_VM_UPDATE = "VM.UPDATE";
public static final String EVENT_VM_UPGRADE = "VM.UPGRADE";
public static final String EVENT_VM_RESETPASSWORD = "VM.RESETPASSWORD";
- public static final String EVENT_VM_MIGRATE = "VM.MIGRATE";
-
+ public static final String EVENT_VM_MIGRATE = "VM.MIGRATE";
+ public static final String EVENT_VM_MOVE = "VM.MOVE";
+
// Domain Router
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
public static final String EVENT_ROUTER_DESTROY = "ROUTER.DESTROY";
diff --git a/api/src/com/cloud/uservm/UserVm.java b/api/src/com/cloud/uservm/UserVm.java
index 323a989b985..c92bb6391e5 100755
--- a/api/src/com/cloud/uservm/UserVm.java
+++ b/api/src/com/cloud/uservm/UserVm.java
@@ -36,5 +36,7 @@ public interface UserVm extends VirtualMachine, ControlledEntity {
void setUserData(String userData);
String getDetail(String name);
+
+ void setAccountId(long accountId);
}
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index 83b783c093e..ad9f5a6e91f 100755
--- a/api/src/com/cloud/vm/UserVmService.java
+++ b/api/src/com/cloud/vm/UserVmService.java
@@ -29,6 +29,7 @@ import com.cloud.api.commands.DeployVMCmd;
import com.cloud.api.commands.DestroyVMCmd;
import com.cloud.api.commands.DetachVolumeCmd;
import com.cloud.api.commands.ListVMsCmd;
+import com.cloud.api.commands.MoveUserVMCmd;
import com.cloud.api.commands.RebootVMCmd;
import com.cloud.api.commands.RecoverVMCmd;
import com.cloud.api.commands.ResetVMPasswordCmd;
@@ -369,4 +370,6 @@ public interface UserVmService {
* if the VM to be migrated is not in Running state
*/
UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException;
+
+ UserVm moveVMToUser(MoveUserVMCmd moveUserVMCmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException ;
}
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 291d8f13df3..ee84b5352ba 100755
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -47,6 +47,7 @@ recoverVirtualMachine=com.cloud.api.commands.RecoverVMCmd;7
listVirtualMachines=com.cloud.api.commands.ListVMsCmd;15
getVMPassword=com.cloud.api.commands.GetVMPasswordCmd;15
migrateVirtualMachine=com.cloud.api.commands.MigrateVMCmd;1
+moveVirtualMachine=com.cloud.api.commands.MoveUserVMCmd;15
#### snapshot commands
createSnapshot=com.cloud.api.commands.CreateSnapshotCmd;15
diff --git a/core/src/com/cloud/vm/UserVmVO.java b/core/src/com/cloud/vm/UserVmVO.java
index 329d54eead7..f6d00247b53 100755
--- a/core/src/com/cloud/vm/UserVmVO.java
+++ b/core/src/com/cloud/vm/UserVmVO.java
@@ -139,5 +139,9 @@ public class UserVmVO extends VMInstanceVO implements UserVm {
public void setDetails(Map details) {
this.details = details;
}
+
+ public void setAccountId(long accountId){
+ this.accountId = accountId;
+ }
}
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 32325f27740..8bcbeb3d4fa 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -63,6 +63,7 @@ import com.cloud.api.commands.DeployVMCmd;
import com.cloud.api.commands.DestroyVMCmd;
import com.cloud.api.commands.DetachVolumeCmd;
import com.cloud.api.commands.ListVMsCmd;
+import com.cloud.api.commands.MoveUserVMCmd;
import com.cloud.api.commands.RebootVMCmd;
import com.cloud.api.commands.RecoverVMCmd;
import com.cloud.api.commands.ResetVMPasswordCmd;
@@ -3200,5 +3201,46 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
UserVmVO migratedVm = _itMgr.migrate((UserVmVO) vm, srcHostId, dest);
return migratedVm;
}
+
+
+ @Override
+ @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false)
+ public UserVm moveVMToUser(MoveUserVMCmd cmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
+ //verify the two users
+ Account oldOwner = UserContext.current().getCaller();
+ Account newOwner = _accountService.getAccount(cmd.getAccountId());
+ if (newOwner == null) {
+ throw new InvalidParameterValueException("Unable to find account " + newOwner + " in domain " + oldOwner.getDomainId());
+ }
+ //get the VM
+ UserVmVO vm = _vmDao.findById(cmd.getHostId());
+ // update the owner
+ vm.setAccountId(newOwner.getAccountId());
+
+ DomainVO domain = _domainDao.findById(oldOwner.getDomainId());
+ // check that caller can operate with domain
+ _accountMgr.checkAccess(oldOwner, domain);
+ // check that vm owner can create vm in the domain
+ _accountMgr.checkAccess(newOwner, domain);
+
+ // check if account/domain is with in resource limits to create a new vm
+ if (_accountMgr.resourceLimitExceeded(newOwner, ResourceType.user_vm)) {
+ ResourceAllocationException rae = new ResourceAllocationException("Maximum number of virtual machines for account: " + newOwner.getAccountName() + " has been exceeded.");
+ rae.setResourceType("vm");
+ throw rae;
+ }
+
+ this.stopVirtualMachine(cmd.getHostId(), true);
+
+ //update ownership
+ vm.setAccountId(newOwner.getId());
+
+ _vmDao.persist(vm);
+
+ this.startVirtualMachine(cmd.getHostId());
+
+ return vm;
+ }
+
}