mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Migrate/Stop VMs with local storage when preparing host for maintenance (#4212)
This commit is contained in:
parent
f42024714c
commit
de557663ec
@ -53,6 +53,14 @@ public interface ResourceManager extends ResourceService, Configurable {
|
|||||||
"Number of retries when preparing a host into Maintenance Mode is faulty before failing",
|
"Number of retries when preparing a host into Maintenance Mode is faulty before failing",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
|
ConfigKey<String> HOST_MAINTENANCE_LOCAL_STRATEGY = new ConfigKey<>("Advanced", String.class,
|
||||||
|
"host.maintenance.local.storage.strategy", "Error",
|
||||||
|
"Defines the strategy towards VMs with volumes on local storage when putting a host in maintenance. "
|
||||||
|
+ "The default strategy is 'Error', preventing maintenance in such a case. "
|
||||||
|
+ "Choose 'Migration' strategy to migrate away VMs running on local storage. "
|
||||||
|
+ "To force-stop VMs, choose 'ForceStop' strategy",
|
||||||
|
true, ConfigKey.Scope.Global);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a listener for different types of resource life cycle events.
|
* Register a listener for different types of resource life cycle events.
|
||||||
* There can only be one type of listener per type of host.
|
* There can only be one type of listener per type of host.
|
||||||
|
|||||||
@ -30,6 +30,18 @@ import java.util.Random;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import com.cloud.deploy.DataCenterDeployment;
|
||||||
|
import com.cloud.deploy.DeployDestination;
|
||||||
|
import com.cloud.deploy.DeploymentPlanner;
|
||||||
|
import com.cloud.deploy.DeploymentPlanningManager;
|
||||||
|
import com.cloud.exception.InsufficientServerCapacityException;
|
||||||
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.service.ServiceOfferingVO;
|
||||||
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
|
import com.cloud.storage.dao.DiskOfferingDao;
|
||||||
|
import com.cloud.vm.UserVmManager;
|
||||||
|
import com.cloud.vm.VirtualMachineProfile;
|
||||||
|
import com.cloud.vm.VirtualMachineProfileImpl;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
|
import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
|
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
|
||||||
@ -206,6 +218,10 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
@Inject
|
@Inject
|
||||||
private CapacityDao _capacityDao;
|
private CapacityDao _capacityDao;
|
||||||
@Inject
|
@Inject
|
||||||
|
private DiskOfferingDao diskOfferingDao;
|
||||||
|
@Inject
|
||||||
|
private ServiceOfferingDao serviceOfferingDao;
|
||||||
|
@Inject
|
||||||
private HostDao _hostDao;
|
private HostDao _hostDao;
|
||||||
@Inject
|
@Inject
|
||||||
private HostDetailsDao _hostDetailsDao;
|
private HostDetailsDao _hostDetailsDao;
|
||||||
@ -226,6 +242,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
@Inject
|
@Inject
|
||||||
private IPAddressDao _publicIPAddressDao;
|
private IPAddressDao _publicIPAddressDao;
|
||||||
@Inject
|
@Inject
|
||||||
|
private DeploymentPlanningManager deploymentManager;
|
||||||
|
@Inject
|
||||||
private VirtualMachineManager _vmMgr;
|
private VirtualMachineManager _vmMgr;
|
||||||
@Inject
|
@Inject
|
||||||
private VMInstanceDao _vmDao;
|
private VMInstanceDao _vmDao;
|
||||||
@ -239,6 +257,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
private DedicatedResourceDao _dedicatedDao;
|
private DedicatedResourceDao _dedicatedDao;
|
||||||
@Inject
|
@Inject
|
||||||
private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
|
private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
|
||||||
|
@Inject
|
||||||
|
private UserVmManager userVmManager;
|
||||||
|
|
||||||
private List<? extends Discoverer> _discoverers;
|
private List<? extends Discoverer> _discoverers;
|
||||||
|
|
||||||
@ -1273,6 +1293,19 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
} else if (HypervisorType.LXC.equals(host.getHypervisorType()) && VirtualMachine.Type.User.equals(vm.getType())){
|
} else if (HypervisorType.LXC.equals(host.getHypervisorType()) && VirtualMachine.Type.User.equals(vm.getType())){
|
||||||
//Migration is not supported for LXC Vms. Schedule restart instead.
|
//Migration is not supported for LXC Vms. Schedule restart instead.
|
||||||
_haMgr.scheduleRestart(vm, false);
|
_haMgr.scheduleRestart(vm, false);
|
||||||
|
} else if (userVmManager.isVMUsingLocalStorage(vm)) {
|
||||||
|
if (isMaintenanceLocalStrategyForceStop()) {
|
||||||
|
_haMgr.scheduleStop(vm, hostId, WorkType.ForceStop);
|
||||||
|
} else if (isMaintenanceLocalStrategyMigrate()) {
|
||||||
|
migrateAwayVmWithVolumes(host, vm);
|
||||||
|
} else if (!isMaintenanceLocalStrategyDefault()){
|
||||||
|
String logMessage = String.format(
|
||||||
|
"Unsupported host.maintenance.local.storage.strategy: %s. Please set a strategy according to the global settings description: "
|
||||||
|
+ "'Error', 'Migration', or 'ForceStop'.",
|
||||||
|
HOST_MAINTENANCE_LOCAL_STRATEGY.value().toString());
|
||||||
|
s_logger.error(logMessage);
|
||||||
|
throw new CloudRuntimeException("There are active VMs using the host's local storage pool. Please stop all VMs on this host that use local storage.");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
s_logger.info("Maintenance: scheduling migration of VM " + vm.getUuid() + " from host " + host.getUuid());
|
s_logger.info("Maintenance: scheduling migration of VM " + vm.getUuid() + " from host " + host.getUuid());
|
||||||
_haMgr.scheduleMigration(vm);
|
_haMgr.scheduleMigration(vm);
|
||||||
@ -1282,6 +1315,32 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for Hosts able to allocate the VM and migrates the VM with its volume.
|
||||||
|
*/
|
||||||
|
private void migrateAwayVmWithVolumes(HostVO host, VMInstanceVO vm) {
|
||||||
|
final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null);
|
||||||
|
ServiceOfferingVO offeringVO = serviceOfferingDao.findById(vm.getServiceOfferingId());
|
||||||
|
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offeringVO, null, null);
|
||||||
|
plan.setMigrationPlan(true);
|
||||||
|
DeployDestination dest = null;
|
||||||
|
try {
|
||||||
|
dest = deploymentManager.planDeployment(profile, plan, new DeploymentPlanner.ExcludeList(), null);
|
||||||
|
} catch (InsufficientServerCapacityException e) {
|
||||||
|
throw new CloudRuntimeException(String.format("Maintenance failed, could not find deployment destination for VM [id=%s, name=%s].", vm.getId(), vm.getInstanceName()),
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
Host destHost = dest.getHost();
|
||||||
|
|
||||||
|
try {
|
||||||
|
_vmMgr.migrateWithStorage(vm.getUuid(), host.getId(), destHost.getId(), null);
|
||||||
|
} catch (ResourceUnavailableException e) {
|
||||||
|
throw new CloudRuntimeException(
|
||||||
|
String.format("Maintenance failed, could not migrate VM [id=%s, name=%s] with local storage from host [id=%s, name=%s] to host [id=%s, name=%s].", vm.getId(),
|
||||||
|
vm.getInstanceName(), host.getId(), host.getName(), destHost.getId(), destHost.getName()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean maintain(final long hostId) throws AgentUnavailableException {
|
public boolean maintain(final long hostId) throws AgentUnavailableException {
|
||||||
final Boolean result = propagateResourceEvent(hostId, ResourceState.Event.AdminAskMaintenance);
|
final Boolean result = propagateResourceEvent(hostId, ResourceState.Event.AdminAskMaintenance);
|
||||||
@ -1322,9 +1381,13 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_storageMgr.isLocalStorageActiveOnHost(host.getId())) {
|
if (_storageMgr.isLocalStorageActiveOnHost(host.getId())) {
|
||||||
|
if(!isMaintenanceLocalStrategyMigrate() && !isMaintenanceLocalStrategyForceStop()) {
|
||||||
throw new CloudRuntimeException("There are active VMs using the host's local storage pool. Please stop all VMs on this host that use local storage.");
|
throw new CloudRuntimeException("There are active VMs using the host's local storage pool. Please stop all VMs on this host that use local storage.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<VMInstanceVO> migratingInVMs = _vmDao.findByHostInStates(hostId, State.Migrating);
|
List<VMInstanceVO> migratingInVMs = _vmDao.findByHostInStates(hostId, State.Migrating);
|
||||||
|
|
||||||
if (migratingInVMs.size() > 0) {
|
if (migratingInVMs.size() > 0) {
|
||||||
throw new CloudRuntimeException("Host contains incoming VMs migrating. Please wait for them to complete before putting to maintenance.");
|
throw new CloudRuntimeException("Host contains incoming VMs migrating. Please wait for them to complete before putting to maintenance.");
|
||||||
}
|
}
|
||||||
@ -1350,6 +1413,31 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isMaintenanceLocalStrategyMigrate() {
|
||||||
|
if(org.apache.commons.lang3.StringUtils.isBlank(HOST_MAINTENANCE_LOCAL_STRATEGY.value())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return HOST_MAINTENANCE_LOCAL_STRATEGY.value().toLowerCase().equals(WorkType.Migration.toString().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isMaintenanceLocalStrategyForceStop() {
|
||||||
|
if(org.apache.commons.lang3.StringUtils.isBlank(HOST_MAINTENANCE_LOCAL_STRATEGY.value())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return HOST_MAINTENANCE_LOCAL_STRATEGY.value().toLowerCase().equals(WorkType.ForceStop.toString().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the host.maintenance.local.storage.strategy is the Default: "Error", blank, empty, or null.
|
||||||
|
*/
|
||||||
|
protected boolean isMaintenanceLocalStrategyDefault() {
|
||||||
|
if (org.apache.commons.lang3.StringUtils.isBlank(HOST_MAINTENANCE_LOCAL_STRATEGY.value().toString())
|
||||||
|
|| HOST_MAINTENANCE_LOCAL_STRATEGY.value().toLowerCase().equals(State.Error.toString().toLowerCase())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add VNC details as user VM details for each VM in 'vms' (KVM hosts only)
|
* Add VNC details as user VM details for each VM in 'vms' (KVM hosts only)
|
||||||
*/
|
*/
|
||||||
@ -3094,6 +3182,6 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey[0];
|
return new ConfigKey<?>[] {KvmSshToAgentEnabled, HOST_MAINTENANCE_LOCAL_STRATEGY};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,6 +96,8 @@ public interface UserVmManager extends UserVmService {
|
|||||||
|
|
||||||
void removeInstanceFromInstanceGroup(long vmId);
|
void removeInstanceFromInstanceGroup(long vmId);
|
||||||
|
|
||||||
|
boolean isVMUsingLocalStorage(VMInstanceVO vm);
|
||||||
|
|
||||||
boolean expunge(UserVmVO vm, long callerUserId, Account caller);
|
boolean expunge(UserVmVO vm, long callerUserId, Account caller);
|
||||||
|
|
||||||
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
|
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
|
||||||
|
|||||||
@ -5839,7 +5839,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isVMUsingLocalStorage(VMInstanceVO vm) {
|
public boolean isVMUsingLocalStorage(VMInstanceVO vm) {
|
||||||
boolean usesLocalStorage = false;
|
boolean usesLocalStorage = false;
|
||||||
|
|
||||||
List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
|
List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
|
||||||
@ -5892,9 +5892,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isOnSupportedHypevisorForMigration(vm)) {
|
if (!isOnSupportedHypevisorForMigration(vm)) {
|
||||||
if (s_logger.isDebugEnabled()) {
|
s_logger.error(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv, cannot migrate this VM from hypervisor type " + vm.getHypervisorType());
|
||||||
s_logger.debug(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv, cannot migrate this VM form hypervisor type " + vm.getHypervisorType());
|
|
||||||
}
|
|
||||||
throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv/Ovm3 only");
|
throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv/Ovm3 only");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5903,9 +5901,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isVMUsingLocalStorage(vm)) {
|
if (isVMUsingLocalStorage(vm)) {
|
||||||
if (s_logger.isDebugEnabled()) {
|
s_logger.error(vm + " is using Local Storage, cannot migrate this VM.");
|
||||||
s_logger.debug(vm + " is using Local Storage, cannot migrate this VM.");
|
|
||||||
}
|
|
||||||
throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate");
|
throw new InvalidParameterValueException("Unsupported operation, VM uses Local storage, cannot migrate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user