package org.ovirt.engine.core.bll; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import javax.inject.Inject; import org.ovirt.engine.core.bll.network.cluster.NetworkHelper; import org.ovirt.engine.core.bll.network.macpool.ReadMacPool; import org.ovirt.engine.core.bll.utils.VmDeviceUtils; import org.ovirt.engine.core.bll.validator.VmValidator; import org.ovirt.engine.core.common.businessentities.ActionGroup; import org.ovirt.engine.core.common.businessentities.ArchitectureType; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.network.Network; import org.ovirt.engine.core.common.businessentities.network.VmNic; import org.ovirt.engine.core.common.businessentities.profiles.CpuProfile; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.dao.ClusterDao; import org.ovirt.engine.core.dao.network.NetworkDao; import org.ovirt.engine.core.dao.network.VmNicDao; import org.ovirt.engine.core.dao.profiles.CpuProfileDao; import org.ovirt.engine.core.di.Injector; import org.ovirt.engine.core.utils.ReplacementUtils; public class ChangeVmClusterValidator { @Inject private ClusterDao clusterDao; @Inject private VmNicDao vmNicDao; @Inject private CpuProfileDao cpuProfileDao; @Inject private NetworkDao networkDao; @Inject private VmDeviceUtils vmDeviceUtils; @Inject private VmHandler vmHandler; private VmCommand parentCommand; private final Guid targetClusterId; private Version vmCompatibilityVersion; private Cluster targetCluster; public static ChangeVmClusterValidator create(VmCommand parentCommand, Guid targetClusterId, Version vmCompatibilityVersion) { return Injector.injectMembers(new ChangeVmClusterValidator(parentCommand, targetClusterId, vmCompatibilityVersion)); } ChangeVmClusterValidator(VmCommand parentCommand, Guid targetClusterId, Version vmCustomCompatibilityVersion) { this.parentCommand = parentCommand; this.targetClusterId = targetClusterId; this.vmCompatibilityVersion = vmCustomCompatibilityVersion; } protected boolean validate() { VM vm = parentCommand.getVm(); if (vm == null) { parentCommand.addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_FOUND); return false; } else { targetCluster = clusterDao.get(targetClusterId); if (targetCluster == null) { parentCommand.addValidationMessage(EngineMessage.VM_CLUSTER_IS_NOT_VALID); return false; } // Check that the target cluster is in the same data center. if (!targetCluster.getStoragePoolId().equals(vm.getStoragePoolId())) { parentCommand.addValidationMessage(EngineMessage.VM_CANNOT_MOVE_TO_CLUSTER_IN_OTHER_STORAGE_POOL); return false; } if (vmCompatibilityVersion == null) { vmCompatibilityVersion = targetCluster.getCompatibilityVersion(); } List<VmNic> interfaces = vmNicDao.getAllForVm(vm.getId()); if (!validateDestinationClusterContainsNetworks(interfaces)) { return false; } // Check if VM static parameters are compatible for new cluster. ValidationResult isCpuSocketsValid = VmValidator.validateCpuSockets(vm.getStaticData(), vmCompatibilityVersion); if (!isCpuSocketsValid.isValid()) { return parentCommand.validate(isCpuSocketsValid); } // Check if the display type is supported if (!vmHandler.isGraphicsAndDisplaySupported( vm.getOs(), vmDeviceUtils.getGraphicsTypesOfEntity(vm.getId()), vm.getDefaultDisplayType(), parentCommand.getReturnValue().getValidationMessages(), vmCompatibilityVersion)) { return false; } if (vmDeviceUtils.hasVirtioScsiController(vm.getId())) { // Verify OS compatibility if (!VmHandler.isOsTypeSupportedForVirtioScsi( vm.getOs(), vmCompatibilityVersion, parentCommand.getReturnValue().getValidationMessages())) { return false; } } // A existing VM cannot be changed into a cluster without a defined architecture if (targetCluster.getArchitecture() == ArchitectureType.undefined) { return parentCommand.failValidation(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_UNDEFINED_ARCHITECTURE); } else if (targetCluster.getArchitecture() != vm.getClusterArch()) { return parentCommand.failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_CLUSTER_DIFFERENT_ARCHITECTURES); } // Cluster must have a cpu profile List<CpuProfile> cpuProfiles = cpuProfileDao.getAllForCluster( targetClusterId, parentCommand.getUserId(), true, ActionGroup.ASSIGN_CPU_PROFILE); if (cpuProfiles.isEmpty()) { return parentCommand.failValidation(EngineMessage.ACTION_TYPE_CPU_PROFILE_EMPTY); } } return true; } public ValidationResult validateCanMoveMacs(ReadMacPool macPoolForTargetCluster, List<String> macsToMigrate) { if (macPoolForTargetCluster.isDuplicateMacAddressesAllowed()) { return ValidationResult.VALID; } List<String> nonMigratableMacs = macsToMigrate.stream() .filter(macPoolForTargetCluster::isMacInUse) .collect(Collectors.toList()); if (nonMigratableMacs.isEmpty()) { return ValidationResult.VALID; } EngineMessage engineMessage = EngineMessage.ACTION_TYPE_FAILED_CANNOT_UPDATE_VM_TARGET_CLUSTER_HAS_DUPLICATED_MACS; Collection<String> replacements = ReplacementUtils.getListVariableAssignmentString(engineMessage, nonMigratableMacs); return new ValidationResult(engineMessage, replacements); } /** * Checks that the destination cluster has all the networks that the given NICs require.<br> * No network on a NIC is allowed (it's checked when running VM). * * @param interfaces The NICs to check networks on. * @return Whether the destination cluster has all networks configured or not. */ private boolean validateDestinationClusterContainsNetworks(List<VmNic> interfaces) { List<Network> networks = networkDao.getAllForCluster(targetClusterId); StringBuilder missingNets = new StringBuilder(); for (VmNic iface : interfaces) { Network network = NetworkHelper.getNetworkByVnicProfileId(iface.getVnicProfileId()); if (network != null) { boolean exists = false; for (Network net : networks) { if (net.getName().equals(network.getName())) { exists = true; break; } } if (!exists) { if (missingNets.length() > 0) { missingNets.append(", "); } missingNets.append(network.getName()); } } } if (missingNets.length() > 0) { parentCommand.addValidationMessage(EngineMessage.MOVE_VM_CLUSTER_MISSING_NETWORK); parentCommand.addValidationMessageVariable("networks", missingNets.toString()); return false; } return true; } }