From 9bd5e5871bbc108495a2de1f70f2e27a05ebb875 Mon Sep 17 00:00:00 2001 From: prachi Date: Thu, 29 Sep 2011 17:46:23 -0700 Subject: [PATCH] Bug 11481 - get manual live migration working with all VMs Changes: - Added a new API 'migrateSystemVm' backed by MigrateSystemVMCmd.java to migrate system VMs (SSVM, consoleproxy, domain routers(router, LB, DHCP)) - This is Admin only action - The existing API 'migratevirtualmachine' is only for user VMs --- api/src/com/cloud/api/ResponseGenerator.java | 3 + .../com/cloud/api/commands/ListHostsCmd.java | 8 +- .../api/commands/MigrateSystemVMCmd.java | 132 ++++++++++++++++++ .../com/cloud/api/commands/MigrateVMCmd.java | 11 +- .../response/SystemVmInstanceResponse.java | 98 +++++++++++++ .../com/cloud/server/ManagementService.java | 7 +- api/src/com/cloud/vm/UserVmService.java | 8 +- client/tomcatconf/commands.properties.in | 1 + .../src/com/cloud/api/ApiResponseHelper.java | 21 +++ .../cloud/server/ManagementServerImpl.java | 12 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 9 +- 11 files changed, 284 insertions(+), 26 deletions(-) create mode 100644 api/src/com/cloud/api/commands/MigrateSystemVMCmd.java create mode 100644 api/src/com/cloud/api/response/SystemVmInstanceResponse.java diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java index 8724bd87b0f..69f4628b84a 100755 --- a/api/src/com/cloud/api/ResponseGenerator.java +++ b/api/src/com/cloud/api/ResponseGenerator.java @@ -55,6 +55,7 @@ import com.cloud.api.response.ServiceOfferingResponse; import com.cloud.api.response.SnapshotPolicyResponse; import com.cloud.api.response.SnapshotResponse; import com.cloud.api.response.StoragePoolResponse; +import com.cloud.api.response.SystemVmInstanceResponse; import com.cloud.api.response.SystemVmResponse; import com.cloud.api.response.TemplatePermissionsResponse; import com.cloud.api.response.TemplateResponse; @@ -230,4 +231,6 @@ public interface ResponseGenerator { ProjectInvitationResponse createProjectInvitationResponse(ProjectInvitation invite); + SystemVmInstanceResponse createSystemVmInstanceResponse(VirtualMachine systemVM); + } diff --git a/api/src/com/cloud/api/commands/ListHostsCmd.java b/api/src/com/cloud/api/commands/ListHostsCmd.java index c52df4cafc0..c8d7d119013 100644 --- a/api/src/com/cloud/api/commands/ListHostsCmd.java +++ b/api/src/com/cloud/api/commands/ListHostsCmd.java @@ -30,9 +30,7 @@ import com.cloud.api.Parameter; import com.cloud.api.response.HostResponse; import com.cloud.api.response.ListResponse; import com.cloud.async.AsyncJob; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.Host; -import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; @Implementation(description="Lists hosts.", responseObject=HostResponse.class) @@ -130,11 +128,7 @@ public class ListHostsCmd extends BaseListCmd { List hostsWithCapacity = new ArrayList(); if(getVirtualMachineId() != null){ - UserVm userVm = _userVmService.getUserVm(getVirtualMachineId()); - if (userVm == null) { - throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId()); - } - Pair, List> hostsForMigration = _mgr.listHostsForMigrationOfVM(userVm, this.getStartIndex(), this.getPageSizeVal()); + Pair, List> hostsForMigration = _mgr.listHostsForMigrationOfVM(getVirtualMachineId(), this.getStartIndex(), this.getPageSizeVal()); result = hostsForMigration.first(); hostsWithCapacity = hostsForMigration.second(); }else{ diff --git a/api/src/com/cloud/api/commands/MigrateSystemVMCmd.java b/api/src/com/cloud/api/commands/MigrateSystemVMCmd.java new file mode 100644 index 00000000000..9b36af45da8 --- /dev/null +++ b/api/src/com/cloud/api/commands/MigrateSystemVMCmd.java @@ -0,0 +1,132 @@ +/** + * Copyright (C) 2011 Citrix Systems, 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.SystemVmInstanceResponse; +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.host.Host; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.vm.VirtualMachine; + +@Implementation(description="Attempts Migration of a system virtual machine to the host specified.", responseObject=SystemVmInstanceResponse.class) +public class MigrateSystemVMCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(MigrateSystemVMCmd.class.getName()); + + private static final String s_name = "migratesystemvmresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.HOST_ID, type=CommandType.LONG, required=true, description="destination Host ID to migrate VM to") + private Long hostId; + + @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.LONG, required=true, description="the ID of the virtual machine") + private Long virtualMachineId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getHostId() { + return hostId; + } + + public Long getVirtualMachineId() { + return virtualMachineId; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_MIGRATE; + } + + @Override + public String getEventDescription() { + return "Attempting to migrate VM Id: " + getVirtualMachineId() + " to host Id: "+ getHostId(); + } + + @Override + public void execute(){ + + Host destinationHost = _resourceService.getHost(getHostId()); + if (destinationHost == null) { + throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId()); + } + try{ + UserContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: "+ getHostId()); + //FIXME : Should not be calling UserVmService to migrate all types of VMs - need a generic VM layer + VirtualMachine migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), destinationHost); + if (migratedVm != null) { + // return the generic system VM instance response + SystemVmInstanceResponse response = _responseGenerator.createSystemVmInstanceResponse(migratedVm); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to migrate the system vm"); + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(BaseCmd.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException e) { + s_logger.warn("Exception: ", e); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.getMessage()); + } catch (ManagementServerException e) { + s_logger.warn("Exception: ", e); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.getMessage()); + } catch (VirtualMachineMigrationException e) { + s_logger.warn("Exception: ", e); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, e.getMessage()); + } + } +} diff --git a/api/src/com/cloud/api/commands/MigrateVMCmd.java b/api/src/com/cloud/api/commands/MigrateVMCmd.java index df3d3a8cb1f..d697e6d76d2 100644 --- a/api/src/com/cloud/api/commands/MigrateVMCmd.java +++ b/api/src/com/cloud/api/commands/MigrateVMCmd.java @@ -36,8 +36,9 @@ import com.cloud.host.Host; import com.cloud.user.Account; import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; -@Implementation(description="Attempts Migration of a virtual machine to the host specified.", responseObject=UserVmResponse.class) +@Implementation(description="Attempts Migration of a user virtual machine to the host specified.", responseObject=UserVmResponse.class) public class MigrateVMCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(MigrateVMCmd.class.getName()); @@ -109,11 +110,11 @@ public class MigrateVMCmd extends BaseAsyncCmd { } try{ UserContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: "+ getHostId()); - UserVm migratedVm = _userVmService.migrateVirtualMachine(userVm, destinationHost); + VirtualMachine migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), destinationHost); if (migratedVm != null) { - UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", migratedVm).get(0); - response.setResponseName(getCommandName()); - this.setResponseObject(response); + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", (UserVm)migratedVm).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to migrate vm"); } diff --git a/api/src/com/cloud/api/response/SystemVmInstanceResponse.java b/api/src/com/cloud/api/response/SystemVmInstanceResponse.java new file mode 100644 index 00000000000..b517e72e832 --- /dev/null +++ b/api/src/com/cloud/api/response/SystemVmInstanceResponse.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2011 Citrix Systems, 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.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +/* + * This is the generic response for all types of System VMs (SSVM, consoleproxy, domain routers(router, LB, DHCP)) + */ +public class SystemVmInstanceResponse extends BaseResponse { + @SerializedName("id") @Param(description="the ID of the system VM") + private Long id; + + @SerializedName("systemvmtype") @Param(description="the system VM type") + private String systemVmType; + + @SerializedName("name") @Param(description="the name of the system VM") + private String name; + + @SerializedName("hostid") @Param(description="the host ID for the system VM") + private Long hostId; + + @SerializedName("state") @Param(description="the state of the system VM") + private String state; + + @SerializedName("role") @Param(description="the role of the system VM") + private String role; + + + public Long getObjectId() { + return getId(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getSystemVmType() { + return systemVmType; + } + + public void setSystemVmType(String systemVmType) { + this.systemVmType = systemVmType; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + +} diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index 98b12691c7e..9e4e3be653e 100755 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -100,7 +100,6 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.SSHKeyPair; import com.cloud.user.UserAccount; -import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.vm.InstanceGroup; import com.cloud.vm.VirtualMachine; @@ -479,11 +478,11 @@ public interface ManagementService { * List hosts for migrating the given VM. The API returns list of all hosts in the VM's cluster minus the current host and * also a list of hosts that seem to have enough CPU and RAM capacity to host this VM. * - * @param UserVm - * vm The VM to migrate + * @param Long vmId + * Id of The VM to migrate * @return Pair, List> List of all Hosts in VM's cluster and list of Hosts with enough capacity */ - Pair, List> listHostsForMigrationOfVM(UserVm vm, Long startIndex, Long pageSize); + Pair, List> listHostsForMigrationOfVM(Long vmId, Long startIndex, Long pageSize); String[] listEventTypes(); diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 38b49742f5f..655b972315b 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -359,11 +359,11 @@ public interface UserVmService { * Migrate the given VM to the destination host provided. The API returns the migrated VM if migration succeeds. Only Root * Admin can migrate a VM. * - * @param UserVm - * vm The VM to migrate + * @param Long vmId + * vmId of The VM to migrate * @param Host * destinationHost to migrate the VM - * @return UserVm migrated VM + * @return VirtualMachine migrated VM * @throws ManagementServerException * in case we get error finding the VM or host or access errors or other internal errors. * @throws ConcurrentOperationException @@ -373,7 +373,7 @@ public interface UserVmService { * @throws VirtualMachineMigrationException * if the VM to be migrated is not in Running state */ - UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; + VirtualMachine migrateVirtualMachine(Long vmId, 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 0572f339725..3355a2e0b14 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -145,6 +145,7 @@ rebootSystemVm=com.cloud.api.commands.RebootSystemVmCmd;1 stopSystemVm=com.cloud.api.commands.StopSystemVmCmd;1 destroySystemVm=com.cloud.api.commands.DestroySystemVmCmd;1 listSystemVms=com.cloud.api.commands.ListSystemVMsCmd;3 +migrateSystemVm=com.cloud.api.commands.MigrateSystemVMCmd;1 #### configuration commands updateConfiguration=com.cloud.api.commands.UpdateCfgCmd;1 diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index c06eae460d3..c9eff204e58 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -76,6 +76,7 @@ import com.cloud.api.response.ServiceResponse; import com.cloud.api.response.SnapshotPolicyResponse; import com.cloud.api.response.SnapshotResponse; import com.cloud.api.response.StoragePoolResponse; +import com.cloud.api.response.SystemVmInstanceResponse; import com.cloud.api.response.SystemVmResponse; import com.cloud.api.response.TemplatePermissionsResponse; import com.cloud.api.response.TemplateResponse; @@ -2539,4 +2540,24 @@ public class ApiResponseHelper implements ResponseGenerator { return response; } + @Override + public SystemVmInstanceResponse createSystemVmInstanceResponse(VirtualMachine vm){ + SystemVmInstanceResponse vmResponse = new SystemVmInstanceResponse(); + vmResponse.setId(vm.getId()); + vmResponse.setSystemVmType(vm.getType().toString().toLowerCase()); + vmResponse.setName(vm.getHostName()); + if (vm.getHostId() != null) { + vmResponse.setHostId(vm.getHostId()); + } + if (vm.getState() != null) { + vmResponse.setState(vm.getState().toString()); + } + if (vm.getType() == Type.DomainRouter) { + VirtualRouter router = (VirtualRouter)vm; + if(router.getRole() != null){ + vmResponse.setRole(router.getRole().toString()); + } + } + return vmResponse; + } } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 8fe5daa880e..91db7d77f0b 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -212,7 +212,6 @@ import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.SSHKeyPairDao; import com.cloud.user.dao.UserAccountDao; import com.cloud.user.dao.UserDao; -import com.cloud.uservm.UserVm; import com.cloud.utils.EnumUtils; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; @@ -950,7 +949,7 @@ public class ManagementServerImpl implements ManagementServer { } @Override - public Pair, List> listHostsForMigrationOfVM(UserVm vm, Long startIndex, Long pageSize) { + public Pair, List> listHostsForMigrationOfVM(Long vmId, Long startIndex, Long pageSize) { // access check - only root admin can migrate VM Account caller = UserContext.current().getCaller(); if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { @@ -959,6 +958,11 @@ public class ManagementServerImpl implements ManagementServer { } throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); } + + VMInstanceVO vm = _vmInstanceDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId); + } // business logic if (vm.getState() != State.Running) { if (s_logger.isDebugEnabled()) { @@ -1009,8 +1013,8 @@ public class ManagementServerImpl implements ManagementServer { List suitableHosts = new ArrayList(); Enumeration enHost = _hostAllocators.enumeration(); - UserVmVO vmVO = (UserVmVO)vm; - VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmVO); + + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); DataCenterDeployment plan = new DataCenterDeployment(srcHost.getDataCenterId(), srcHost.getPodId(), srcHost.getClusterId(), null, null); ExcludeList excludes = new ExcludeList(); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 1fc164af6a3..1e943e9f07a 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3187,7 +3187,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) - public UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { // access check - only root admin can migrate VM Account caller = UserContext.current().getCaller(); if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { @@ -3196,6 +3196,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); } + + VMInstanceVO vm = _vmInstanceDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException("Unable to find the VM by id=" + vmId); + } // business logic if (vm.getState() != State.Running) { if (s_logger.isDebugEnabled()) { @@ -3237,7 +3242,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new VirtualMachineMigrationException("Destination host, hostId: "+ destinationHost.getId() +" already has max Running VMs(count includes system VMs), limit is: " + maxGuestLimit + " , cannot migrate to this host"); } - UserVmVO migratedVm = _itMgr.migrate((UserVmVO) vm, srcHostId, dest); + VMInstanceVO migratedVm = _itMgr.migrate(vm, srcHostId, dest); return migratedVm; }