package org.ovirt.engine.core.bll.startup.recovery;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.ejb.DependsOn;
import javax.ejb.Startup;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.VdsDynamic;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.VdsDao;
import org.ovirt.engine.core.dao.VdsDynamicDao;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.vdsbroker.ResourceManager;
@Startup
@Singleton
@DependsOn("InitBackendServicesOnStartupBean")
final class RecoveryStartupBean {
private final ResourceManager resourceManager;
private final VdsDao hostDao;
private final VdsDynamicDao hostDynamicDao;
private final VmDao vmDao;
@Inject
RecoveryStartupBean(
ResourceManager resourceManager,
VdsDao hostDao,
VdsDynamicDao hostDynamicDao,
VmDao vmDao) {
Objects.requireNonNull(resourceManager, "resourceManager cannot be null");
Objects.requireNonNull(hostDao, "hostDao cannot be null");
Objects.requireNonNull(hostDynamicDao, "hostDynamicDao cannot be null");
Objects.requireNonNull(vmDao, "vmDao cannot be null");
this.resourceManager = resourceManager;
this.hostDynamicDao = hostDynamicDao;
this.hostDao = hostDao;
this.vmDao = vmDao;
}
@PostConstruct
void recovery() {
final List<VDS> allHosts = hostDao.getAll();
final Set<Guid> nonResponsiveHosts = findNonResponsiveHosts(allHosts);
final boolean vmsInTransition = setVmsInTransitionAsUnknown(nonResponsiveHosts);
if (!vmsInTransition) {
updateHostsResources(allHosts);
}
}
/**
* Clean pending memory and CPUs
* (meaning we tried to start a VM and the engine crashed before telling VDSM about it).
*/
private void updateHostsResources(List<VDS> hosts) {
final List<VdsDynamic> updatedEntities = new ArrayList<>();
for (VDS host : hosts) {
boolean hostDynamicDataTobeSaved = false;
if (host.getPendingVcpusCount() != 0) {
host.setPendingVcpusCount(0);
hostDynamicDataTobeSaved = true;
}
if (host.getPendingVmemSize() != 0) {
host.setPendingVmemSize(0);
hostDynamicDataTobeSaved = true;
}
if (hostDynamicDataTobeSaved) {
updatedEntities.add(host.getDynamicData());
}
}
hostDynamicDao.updateAllInBatch(updatedEntities);
}
/**
* Sets "unknown" status on VMs that are in transition state on the given hosts.
* Cleanup all vms dynamic data. This is defensive code on power crash.
*
* @param hostIds host ids to be inspected
* @return <code>true</code> if VMs in transition state were found
*/
private boolean setVmsInTransitionAsUnknown(Set<Guid> hostIds) {
// Is there any VM that is not fully Up or fully Down?
boolean runningVmsInTransition = false;
final List<VM> vms = vmDao.getAll();
for (VM vm : vms) {
if (!vm.isNotRunning()) {
if (vm.getRunOnVds() != null && hostIds.contains(vm.getRunOnVds())) {
resourceManager.setVmUnknown(vm);
}
}
if (isVmInTransition(vm)) {
runningVmsInTransition = true;
}
}
return runningVmsInTransition;
}
private boolean isVmInTransition(VM vm) {
return vm.isRunning() && vm.getStatus() != VMStatus.Up;
}
private Set<Guid> findNonResponsiveHosts(List<VDS> hosts) {
final Set<Guid> nonResponsiveHosts = new HashSet<>();
for (VDS host : hosts) {
if (host.getStatus() == VDSStatus.NonResponsive) {
nonResponsiveHosts.add(host.getId());
}
}
return nonResponsiveHosts;
}
}