This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push:
new de55766 Migrate/Stop VMs with local storage when preparing host for
maintenance (#4212)
de55766 is described below
commit de557663ec2a16a5372f608f01793b83bedfd6f3
Author: Gabriel Beims Bräscher <[email protected]>
AuthorDate: Mon Apr 19 04:41:42 2021 -0300
Migrate/Stop VMs with local storage when preparing host for maintenance
(#4212)
---
.../java/com/cloud/resource/ResourceManager.java | 8 ++
.../com/cloud/resource/ResourceManagerImpl.java | 92 +++++++++++++++++++++-
.../src/main/java/com/cloud/vm/UserVmManager.java | 2 +
.../main/java/com/cloud/vm/UserVmManagerImpl.java | 10 +--
4 files changed, 103 insertions(+), 9 deletions(-)
diff --git
a/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java
b/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java
index ade2eeb..2857bbc 100755
---
a/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java
+++
b/engine/components-api/src/main/java/com/cloud/resource/ResourceManager.java
@@ -53,6 +53,14 @@ public interface ResourceManager extends ResourceService,
Configurable {
"Number of retries when preparing a host into Maintenance Mode is
faulty before failing",
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.
* There can only be one type of listener per type of host.
diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
index f245502..a3b9df8 100755
--- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java
@@ -30,6 +30,18 @@ import java.util.Random;
import javax.inject.Inject;
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.command.admin.cluster.AddClusterCmd;
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
@@ -206,6 +218,10 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
@Inject
private CapacityDao _capacityDao;
@Inject
+ private DiskOfferingDao diskOfferingDao;
+ @Inject
+ private ServiceOfferingDao serviceOfferingDao;
+ @Inject
private HostDao _hostDao;
@Inject
private HostDetailsDao _hostDetailsDao;
@@ -226,6 +242,8 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
@Inject
private IPAddressDao _publicIPAddressDao;
@Inject
+ private DeploymentPlanningManager deploymentManager;
+ @Inject
private VirtualMachineManager _vmMgr;
@Inject
private VMInstanceDao _vmDao;
@@ -239,6 +257,8 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
private DedicatedResourceDao _dedicatedDao;
@Inject
private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
+ @Inject
+ private UserVmManager userVmManager;
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())){
//Migration is not supported for LXC Vms. Schedule restart
instead.
_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 {
s_logger.info("Maintenance: scheduling migration of VM " +
vm.getUuid() + " from host " + host.getUuid());
_haMgr.scheduleMigration(vm);
@@ -1282,6 +1315,32 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
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
public boolean maintain(final long hostId) throws
AgentUnavailableException {
final Boolean result = propagateResourceEvent(hostId,
ResourceState.Event.AdminAskMaintenance);
@@ -1322,9 +1381,13 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
}
if (_storageMgr.isLocalStorageActiveOnHost(host.getId())) {
- 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.");
+ 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.");
+ }
}
+
List<VMInstanceVO> migratingInVMs = _vmDao.findByHostInStates(hostId,
State.Migrating);
+
if (migratingInVMs.size() > 0) {
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)
*/
@@ -3094,6 +3182,6 @@ public class ResourceManagerImpl extends ManagerBase
implements ResourceManager,
@Override
public ConfigKey<?>[] getConfigKeys() {
- return new ConfigKey[0];
+ return new ConfigKey<?>[] {KvmSshToAgentEnabled,
HOST_MAINTENANCE_LOCAL_STRATEGY};
}
}
diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java
b/server/src/main/java/com/cloud/vm/UserVmManager.java
index e8f7097..e4206ef 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManager.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManager.java
@@ -96,6 +96,8 @@ public interface UserVmManager extends UserVmService {
void removeInstanceFromInstanceGroup(long vmId);
+ boolean isVMUsingLocalStorage(VMInstanceVO vm);
+
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)
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index f71ae19..fc23028 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -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;
List<VolumeVO> volumes = _volsDao.findByInstance(vm.getId());
@@ -5892,9 +5892,7 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
}
if (!isOnSupportedHypevisorForMigration(vm)) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv,
cannot migrate this VM form hypervisor type " + vm.getHypervisorType());
- }
+ s_logger.error(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv,
cannot migrate this VM from hypervisor type " + vm.getHypervisorType());
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 (s_logger.isDebugEnabled()) {
- s_logger.debug(vm + " is using Local Storage, cannot migrate
this VM.");
- }
+ s_logger.error(vm + " is using Local Storage, cannot migrate this
VM.");
throw new InvalidParameterValueException("Unsupported operation,
VM uses Local storage, cannot migrate");
}