package org.ovirt.engine.core.bll.validator; import static java.util.Objects.requireNonNull; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.hostdev.HostDeviceManager; import org.ovirt.engine.core.common.businessentities.MigrationSupport; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VmNumaNode; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.utils.OS; @Singleton public class InClusterUpgradeValidator { @Inject HostDeviceManager hostDeviceManager; public ValidationResult isUpgradePossible(Collection<VDS> hosts, Collection<VM> vms) { requireNonNull(hosts); requireNonNull(vms); final List<String> errors = new ArrayList<>(); for (final VDS host : hosts) { final OS hostOs = OS.fromPackageVersionString(host.getHostOs()); if (!hostOs.isValid()) { errors.addAll(toHostEngineMessage(host, EngineMessage.CLUSTER_UPGRADE_DETAIL_HOST_INVALID_OS)); } } for (final VM vm : vms) { errors.addAll(checkVmReadyForUpgrade(vm)); } if (errors.isEmpty()) { return ValidationResult.VALID; } else { return new ValidationResult(EngineMessage.CLUSTER_UPGRADE_CAN_NOT_BE_STARTED, errors); } } public ValidationResult isVmReadyForUpgrade(final VM vm) { requireNonNull(vm); List<String> vmErrors = checkVmReadyForUpgrade(vm); if (vmErrors.isEmpty()) { return ValidationResult.VALID; } else { return new ValidationResult(EngineMessage.BOUND_TO_HOST_WHILE_UPGRADING_CLUSTER, vmErrors); } } protected List<String> checkVmReadyForUpgrade(final VM vm) { requireNonNull(vm); final List<String> errors = new ArrayList<>(); if (vm.getStatus().isSuspended()) { errors.addAll(toVmEngineMessage(vm, EngineMessage.CLUSTER_UPGRADE_DETAIL_VM_SUSPENDED)); } if (!StringUtils.isEmpty(vm.getCpuPinning())) { errors.addAll(toVmEngineMessage(vm, EngineMessage.CLUSTER_UPGRADE_DETAIL_VM_CPUS_PINNED)); } for (VmNumaNode vmNumaNode : vm.getvNumaNodeList()) { if (!vmNumaNode.getVdsNumaNodeList().isEmpty()) { errors.addAll(toVmEngineMessage(vm, EngineMessage.CLUSTER_UPGRADE_DETAIL_VM_NUMA_PINNED)); break; } } if (MigrationSupport.MIGRATABLE != vm.getMigrationSupport()) { errors.addAll(toVmEngineMessage(vm, EngineMessage.CLUSTER_UPGRADE_DETAIL_VM_NOT_MIGRATABLE)); } //TODO use a more efficient way, this does a db call per VM if (hostDeviceManager.checkVmNeedsDirectPassthrough(vm)) { errors.addAll(toVmEngineMessage(vm, EngineMessage.CLUSTER_UPGRADE_DETAIL_VM_NEEDS_PASSTHROUGH)); } return errors; } public ValidationResult isUpgradeDone(Collection<VDS> hosts) { requireNonNull(hosts); final List<String> errors = new ArrayList<>(); final Map<String, Set<Integer>> majorVersions = new LinkedHashMap<>(); final Map<String, Set<VDS>> osToHostIdMap = new HashMap<>(); for (final VDS host : hosts) { final OS hostOs = OS.fromPackageVersionString(host.getHostOs()); if (!hostOs.isValid()) { errors.addAll(toHostEngineMessage(host, EngineMessage.CLUSTER_UPGRADE_DETAIL_HOST_INVALID_OS)); } else { putMajorVersion(majorVersions, hostOs); putHost(osToHostIdMap, host, hostOs); } } for (Map.Entry<String, Set<Integer>> entry : majorVersions.entrySet()) { if (entry.getValue().size() > 1) { final int newestMajorVersion = Collections.max(entry.getValue()); for (VDS host : osToHostIdMap.get(entry.getKey())) { final OS hostOs = OS.fromPackageVersionString(host.getHostOs()); if (hostOs.getVersion().getMajor() < newestMajorVersion) { errors.addAll(toHostEngineMessage(host, EngineMessage.CLUSTER_UPGRADE_DETAIL_HOST_RUNS_TOO_OLD_OS)); } } } } if (errors.isEmpty()) { return ValidationResult.VALID; } else { return new ValidationResult(EngineMessage.CLUSTER_UPGRADE_NOT_FINISHED, errors); } } private static void putMajorVersion(Map<String, Set<Integer>> majorVersions, OS hostOs) { if (!majorVersions.containsKey(hostOs.getOsFamily())) { majorVersions.put(hostOs.getOsFamily(), new HashSet<>()); } majorVersions.get(hostOs.getOsFamily()).add(hostOs.getVersion().getMajor()); } private static void putHost(Map<String, Set<VDS>> osToHostIdMap, VDS host, OS hostOs) { if (!osToHostIdMap.containsKey(hostOs.getOsFamily())) { osToHostIdMap.put(hostOs.getOsFamily(), new HashSet<>()); } osToHostIdMap.get(hostOs.getOsFamily()).add(host); } private List<String> toVmEngineMessage(final VM vm, final EngineMessage error) { return Arrays.asList(error.name(), String.format("$%1$s %2$s", "vmName", vm.getName()), String.format("$%1$s %2$s", "vmId", vm.getId())); } private List<String> toHostEngineMessage(final VDS host, final EngineMessage error) { return Arrays.asList(error.name(), String.format("$%1$s %2$s", "hostName", host.getName()), String.format("$%1$s %2$s", "hostId", host.getId())); } }