mirror of https://github.com/apache/cloudstack.git
API changes for Bug 3316 - Manual live migration of Virtual Machines
This is a Root admin only functionality --------------------- Service API changes: --------------------- - ManagementServer will expose new API: Pair<List<HostVO>, List<Long>> listHostsForMigrationOfVM(UserVm vm, Long startIndex, Long pageSize) The API returns list of all hosts in the VM's cluster minus the current host and also a list of hostIds that seem to have enough CPU and RAM capacity to host this VM. - ListHostsCmd will call this service API if virtualmachineid is present in the request. - MigrateVmCmd is the new command added that takes in virtualmachineid and destination hostid - UserVmService will expose a new API: UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) ------------------------------------ API throws error in following cases: ------------------------------------ - User is not a root Admin. (‘Permission denied’) - A VM uses local storage, we cannot migrate it, so ‘listHosts’ will throw error. - We fail to migrate the VM on the chosen host. - API will support migration for XenServer only currently. So error is thrown if hypervisor is not XenServer (e.g KVM, vSphere etc) - Destination host is not in same cluster as source host. - VM is not in running state
This commit is contained in:
parent
65ca813c24
commit
ed9a11c6b6
|
|
@ -27,10 +27,14 @@ import com.cloud.api.ApiConstants;
|
|||
import com.cloud.api.BaseListCmd;
|
||||
import com.cloud.api.Implementation;
|
||||
import com.cloud.api.Parameter;
|
||||
import com.cloud.api.BaseCmd.CommandType;
|
||||
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)
|
||||
public class ListHostsCmd extends BaseListCmd {
|
||||
|
|
@ -63,7 +67,9 @@ public class ListHostsCmd extends BaseListCmd {
|
|||
@Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, description="the Zone ID for the host")
|
||||
private Long zoneId;
|
||||
|
||||
|
||||
@Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.LONG, required=false, description="lists hosts in the same cluster as this VM and flag hosts with enough CPU/RAm to host this VM")
|
||||
private Long virtualMachineId;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -96,7 +102,9 @@ public class ListHostsCmd extends BaseListCmd {
|
|||
return zoneId;
|
||||
}
|
||||
|
||||
|
||||
public Long getVirtualMachineId() {
|
||||
return virtualMachineId;
|
||||
}
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
|
@ -112,12 +120,30 @@ public class ListHostsCmd extends BaseListCmd {
|
|||
|
||||
@Override
|
||||
public void execute(){
|
||||
List<? extends Host> result = _mgr.searchForServers(this);
|
||||
List<? extends Host> result = new ArrayList<Host>();
|
||||
List<Long> hostIdsWithCapacity = new ArrayList<Long>();
|
||||
|
||||
if(getVirtualMachineId() != null){
|
||||
UserVm userVm = _userVmService.getUserVm(getVirtualMachineId());
|
||||
if (userVm == null) {
|
||||
throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId());
|
||||
}
|
||||
Pair<List<? extends Host>, List<Long>> hostsForMigration = _mgr.listHostsForMigrationOfVM(userVm, this.getStartIndex(), this.getPageSizeVal());
|
||||
result = hostsForMigration.first();
|
||||
hostIdsWithCapacity = hostsForMigration.second();
|
||||
}else{
|
||||
result = _mgr.searchForServers(this);
|
||||
}
|
||||
|
||||
ListResponse<HostResponse> response = new ListResponse<HostResponse>();
|
||||
List<HostResponse> hostResponses = new ArrayList<HostResponse>();
|
||||
for (Host host : result) {
|
||||
HostResponse hostResponse = _responseGenerator.createHostResponse(host);
|
||||
Boolean hasEnoughCapacity = false;
|
||||
if(hostIdsWithCapacity.contains(host.getId())){
|
||||
hasEnoughCapacity = true;
|
||||
}
|
||||
hostResponse.setHasEnoughCapacity(hasEnoughCapacity);
|
||||
hostResponse.setObjectName("host");
|
||||
hostResponses.add(hostResponse);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
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.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.host.Host;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.uservm.UserVm;
|
||||
|
||||
@Implementation(description="Attempts Migration of a 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());
|
||||
|
||||
private static final String s_name = "migrateVirtualMachine";
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
//////////////// 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() {
|
||||
UserVm userVm = _entityMgr.findById(UserVm.class, getVirtualMachineId());
|
||||
if (userVm != null) {
|
||||
return userVm.getAccountId();
|
||||
}
|
||||
|
||||
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: " + getVirtualMachineId() + " to host: "+ getHostId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(){
|
||||
UserVm userVm = _userVmService.getUserVm(getVirtualMachineId());
|
||||
if (userVm == null) {
|
||||
throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId());
|
||||
}
|
||||
|
||||
Host destinationHost = _resourceService.getHost(getHostId());
|
||||
if (destinationHost == null) {
|
||||
throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId());
|
||||
}
|
||||
try{
|
||||
UserVm migratedVm = _userVmService.migrateVirtualMachine(userVm, destinationHost);
|
||||
|
||||
if (migratedVm != null) {
|
||||
UserVmResponse response = _responseGenerator.createUserVmResponse(migratedVm);
|
||||
response.setResponseName(getCommandName());
|
||||
this.setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to migrate 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +143,9 @@ public class HostResponse extends BaseResponse {
|
|||
|
||||
@SerializedName("hosttags") @Param(description="comma-separated list of tags for the host")
|
||||
private String hostTags;
|
||||
|
||||
@SerializedName("hasEnoughCapacity") @Param(description="true if this host has enough CPU and RAM capacity to migrate a VM to it, false otherwise")
|
||||
private Boolean hasEnoughCapacity;
|
||||
|
||||
@Override
|
||||
public Long getObjectId() {
|
||||
|
|
@ -464,4 +467,12 @@ public class HostResponse extends BaseResponse {
|
|||
public void setHostTags(String hostTags) {
|
||||
this.hostTags = hostTags;
|
||||
}
|
||||
|
||||
public Boolean hasEnoughCapacity() {
|
||||
return hasEnoughCapacity;
|
||||
}
|
||||
|
||||
public void setHasEnoughCapacity(Boolean hasEnoughCapacity) {
|
||||
this.hasEnoughCapacity = hasEnoughCapacity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ public class EventTypes {
|
|||
public static final String EVENT_VM_REBOOT = "VM.REBOOT";
|
||||
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_RESETPASSWORD = "VM.RESETPASSWORD";
|
||||
public static final String EVENT_VM_MIGRATE = "VM.MIGRATE";
|
||||
|
||||
// Domain Router
|
||||
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
|
||||
|
|
|
|||
|
|
@ -71,4 +71,6 @@ public interface ResourceService {
|
|||
* @throws InvalidParameterValueException
|
||||
*/
|
||||
boolean deleteHost(DeleteHostCmd cmd) throws InvalidParameterValueException;
|
||||
|
||||
Host getHost(long hostId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ 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;
|
||||
|
|
@ -427,5 +428,13 @@ public interface ManagementService {
|
|||
String getVMPassword(GetVMPasswordCmd cmd);
|
||||
|
||||
Type findSystemVMTypeById(long instanceId);
|
||||
|
||||
/**
|
||||
* 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 hostIds that seem to have enough CPU and RAM capacity to host this VM.
|
||||
* @param UserVm vm The VM to migrate
|
||||
* @return Pair<List<? extends Host>, List<Long>> List of all Hosts in VM's cluster and list of HostIds with enough capacity
|
||||
*/
|
||||
Pair<List<? extends Host>, List<Long>> listHostsForMigrationOfVM(UserVm vm, Long startIndex, Long pageSize);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import com.cloud.dc.DataCenter;
|
|||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
|
|
@ -49,6 +50,7 @@ import com.cloud.storage.Volume;
|
|||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.utils.exception.ExecutionException;
|
||||
|
||||
public interface UserVmService {
|
||||
|
|
@ -270,4 +272,16 @@ public interface UserVmService {
|
|||
|
||||
UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException,
|
||||
StorageUnavailableException, ResourceAllocationException;
|
||||
UserVm getUserVm(long vmId);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return Host destinationHost to migrate the VM
|
||||
* @throws ManagementServerException in case we get error finding the VM or host or access errors or other internal errors.
|
||||
* @throws ConcurrentOperationException if there are multiple users working on the same VM.
|
||||
* @throws ResourceUnavailableException if the destination host to migrate the VM is not currently available.
|
||||
*/
|
||||
UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ changeServiceForVirtualMachine=com.cloud.api.commands.UpgradeVMCmd;15
|
|||
updateVirtualMachine=com.cloud.api.commands.UpdateVMCmd;15
|
||||
recoverVirtualMachine=com.cloud.api.commands.RecoverVMCmd;3
|
||||
listVirtualMachines=com.cloud.api.commands.ListVMsCmd;15
|
||||
getVMPassword=com.cloud.api.commands.GetVMPasswordCmd;15
|
||||
getVMPassword=com.cloud.api.commands.GetVMPasswordCmd;15
|
||||
migrateVirtualMachine=com.cloud.api.commands.MigrateVMCmd;1
|
||||
|
||||
#### snapshot commands
|
||||
createSnapshot=com.cloud.api.commands.CreateSnapshotCmd;15
|
||||
|
|
|
|||
|
|
@ -2809,6 +2809,10 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory,
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
public Host getHost(long hostId){
|
||||
return _hostDao.findById(hostId);
|
||||
}
|
||||
|
||||
// create capacity entries if none exist for this server
|
||||
private void createCapacityEntry(final StartupCommand startup, HostVO server) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import com.cloud.vm.VirtualMachine;
|
|||
public interface CapacityManager extends Manager {
|
||||
public boolean releaseVmCapacity(VirtualMachine vm, boolean moveFromReserved, boolean moveToReservered, Long hostId);
|
||||
|
||||
boolean allocateVmCapacity(VirtualMachine vm, boolean fromLastHost);
|
||||
void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost);
|
||||
|
||||
boolean checkIfHostHasCapacity(long hostId, Integer cpu, long ram, boolean checkFromReservedCapacity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ public class CapacityManagerImpl implements CapacityManager , StateListener<Stat
|
|||
|
||||
@DB
|
||||
@Override
|
||||
public boolean allocateVmCapacity(VirtualMachine vm, boolean fromLastHost) {
|
||||
public void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost) {
|
||||
|
||||
long hostId = vm.getHostId();
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ public class CapacityManagerImpl implements CapacityManager , StateListener<Stat
|
|||
CapacityVO capacityMem = _capacityDao.findByHostIdType(hostId, CapacityVO.CAPACITY_TYPE_MEMORY);
|
||||
|
||||
if (capacityCpu == null || capacityMem == null || svo == null) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
int cpu = svo.getCpu() * svo.getSpeed();
|
||||
|
|
@ -203,71 +203,53 @@ public class CapacityManagerImpl implements CapacityManager , StateListener<Stat
|
|||
long reservedMem = capacityMem.getReservedCapacity();
|
||||
long totalCpu = capacityCpu.getTotalCapacity();
|
||||
long totalMem = capacityMem.getTotalCapacity();
|
||||
|
||||
long freeCpu = totalCpu - (reservedCpu + usedCpu);
|
||||
long freeMem = totalMem - (reservedMem + usedMem);
|
||||
|
||||
boolean success = false;
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("We are allocating VM, increasing the used capacity of this host:"+ hostId);
|
||||
s_logger.debug("Current Used CPU: "+usedCpu + " , Free CPU:"+freeCpu+" ,Requested CPU: "+cpu);
|
||||
s_logger.debug("Current Used RAM: "+usedMem + " , Free RAM:"+freeMem+" ,Requested RAM: "+ram);
|
||||
}
|
||||
capacityCpu.setUsedCapacity(usedCpu + cpu);
|
||||
capacityMem.setUsedCapacity(usedMem + ram);
|
||||
|
||||
if (fromLastHost) {
|
||||
/*alloc from reserved*/
|
||||
long freeCpu = reservedCpu;
|
||||
long freeMem = reservedMem;
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("We need to allocate to the last host again, so trying to allocate from reserved capacity");
|
||||
s_logger.debug("Free CPU: "+freeCpu + " , Requested CPU: "+cpu);
|
||||
s_logger.debug("Free RAM: "+freeMem + " , Requested RAM: "+ram);
|
||||
s_logger.debug("We are allocating VM to the last host again, so adjusting the reserved capacity if it is not less than required");
|
||||
s_logger.debug("Reserved CPU: "+reservedCpu + " , Requested CPU: "+cpu);
|
||||
s_logger.debug("Reserved RAM: "+reservedMem + " , Requested RAM: "+ram);
|
||||
}
|
||||
if (reservedCpu >= cpu && reservedMem >= ram) {
|
||||
capacityCpu.setReservedCapacity(reservedCpu - cpu);
|
||||
capacityMem.setReservedCapacity(reservedMem - ram);
|
||||
|
||||
capacityCpu.setUsedCapacity(usedCpu + cpu);
|
||||
capacityMem.setUsedCapacity(usedMem + ram);
|
||||
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*alloc from free resource*/
|
||||
long freeCpu = totalCpu - (reservedCpu + usedCpu);
|
||||
long freeMem = totalMem - (reservedMem + usedMem);
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Free CPU: "+freeCpu + " , Requested CPU: "+cpu);
|
||||
s_logger.debug("Free RAM: "+freeMem + " , Requested RAM: "+ram);
|
||||
}
|
||||
if ((reservedCpu + usedCpu + cpu <= totalCpu) && (reservedMem + usedMem + ram <= totalMem)) {
|
||||
capacityCpu.setUsedCapacity(usedCpu + cpu);
|
||||
capacityMem.setUsedCapacity(usedMem + ram);
|
||||
success = true;
|
||||
if (!((reservedCpu + usedCpu + cpu <= totalCpu) && (reservedMem + usedMem + ram <= totalMem))) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Host doesnt seem to have enough free capacity, but increasing the used capacity anyways, since the VM is already starting on this host ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
s_logger.debug("Success in alloc cpu from host: " + hostId + ", old used: " + usedCpu + ", old reserved: " +
|
||||
reservedCpu + ", old total: " + totalCpu +
|
||||
"; new used:" + capacityCpu.getUsedCapacity() + ", reserved:" + capacityCpu.getReservedCapacity() + ", total: " + capacityCpu.getTotalCapacity() +
|
||||
"; requested cpu:" + cpu + ",alloc_from_last:" + fromLastHost);
|
||||
s_logger.debug("CPU STATS after allocation: for host: " + hostId + ", old used: " + usedCpu + ", old reserved: " +
|
||||
reservedCpu + ", old total: " + totalCpu +
|
||||
"; new used:" + capacityCpu.getUsedCapacity() + ", reserved:" + capacityCpu.getReservedCapacity() + ", total: " + capacityCpu.getTotalCapacity() +
|
||||
"; requested cpu:" + cpu + ",alloc_from_last:" + fromLastHost);
|
||||
|
||||
s_logger.debug("Success in alloc mem from host: " + hostId + ", old used: " + usedMem + ", old reserved: " +
|
||||
reservedMem + ", old total: " + totalMem + "; new used: " + capacityMem.getUsedCapacity() + ", reserved: " +
|
||||
capacityMem.getReservedCapacity() + ", total: " + capacityMem.getTotalCapacity() + "; requested mem: " + ram + ",alloc_from_last:" + fromLastHost);
|
||||
|
||||
_capacityDao.update(capacityCpu.getId(), capacityCpu);
|
||||
_capacityDao.update(capacityMem.getId(), capacityMem);
|
||||
} else {
|
||||
if (fromLastHost) {
|
||||
s_logger.debug("Failed to alloc resource from host: " + hostId + " reservedCpu: " + reservedCpu + ", requested cpu: " + cpu +
|
||||
", reservedMem: " + reservedMem + ", requested mem: " + ram);
|
||||
} else {
|
||||
s_logger.debug("Failed to alloc resource from host: " + hostId + " reservedCpu: " + reservedCpu + ", used cpu: " + usedCpu + ", requested cpu: " + cpu +
|
||||
", total cpu: " + totalCpu +
|
||||
", reservedMem: " + reservedMem + ", used Mem: " + usedMem + ", requested mem: " + ram + ", total Mem:" + totalMem);
|
||||
}
|
||||
}
|
||||
s_logger.debug("RAM STATS after allocation: for host: " + hostId + ", old used: " + usedMem + ", old reserved: " +
|
||||
reservedMem + ", old total: " + totalMem + "; new used: " + capacityMem.getUsedCapacity() + ", reserved: " +
|
||||
capacityMem.getReservedCapacity() + ", total: " + capacityMem.getTotalCapacity() + "; requested mem: " + ram + ",alloc_from_last:" + fromLastHost);
|
||||
|
||||
_capacityDao.update(capacityCpu.getId(), capacityCpu);
|
||||
_capacityDao.update(capacityMem.getId(), capacityMem);
|
||||
txn.commit();
|
||||
return success;
|
||||
} catch (Exception e) {
|
||||
txn.rollback();
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -297,8 +279,8 @@ public class CapacityManagerImpl implements CapacityManager , StateListener<Stat
|
|||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("We need to allocate to the last host again, so checking if there is enough reserved capacity");
|
||||
s_logger.debug("Free CPU: "+freeCpu + " , Requested CPU: "+cpu);
|
||||
s_logger.debug("Free RAM: "+freeMem + " , Requested RAM: "+ram);
|
||||
s_logger.debug("Reserved CPU: "+freeCpu + " , Requested CPU: "+cpu);
|
||||
s_logger.debug("Reserved RAM: "+freeMem + " , Requested RAM: "+ram);
|
||||
}
|
||||
/*alloc from reserved*/
|
||||
if (reservedCpu >= cpu){
|
||||
|
|
@ -462,7 +444,7 @@ public class CapacityManagerImpl implements CapacityManager , StateListener<Stat
|
|||
@Override
|
||||
public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vm, boolean status, Long oldHostId) {
|
||||
s_logger.debug("VM state transitted from :" + oldState + " to " + newState + " with event: " + event +
|
||||
"vm's original host id: " + vm.getLastHostId() + " new host id: " + vm.getHostId());
|
||||
"vm's original host id: " + vm.getLastHostId() + " new host id: " + vm.getHostId() + " host id before state transition: " + oldHostId);
|
||||
if (!status) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,5 +29,6 @@ public interface CapacityDao extends GenericDao<CapacityVO, Long> {
|
|||
CapacityVO findByHostIdType(Long hostId, short capacityType);
|
||||
void clearNonStorageCapacities2();
|
||||
List<CapacityVO> findByHostorPoolId(Long hostorPoolId);
|
||||
List<Long> orderClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone);
|
||||
List<Long> orderClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone);
|
||||
List<Long> listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||
|
||||
private SearchBuilder<CapacityVO> _hostIdTypeSearch;
|
||||
private SearchBuilder<CapacityVO> _hostOrPoolIdSearch;
|
||||
|
||||
private static final String LIST_HOSTS_IN_CLUSTER_WITH_ENOUGH_CAPACITY = "SELECT a.host_id FROM (host JOIN op_host_capacity a ON host.id = a.host_id AND host.cluster_id = ? AND host.type = ? " +
|
||||
"AND a.total_capacity - (a.used_capacity + a.reserved_capacity) >= ? and a.capacity_type = 1) " +
|
||||
"JOIN op_host_capacity b ON a.host_id = b.host_id AND b.total_capacity - (b.used_capacity + b.reserved_capacity) >= ? AND b.capacity_type = 0";
|
||||
|
||||
public CapacityDaoImpl() {
|
||||
_hostIdTypeSearch = createSearchBuilder();
|
||||
|
|
@ -200,5 +204,32 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||
} catch (Throwable e) {
|
||||
throw new CloudRuntimeException("Caught: " + sql, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Long> listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType){
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
PreparedStatement pstmt = null;
|
||||
List<Long> result = new ArrayList<Long>();
|
||||
|
||||
StringBuilder sql = new StringBuilder(LIST_HOSTS_IN_CLUSTER_WITH_ENOUGH_CAPACITY);
|
||||
try {
|
||||
pstmt = txn.prepareAutoCloseStatement(sql.toString());
|
||||
pstmt.setLong(1, clusterId);
|
||||
pstmt.setString(2, hostType);
|
||||
pstmt.setLong(3, requiredCpu);
|
||||
pstmt.setLong(4, requiredRam);
|
||||
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
result.add(rs.getLong(1));
|
||||
}
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
throw new CloudRuntimeException("DB Exception on: " + sql, e);
|
||||
} catch (Throwable e) {
|
||||
throw new CloudRuntimeException("Caught: " + sql, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ import com.cloud.exception.PermissionDeniedException;
|
|||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.StorageUnavailableException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.Host.Type;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.Status;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
|
|
@ -238,6 +239,7 @@ 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;
|
||||
|
|
@ -1224,11 +1226,80 @@ public class ManagementServerImpl implements ManagementServer {
|
|||
|
||||
return searchForServers(cmd.getStartIndex(), cmd.getPageSizeVal(), name, type, state, zone, pod, cluster, id, keyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<? extends Host>, List<Long>> listHostsForMigrationOfVM(UserVm vm, Long startIndex, Long pageSize) {
|
||||
//access check - only root admin can migrate VM
|
||||
Account caller = UserContext.current().getCaller();
|
||||
if(caller.getType() != Account.ACCOUNT_TYPE_ADMIN){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Caller is not a root admin, permission denied to migrate the VM");
|
||||
}
|
||||
throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!");
|
||||
}
|
||||
//business logic
|
||||
if(vm.getState() != State.Running){
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
|
||||
}
|
||||
throw new InvalidParameterValueException("VM is not Running, unable to migrate the vm " + vm);
|
||||
}
|
||||
|
||||
if(!vm.getHypervisorType().equals(HypervisorType.XenServer)){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug(vm + " is not XenServer, cannot migrate this VM.");
|
||||
}
|
||||
throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer only");
|
||||
}
|
||||
ServiceOfferingVO svcOffering = _offeringsDao.findById(vm.getServiceOfferingId());
|
||||
if(svcOffering.getUseLocalStorage()){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug(vm + " is using Local Storage, cannot migrate this VM.");
|
||||
}
|
||||
throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate");
|
||||
}
|
||||
long srcHostId = vm.getHostId();
|
||||
Host srcHost = _hostDao.findById(srcHostId);
|
||||
if(srcHost == null){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Unable to find the host with id: "+srcHostId+" of this VM:" + vm);
|
||||
}
|
||||
throw new InvalidParameterValueException("Unable to find the host with id: "+srcHostId+" of this VM:" + vm);
|
||||
}
|
||||
Long cluster = srcHost.getClusterId();
|
||||
Type hostType = srcHost.getType();
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Searching for all hosts in cluster: " +cluster+ " for migrating VM "+ vm);
|
||||
}
|
||||
|
||||
List<? extends Host> allHostsInCluster = searchForServers(startIndex, pageSize, null, hostType, null, null, null, cluster, null, null);
|
||||
//filter out the current host
|
||||
allHostsInCluster.remove(srcHost);
|
||||
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Other Hosts in this cluster: "+allHostsInCluster);
|
||||
}
|
||||
|
||||
int requiredCpu = svcOffering.getCpu() * svcOffering.getSpeed();
|
||||
long requiredRam = svcOffering.getRamSize() * 1024L * 1024L;
|
||||
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Searching for hosts in cluster: " +cluster+ " having required CPU: " +requiredCpu+ " and RAM:"+ requiredRam);
|
||||
}
|
||||
|
||||
List<Long> hostsWithCapacity = _capacityDao.listHostsWithEnoughCapacity(requiredCpu, requiredRam, cluster, hostType.name());
|
||||
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Hosts having capacity: "+hostsWithCapacity );
|
||||
}
|
||||
|
||||
return new Pair<List<? extends Host>, List<Long>>(allHostsInCluster, hostsWithCapacity);
|
||||
}
|
||||
|
||||
private List<HostVO> searchForServers(Long startIndex, Long pageSize, Object name, Object type, Object state, Object zone, Object pod, Object cluster, Object id, Object keyword) {
|
||||
Filter searchFilter = new Filter(HostVO.class, "id", Boolean.TRUE, startIndex, pageSize);
|
||||
SearchCriteria<HostVO> sc = _hostDao.createSearchCriteria();
|
||||
|
||||
|
||||
if (keyword != null) {
|
||||
SearchCriteria<HostVO> ssc = _hostDao.createSearchCriteria();
|
||||
ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
|
||||
|
|
@ -1260,7 +1331,7 @@ public class ManagementServerImpl implements ManagementServer {
|
|||
if (cluster != null) {
|
||||
sc.addAnd("clusterId", SearchCriteria.Op.EQ, cluster);
|
||||
}
|
||||
|
||||
|
||||
return _hostDao.search(sc, searchFilter);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,12 +95,14 @@ import com.cloud.event.dao.UsageEventDao;
|
|||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.exception.StorageUnavailableException;
|
||||
import com.cloud.ha.HighAvailabilityManager;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.DetailsDao;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
|
|
@ -197,6 +199,8 @@ import com.cloud.vm.dao.InstanceGroupVMMapDao;
|
|||
import com.cloud.vm.dao.NicDao;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.UserVmDetailsDao;
|
||||
import com.cloud.org.Cluster;
|
||||
|
||||
@Local(value={UserVmManager.class, UserVmService.class})
|
||||
public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager {
|
||||
private static final Logger s_logger = Logger.getLogger(UserVmManagerImpl.class);
|
||||
|
|
@ -2850,4 +2854,50 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
|
|||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public UserVm getUserVm(long vmId){
|
||||
return _vmDao.findById(vmId);
|
||||
}
|
||||
|
||||
public UserVm migrateVirtualMachine(UserVm vm, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException{
|
||||
//access check - only root admin can migrate VM
|
||||
Account caller = UserContext.current().getCaller();
|
||||
if(caller.getType() != Account.ACCOUNT_TYPE_ADMIN){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug("Caller is not a root admin, permission denied to migrate the VM");
|
||||
}
|
||||
throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!");
|
||||
}
|
||||
//business logic
|
||||
if(vm.getState() != State.Running){
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
|
||||
}
|
||||
throw new InvalidParameterValueException("VM is not Running, unable to migrate the vm " + vm);
|
||||
}
|
||||
if(!vm.getHypervisorType().equals(HypervisorType.XenServer)){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug(vm + " is not XenServer, cannot migrate this VM.");
|
||||
}
|
||||
throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer only");
|
||||
}
|
||||
|
||||
ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId());
|
||||
if(svcOffering.getUseLocalStorage()){
|
||||
if(s_logger.isDebugEnabled()){
|
||||
s_logger.debug(vm + " is using Local Storage, cannot migrate this VM.");
|
||||
}
|
||||
throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate");
|
||||
}
|
||||
|
||||
//call to core process
|
||||
DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId());
|
||||
HostPodVO pod = _podDao.findById(destinationHost.getPodId());
|
||||
long srcHostId = vm.getHostId();
|
||||
Cluster cluster = _clusterDao.findById(destinationHost.getClusterId());
|
||||
DeployDestination dest = new DeployDestination(dcVO, pod,cluster,destinationHost);
|
||||
|
||||
UserVmVO migratedVm = _itMgr.migrate((UserVmVO)vm, srcHostId, dest);
|
||||
return migratedVm;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.cloud.exception.AgentUnavailableException;
|
|||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InsufficientServerCapacityException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
|
|
@ -94,7 +95,7 @@ public interface VirtualMachineManager extends Manager {
|
|||
|
||||
boolean migrateAway(VirtualMachine.Type type, long vmid, long hostId) throws InsufficientServerCapacityException;
|
||||
|
||||
<T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException;
|
||||
<T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException;
|
||||
|
||||
<T extends VMInstanceVO> T reboot(T vm, Map<VirtualMachineProfile.Param, Object> params, User caller, Account account) throws InsufficientCapacityException, ResourceUnavailableException;
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ import com.cloud.exception.ConcurrentOperationException;
|
|||
import com.cloud.exception.ConnectionException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.InsufficientServerCapacityException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.ManagementServerException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.ha.HighAvailabilityManager;
|
||||
|
|
@ -900,6 +902,10 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
|
|||
if (e == Event.OperationSucceeded) {
|
||||
vm.setLastHostId(hostId);
|
||||
}
|
||||
}else if (oldState == State.Stopping ) {
|
||||
if (e == Event.OperationSucceeded) {
|
||||
vm.setLastHostId(vm.getHostId());
|
||||
}
|
||||
}
|
||||
return _stateMachine.transitTo(vm, e, hostId, _vmDao);
|
||||
}
|
||||
|
|
@ -948,15 +954,21 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException {
|
||||
public <T extends VMInstanceVO> T migrate(T vm, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException {
|
||||
s_logger.info("Migrating " + vm + " to " + dest);
|
||||
|
||||
long dstHostId = dest.getHost().getId();
|
||||
Host fromHost = _hostDao.findById(srcHostId);
|
||||
if (fromHost == null) {
|
||||
s_logger.info("Unable to find the host to migrate from: " + srcHostId);
|
||||
return null;
|
||||
throw new ManagementServerException("Unable to find the host to migrate from: " + srcHostId);
|
||||
}
|
||||
|
||||
if(fromHost.getClusterId().longValue() != dest.getCluster().getId()){
|
||||
s_logger.info("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId());
|
||||
throw new ManagementServerException("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId());
|
||||
}
|
||||
|
||||
VirtualMachineGuru<T> vmGuru = getVmGuru(vm);
|
||||
|
||||
vm = vmGuru.findById(vm.getId());
|
||||
|
|
@ -964,7 +976,14 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
|
|||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Unable to find the vm " + vm);
|
||||
}
|
||||
return null;
|
||||
throw new ManagementServerException("Unable to find a virtual machine with id " + vm.getId());
|
||||
}
|
||||
|
||||
if(vm.getState() != State.Running){
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
|
||||
}
|
||||
throw new ManagementServerException("VM is not Running, unable to migrate the vm " + vm);
|
||||
}
|
||||
|
||||
short alertType = AlertManager.ALERT_TYPE_USERVM_MIGRATE;
|
||||
|
|
@ -1008,7 +1027,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
|
|||
vm.setLastHostId(srcHostId);
|
||||
if (vm == null || vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) {
|
||||
s_logger.info("Migration cancelled because state has changed: " + vm);
|
||||
return null;
|
||||
throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
|
||||
}
|
||||
|
||||
boolean migrated = false;
|
||||
|
|
@ -1142,7 +1161,11 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
|
|||
vm = migrate(vm, srcHostId, dest);
|
||||
} catch (ResourceUnavailableException e) {
|
||||
s_logger.debug("Unable to migrate to unavailable " + dest);
|
||||
}
|
||||
} catch (ConcurrentOperationException e) {
|
||||
s_logger.debug("Unable to migrate VM due to: " + e.getMessage());
|
||||
} catch (ManagementServerException e) {
|
||||
s_logger.debug("Unable to migrate VM: " + e.getMessage());
|
||||
}
|
||||
if (vm != null) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue