package org.ovirt.engine.core.bll;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.network.cluster.DefaultManagementNetworkFinder;
import org.ovirt.engine.core.bll.network.cluster.NetworkClusterValidatorBase;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.utils.RngDeviceUtils;
import org.ovirt.engine.core.bll.utils.VersionSupport;
import org.ovirt.engine.core.bll.validator.ClusterValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.FeatureSupported;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.HasRngDevice;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.ManagementNetworkOnClusterOperationParameters;
import org.ovirt.engine.core.common.action.UpdateVmTemplateParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.action.VdsActionParameters;
import org.ovirt.engine.core.common.action.VmManagementParametersBase;
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.DisplayType;
import org.ovirt.engine.core.common.businessentities.MigrateOnErrorOptions;
import org.ovirt.engine.core.common.businessentities.OriginType;
import org.ovirt.engine.core.common.businessentities.StoragePool;
import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature;
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.VmRngDevice;
import org.ovirt.engine.core.common.businessentities.VmStatic;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity;
import org.ovirt.engine.core.common.businessentities.network.NetworkCluster;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.qualifiers.MomPolicyUpdate;
import org.ovirt.engine.core.common.utils.CompatibilityVersionUtils;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.validation.group.UpdateEntity;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase;
import org.ovirt.engine.core.dao.ClusterDao;
import org.ovirt.engine.core.dao.ClusterFeatureDao;
import org.ovirt.engine.core.dao.StoragePoolDao;
import org.ovirt.engine.core.dao.SupportedHostFeatureDao;
import org.ovirt.engine.core.dao.VdsDao;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.dao.VmStaticDao;
import org.ovirt.engine.core.dao.VmTemplateDao;
import org.ovirt.engine.core.dao.gluster.GlusterVolumeDao;
import org.ovirt.engine.core.dao.network.InterfaceDao;
import org.ovirt.engine.core.dao.network.NetworkClusterDao;
import org.ovirt.engine.core.dao.network.NetworkDao;
import org.ovirt.engine.core.vdsbroker.ResourceManager;
import org.ovirt.engine.core.vdsbroker.VmManager;
public class UpdateClusterCommand<T extends ManagementNetworkOnClusterOperationParameters> extends
ClusterOperationCommandBase<T> implements RenamedEntityInfoProvider{
@Inject
private DefaultManagementNetworkFinder defaultManagementNetworkFinder;
@Inject
private SupportedHostFeatureDao hostFeatureDao;
@Inject
private ClusterFeatureDao clusterFeatureDao;
@Inject
@MomPolicyUpdate
private Event<Cluster> momPolicyUpdatedEvent;
@Inject
private MoveMacs moveMacs;
@Inject
private InitGlusterCommandHelper glusterCommandHelper;
@Inject
private ResourceManager resourceManager;
@Inject
private RngDeviceUtils rngDeviceUtils;
@Inject
private ClusterDao clusterDao;
@Inject
private VmStaticDao vmStaticDao;
@Inject
private VmTemplateDao vmTemplateDao;
@Inject
private NetworkClusterDao networkClusterDao;
@Inject
private VdsDao vdsDao;
@Inject
private StoragePoolDao storagePoolDao;
@Inject
private GlusterVolumeDao glusterVolumeDao;
@Inject
private NetworkDao networkDao;
@Inject
private InterfaceDao interfaceDao;
@Inject
private VmDao vmDao;
private List<VDS> allForCluster;
private Cluster oldCluster;
private boolean isAddedToStoragePool = false;
private List<VmStatic> vmsLockedForUpdate = Collections.emptyList();
private List<VmTemplate> templatesLockedForUpdate = Collections.emptyList();
private Map<String, String> failedUpgradeEntities = new HashMap<>();
public static final String MESSAGE_REG_EX = "^(?<message>\\$message) (?<error>.*)";
public static final Pattern msgRegEx = Pattern.compile(MESSAGE_REG_EX);
@Override
protected void init() {
updateMigrateOnError();
oldCluster = clusterDao.get(getCluster().getId());
if (oldCluster != null
&& !Objects.equals(oldCluster.getCompatibilityVersion(), getCluster().getCompatibilityVersion())) {
vmsLockedForUpdate = filterVmsInClusterNeedUpdate();
templatesLockedForUpdate = filterTemplatesInClusterNeedUpdate();
}
}
/**
* Returns list of VMs that requires to be updated provided cluster compatibility version has changed.
*/
protected List<VmStatic> filterVmsInClusterNeedUpdate() {
return vmStaticDao.getAllByCluster(getCluster().getId()).stream()
.filter(vm -> vm.getOrigin() != OriginType.EXTERNAL && !vm.isHostedEngine())
.filter(vm -> vm.getCustomCompatibilityVersion() == null)
.sorted()
.collect(Collectors.toList());
}
protected List<VmTemplate> filterTemplatesInClusterNeedUpdate() {
if (!VmRngDevice.Source.urandomRandomUpdateRequired(
oldCluster.getCompatibilityVersion(), getCluster().getCompatibilityVersion())) {
return Collections.emptyList();
}
return vmTemplateDao.getAllForCluster(getCluster().getId()).stream()
.filter(template -> template.getCustomCompatibilityVersion() == null)
.sorted()
.collect(Collectors.toList());
}
public UpdateClusterCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
protected UpdateClusterCommand(Guid commandId) {
super(commandId);
}
private Guid getOldMacPoolId() {
return oldCluster.getMacPoolId();
}
private Guid getNewMacPoolId() {
final Cluster cluster = getCluster();
return cluster == null ? null : cluster.getMacPoolId();
}
@Override
protected void executeCommand() {
Guid newMacPoolId = getNewMacPoolId();
moveMacs.moveMacsOfUpdatedCluster(
oldCluster,
newMacPoolId,
getContext());
getCluster().setArchitecture(getArchitecture());
setDefaultSwitchTypeIfNeeded();
// TODO: This code should be revisited and proper compensation logic should be introduced here
checkMaxMemoryOverCommitValue();
if (!Objects.equals(oldCluster.getCompatibilityVersion(), getParameters().getCluster().getCompatibilityVersion())) {
String emulatedMachine = null;
// pick an UP host randomly - all should have latest compat version already if we passed validate.
for (VDS vds : allForCluster) {
if (vds.getStatus() == VDSStatus.Up) {
emulatedMachine = getEmulatedMachineOfHostInCluster(vds);
break;
}
}
if (emulatedMachine == null) {
getParameters().getCluster().setDetectEmulatedMachine(true);
} else {
getParameters().getCluster().setEmulatedMachine(emulatedMachine);
}
}
else if (oldCluster.getArchitecture() != getCluster().getArchitecture()) {
// if architecture was changed, emulated machines must be updated when adding new host.
// At this point the cluster is empty and have changed CPU name
getParameters().getCluster().setDetectEmulatedMachine(true);
getParameters().getCluster().setEmulatedMachine(null);
}
if (getParameters().isForceResetEmulatedMachine()) {
getParameters().getCluster().setDetectEmulatedMachine(true);
}
boolean isKsmPolicyChanged = (getCluster().isKsmMergeAcrossNumaNodes() != getPrevCluster().isKsmMergeAcrossNumaNodes()) ||
(getCluster().isEnableKsm() != getPrevCluster().isEnableKsm());
clusterDao.update(getParameters().getCluster());
addOrUpdateAddtionalClusterFeatures();
if (!oldCluster.supportsGlusterService() && getCluster().supportsGlusterService()) {
//update gluster parameters on all hosts
updateGlusterHosts();
}
if (isAddedToStoragePool) {
for (VDS vds : allForCluster) {
VdsActionParameters parameters = new VdsActionParameters();
parameters.setVdsId(vds.getId());
VdcReturnValueBase addVdsSpmIdReturn = runInternalAction(VdcActionType.AddVdsSpmId, parameters, cloneContextAndDetachFromParent());
if (!addVdsSpmIdReturn.getSucceeded()) {
setSucceeded(false);
getReturnValue().setFault(addVdsSpmIdReturn.getFault());
return;
}
}
final NetworkCluster managementNetworkCluster = createManagementNetworkCluster();
networkClusterDao.save(managementNetworkCluster);
}
alertIfFencingDisabled();
if (isKsmPolicyChanged) {
momPolicyUpdatedEvent.fire(getCluster());
}
// Call UpdateVmCommand on all VMs in the cluster to update defaults (i.e. DisplayType)
updateVms();
updateTemplates();
if (!failedUpgradeEntities.isEmpty()) {
logFailedUpgrades();
failValidation(Arrays.asList(EngineMessage.CLUSTER_CANNOT_UPDATE_CLUSTER_FAILED_TO_UPDATE_VMS),
"$VmList " + StringUtils.join(failedUpgradeEntities.keySet(), ", "));
getReturnValue().setValid(false);
setSucceeded(false);
return;
}
if (!Objects.equals(oldCluster.getCompatibilityVersion(), getCluster().getCompatibilityVersion())) {
vmStaticDao.getAllByCluster(getCluster().getId()).forEach(this::updateClusterVersionInManager);
}
setSucceeded(true);
}
private void updateClusterVersionInManager(VmStatic vm) {
VmManager vmManager = resourceManager.getVmManager(vm.getId(), false);
if (vmManager != null) {
vmManager.setClusterCompatibilityVersion(getCluster().getCompatibilityVersion());
}
}
private boolean updateVms() {
for (VmStatic vm : vmsLockedForUpdate) {
VmManagementParametersBase updateParams = new VmManagementParametersBase(vm);
/*
Locking by UpdateVmCommand is disabled since VMs are already locked in #getExclusiveLocks method.
This logic relies on assumption that UpdateVmCommand locks exactly only updated VM.
*/
updateParams.setLockProperties(LockProperties.create(LockProperties.Scope.None));
updateParams.setClusterLevelChangeFromVersion(oldCluster.getCompatibilityVersion());
upgradeGraphicsDevices(vm, updateParams);
updateRngDeviceIfNecessary(vm.getId(), vm.getCustomCompatibilityVersion(), updateParams);
VdcReturnValueBase result = runInternalAction(
VdcActionType.UpdateVm,
updateParams,
cloneContextAndDetachFromParent());
if (!result.getSucceeded()) {
List<String> params = new ArrayList<>();
params.add("$action Update");
params.add("$type VM");
params.add(parseErrorMessage(result.getValidationMessages()));
List<String> messages = Backend.getInstance().getErrorsTranslator().translateErrorText(params);
failedUpgradeEntities.put(vm.getName(), getFailedMessage(messages));
}
}
return true;
}
private void logFailedUpgrades() {
for (Map.Entry<String, String> entry : failedUpgradeEntities.entrySet()) {
addCustomValue("VmName", entry.getKey());
addCustomValue("Message", entry.getValue());
auditLogDirector.log(this, AuditLogType.CLUSTER_CANNOT_UPDATE_VM_COMPATIBILITY_VERSION);
}
}
/**
* This can be dropped together with support of 4.0 compatibility level.
*/
private void updateRngDeviceIfNecessary(
Guid vmBaseId,
Version customCompatibilityLevel,
HasRngDevice updateParameters) {
final Version oldEffectiveVersion = CompatibilityVersionUtils.getEffective(
customCompatibilityLevel,
() -> oldCluster.getCompatibilityVersion());
final Version newEffectiveVersion = CompatibilityVersionUtils.getEffective(
customCompatibilityLevel,
() -> getCluster().getCompatibilityVersion());
final Optional<VmRngDevice> updatedDeviceOptional = rngDeviceUtils.createUpdatedRngDeviceIfNecessary(
oldEffectiveVersion, newEffectiveVersion, vmBaseId, cloneContext());
if (updatedDeviceOptional.isPresent()) {
updateParameters.setUpdateRngDevice(true);
updateParameters.setRngDevice(updatedDeviceOptional.get());
}
}
private boolean updateTemplates() {
for (VmTemplate template : templatesLockedForUpdate) {
// the object was loaded in before command execution started and thus the value may be outdated
template.setClusterCompatibilityVersion(getCluster().getCompatibilityVersion());
UpdateVmTemplateParameters parameters = new UpdateVmTemplateParameters(template);
// Locking by UpdateVmTemplate is disabled since templates are already locked in #getExclusiveLocks method.
parameters.setLockProperties(LockProperties.create(LockProperties.Scope.None));
parameters.setClusterLevelChangeFromVersion(oldCluster.getCompatibilityVersion());
updateRngDeviceIfNecessary(template.getId(), template.getCustomCompatibilityVersion(), parameters);
if (!parameters.isUpdateRngDevice()) {
continue;
}
final VdcReturnValueBase result = runInternalAction(
VdcActionType.UpdateVmTemplate,
parameters,
cloneContextAndDetachFromParent());
if (!result.getSucceeded()) {
List<String> params = new ArrayList<>();
params.add("$action Update");
params.add("$type Template");
params.add(parseErrorMessage(result.getValidationMessages()));
List<String> messages = Backend.getInstance().getErrorsTranslator().translateErrorText(params);
failedUpgradeEntities.put(template.getName(), getFailedMessage(messages));
}
}
return true;
}
private String getFailedMessage(List<String> messages) {
String msg = "[No Message]";
if (messages.size() > 0 && !StringUtils.isEmpty(messages.get(0))) {
msg = messages.get(0);
}
return msg;
}
private String parseErrorMessage(List<String> messages) {
// method gets command Validation Messages and return the message
for(String message: messages) {
Matcher matcher = msgRegEx.matcher(message);
if (matcher.matches()) {
return matcher.group("error");
}
}
return "";
}
/**
* If upgrading cluster from 3.6 to 4.0 then switch from VNC/cirrus to VNC/vga
*/
private void upgradeGraphicsDevices(VmStatic dbVm, VmManagementParametersBase updateParams) {
Version oldVersion = updateParams.getClusterLevelChangeFromVersion();
if (Version.v4_0.greater(oldVersion)) {
VmStatic paramVm = updateParams.getVmStaticData();
if (dbVm.getDefaultDisplayType() == DisplayType.cirrus) {
paramVm.setDefaultDisplayType(DisplayType.vga);
}
}
}
private String getEmulatedMachineOfHostInCluster(VDS vds) {
Set<String> emulatedMachinesLookup =
new HashSet<>(Arrays.asList(vds.getSupportedEmulatedMachines().split(",")));
return Config.<List<String>>getValue(ConfigValues.ClusterEmulatedMachines,
getParameters().getCluster().getCompatibilityVersion().getValue())
.stream().filter(emulatedMachinesLookup::contains).findFirst().orElse(null);
}
private void addOrUpdateAddtionalClusterFeatures() {
Set<SupportedAdditionalClusterFeature> featuresInDb =
clusterFeatureDao.getSupportedFeaturesByClusterId(getCluster().getId());
Map<Guid, SupportedAdditionalClusterFeature> featuresEnabled = new HashMap<>();
for (SupportedAdditionalClusterFeature feature : getCluster().getAddtionalFeaturesSupported()) {
featuresEnabled.put(feature.getFeature().getId(), feature);
}
for (SupportedAdditionalClusterFeature featureInDb : featuresInDb) {
if (featureInDb.isEnabled() && !featuresEnabled.containsKey(featureInDb.getFeature().getId())) {
// Disable the features which are not selected in update cluster
featureInDb.setEnabled(false);
clusterFeatureDao.updateSupportedClusterFeature(featureInDb);
} else if (!featureInDb.isEnabled() && featuresEnabled.containsKey(featureInDb.getFeature().getId())) {
// Enable the features which are selected in update cluster
featureInDb.setEnabled(true);
clusterFeatureDao.updateSupportedClusterFeature(featureInDb);
}
featuresEnabled.remove(featureInDb.getFeature().getId());
}
// Add the newly add cluster features
if (CollectionUtils.isNotEmpty(featuresEnabled.values())) {
clusterFeatureDao.addAllSupportedClusterFeature(featuresEnabled.values());
}
}
private void updateGlusterHosts() {
allForCluster.forEach(glusterCommandHelper::initGlusterHost);
}
@Override
public AuditLogType getAuditLogTypeValue() {
if (getParameters().getIsInternalCommand()) {
return getSucceeded() ? AuditLogType.SYSTEM_UPDATE_CLUSTER
: AuditLogType.SYSTEM_UPDATE_CLUSTER_FAILED;
}
return getSucceeded() ? AuditLogType.USER_UPDATE_CLUSTER
: AuditLogType.USER_UPDATE_CLUSTER_FAILED;
}
@Override
protected boolean validate() {
boolean result = true;
boolean hasVms = false;
boolean hasVmOrHost = false;
boolean sameCpuNames = false;
boolean allVdssInMaintenance = false;
List<VM> vmList = null;
if (oldCluster == null) {
addValidationMessage(EngineMessage.VDS_CLUSTER_IS_NOT_VALID);
result = false;
}
// if the name was changed then make sure the new name is unique
if (result && !StringUtils.equals(oldCluster.getName(), getCluster().getName())) {
if (!isClusterUnique(getCluster().getName())) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_DO_ACTION_NAME_IN_USE);
result = false;
}
}
if (result && !VersionSupport.checkVersionSupported(getCluster()
.getCompatibilityVersion())) {
addValidationMessage(VersionSupport.getUnsupportedVersionMessage());
result = false;
}
if (result) {
allForCluster = vdsDao.getAllForCluster(oldCluster.getId());
}
// decreasing of compatibility version is only allowed when no hosts exists, and not beneath the DC version
if (result && getCluster().getCompatibilityVersion().compareTo(oldCluster.getCompatibilityVersion()) < 0) {
if (!allForCluster.isEmpty()) {
result = false;
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_CANNOT_DECREASE_CLUSTER_WITH_HOSTS_COMPATIBILITY_VERSION);
}
if (oldCluster.getStoragePoolId() != null) {
ClusterValidator validator = new ClusterValidator(
getDbFacade(), oldCluster, getCpuFlagsManagerHandler());
if (!validate(validator.dataCenterVersionMismatch())) {
result = false;
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_CANNOT_DECREASE_COMPATIBILITY_VERSION_UNDER_DC);
}
}
}
if (result && oldCluster.getStoragePoolId() != null
&& !oldCluster.getStoragePoolId().equals(getCluster().getStoragePoolId())) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_CHANGE_STORAGE_POOL);
result = false;
}
// If both original Cpu and new Cpu are null, don't check Cpu validity
if (result) {
allVdssInMaintenance = areAllVdssInMaintenance(allForCluster);
}
// Validate the cpu only if the cluster supports Virt
if (result && getCluster().supportsVirtService()
&& (oldCluster.getCpuName() != null || getCluster().getCpuName() != null)) {
// Check that cpu exist
if (!checkIfCpusExist()) {
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_CPU_NOT_FOUND);
addValidationMessage(EngineMessage.VAR__TYPE__CLUSTER);
result = false;
} else {
// if cpu changed from intel to amd (or backwards) and there are
// vds in this cluster, cannot update
if (!StringUtils.isEmpty(oldCluster.getCpuName())
&& !checkIfCpusSameManufacture(oldCluster)
&& !allVdssInMaintenance) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_CPU_ILLEGAL);
result = false;
}
}
}
if (result) {
vmList = vmDao.getAllForCluster(oldCluster.getId());
hasVmOrHost = !vmList.isEmpty() || !allForCluster.isEmpty();
}
// cannot change the the processor architecture while there are attached hosts or VMs to the cluster
if (result && getCluster().supportsVirtService()
&& !isArchitectureUpdatable()
&& hasVmOrHost) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_CPU_ARCHITECTURE_ILLEGAL);
result = false;
}
if (result) {
sameCpuNames = StringUtils.equals(oldCluster.getCpuName(), getCluster().getCpuName());
}
if (result) {
boolean isOldCPUEmpty = StringUtils.isEmpty(oldCluster.getCpuName());
if (!isOldCPUEmpty && !sameCpuNames && !isCpuUpdatable(oldCluster) && hasVmOrHost) {
addValidationMessage(EngineMessage.CLUSTER_CPU_IS_NOT_UPDATABLE);
result = false;
}
}
if (result && !oldCluster.getCompatibilityVersion().equals(getCluster().getCompatibilityVersion())) {
for (VM vm : vmList) {
if (vm.isPreviewSnapshot()) {// can't change cluster version when a VM is in preview
if (result) {
addValidationMessage(EngineMessage.CLUSTER_VERSION_CHANGE_VM_PREVIEW);
result = false; // and continue with adding validation messages
}
addValidationMessage(vm.getName());
}
}
}
if (result) {
List<VDS> vdss = new ArrayList<>();
isAddedToStoragePool = oldCluster.getStoragePoolId() == null
&& getCluster().getStoragePoolId() != null;
if (isAddedToStoragePool && !validateManagementNetwork()) {
return false;
}
for (VDS vds : allForCluster) {
if (vds.getStatus() == VDSStatus.Up) {
if (isAddedToStoragePool) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_VDS_UP);
return false;
} else {
vdss.add(vds);
}
}
}
for (VDS vds : vdss) {
if (!VersionSupport.checkClusterVersionSupported(
getCluster().getCompatibilityVersion(), vds)) {
result = false;
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_COMPATIBILITY_VERSION_WITH_LOWER_HOSTS);
break;
} else if (getCluster().supportsVirtService() && missingServerCpuFlags(vds) != null) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_CPU_WITH_LOWER_HOSTS);
result = false;
break;
}
if (!isSupportedEmulatedMachinesMatchClusterLevel(vds)) {
return failValidation(EngineMessage.CLUSTER_CANNOT_UPDATE_COMPATIBILITY_VERSION_WITH_INCOMPATIBLE_EMULATED_MACHINE);
}
}
if (result) {
Set<SupportedAdditionalClusterFeature> additionalClusterFeaturesAdded =
getAdditionalClusterFeaturesAdded();
// New Features cannot be enabled if all up hosts are not supporting the selected feature
if (CollectionUtils.isNotEmpty(additionalClusterFeaturesAdded)
&& !checkClusterFeaturesSupported(vdss, additionalClusterFeaturesAdded)) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS);
result = false;
}
}
if (result) {
boolean notDownVms = false;
boolean suspendedVms = false;
hasVms = vmList.size() > 0;
if (!sameCpuNames) {
for (VM vm : vmList) {
if (vm.getStatus() == VMStatus.Suspended) {
suspendedVms = true;
break;
} else if (vm.getStatus() != VMStatus.Down) {
notDownVms = true;
break;
}
}
if (suspendedVms) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_UPDATE_CPU_WITH_SUSPENDED_VMS);
result = false;
} else if (notDownVms) {
int compareResult = compareCpuLevels(oldCluster);
if (compareResult > 0) {// Upgrade of CPU in same compability level is allowed if
// there
// are running VMs - but we should warn they
// cannot not be hibernated
addCustomValue("Cluster", getParameters().getCluster().getName());
auditLogDirector.log(this,
AuditLogType.CANNOT_HIBERNATE_RUNNING_VMS_AFTER_CLUSTER_CPU_UPGRADE);
}
}
}
}
}
if (result && getCluster().getStoragePoolId() != null) {
StoragePool storagePool = storagePoolDao.get(getCluster().getStoragePoolId());
if (oldCluster.getStoragePoolId() == null && storagePool.isLocal()) {
// we allow only one cluster in localfs data center
if (!clusterDao.getAllForStoragePool(getCluster().getStoragePoolId()).isEmpty()) {
getReturnValue()
.getValidationMessages()
.add(EngineMessage.CLUSTER_CANNOT_ADD_MORE_THEN_ONE_HOST_TO_LOCAL_STORAGE
.toString());
result = false;
}
else if (Config.getValue(ConfigValues.AutoRegistrationDefaultClusterID).equals(getCluster().getId())) {
addValidationMessage(EngineMessage.DEFAULT_CLUSTER_CANNOT_BE_ON_LOCALFS);
result = false;
}
}
}
if (result) {
if (!(getCluster().supportsGlusterService() || getCluster().supportsVirtService())) {
addValidationMessage(EngineMessage.CLUSTER_AT_LEAST_ONE_SERVICE_MUST_BE_ENABLED);
result = false;
}
else if (getCluster().supportsGlusterService() && getCluster().supportsVirtService()
&& !isAllowClusterWithVirtGluster()) {
addValidationMessage(EngineMessage.CLUSTER_ENABLING_BOTH_VIRT_AND_GLUSTER_SERVICES_NOT_ALLOWED);
result = false;
}
}
if (result && hasVms && !getCluster().supportsVirtService()) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_DISABLE_VIRT_WHEN_CLUSTER_CONTAINS_VMS);
result = false;
}
if (result && !getCluster().supportsGlusterService()) {
List<GlusterVolumeEntity> volumes = glusterVolumeDao.getByClusterId(getCluster().getId());
if (volumes != null && volumes.size() > 0) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_DISABLE_GLUSTER_WHEN_CLUSTER_CONTAINS_VOLUMES);
result = false;
}
}
if (result && getCluster().supportsTrustedService() && Config.<String> getValue(ConfigValues.AttestationServer).equals("")) {
addValidationMessage(EngineMessage.CLUSTER_CANNOT_SET_TRUSTED_ATTESTATION_SERVER_NOT_CONFIGURED);
result = false;
}
if (result
&& !FeatureSupported.isMigrationSupported(getArchitecture(), getCluster().getCompatibilityVersion())
&& getCluster().getMigrateOnError() != MigrateOnErrorOptions.NO) {
return failValidation(EngineMessage.MIGRATION_ON_ERROR_IS_NOT_SUPPORTED);
}
if (result) {
result = validateClusterPolicy(oldCluster);
}
if (result && getParameters().isForceResetEmulatedMachine()) {
for (VDS vds : allForCluster) {
if (vds.getStatus() == VDSStatus.Up) {
addValidationMessage(EngineMessage.CLUSTER_HOSTS_MUST_BE_DOWN);
result = false;
break;
}
}
}
ClusterValidator clusterValidator = new ClusterValidator(
getDbFacade(),
getCluster(),
cpuFlagsManagerHandler);
result = result
&& validate(clusterValidator.rngSourcesAllowed())
&& validate(clusterValidator.memoryOptimizationConfiguration());
return result;
}
protected boolean isSupportedEmulatedMachinesMatchClusterLevel(VDS vds) {
return getEmulatedMachineOfHostInCluster(vds) != null;
}
private Set<SupportedAdditionalClusterFeature> getAdditionalClusterFeaturesAdded() {
// Lets not modify the existing collection. Hence creating a new hashset.
Set<SupportedAdditionalClusterFeature> featuresSupported =
new HashSet<>(getCluster().getAddtionalFeaturesSupported());
featuresSupported.removeAll(clusterFeatureDao.getSupportedFeaturesByClusterId(getCluster().getId()));
return featuresSupported;
}
private boolean checkClusterFeaturesSupported(List<VDS> vdss,
Set<SupportedAdditionalClusterFeature> newFeaturesEnabled) {
Set<String> featuresNamesEnabled = new HashSet<>();
for (SupportedAdditionalClusterFeature feature : newFeaturesEnabled) {
featuresNamesEnabled.add(feature.getFeature().getName());
}
for (VDS vds : vdss) {
Set<String> featuresSupportedByVds = hostFeatureDao.getSupportedHostFeaturesByHostId(vds.getId());
if (!featuresSupportedByVds.containsAll(featuresNamesEnabled)) {
return false;
}
}
return true;
}
@Override
protected void setActionMessageParameters() {
super.setActionMessageParameters();
addValidationMessage(EngineMessage.VAR__ACTION__UPDATE);
}
protected boolean isArchitectureUpdatable() {
return oldCluster.getArchitecture() == ArchitectureType.undefined ? true
: getArchitecture() == oldCluster.getArchitecture();
}
protected boolean checkIfCpusSameManufacture(Cluster group) {
return getCpuFlagsManagerHandler().checkIfCpusSameManufacture(group.getCpuName(),
getCluster().getCpuName(),
getCluster().getCompatibilityVersion());
}
protected boolean checkIfCpusExist() {
return getCpuFlagsManagerHandler().checkIfCpusExist(getCluster().getCpuName(),
getCluster().getCompatibilityVersion());
}
protected List<String> missingServerCpuFlags(VDS vds) {
return getCpuFlagsManagerHandler().missingServerCpuFlags(
getCluster().getCpuName(),
vds.getCpuFlags(),
getCluster().getCompatibilityVersion());
}
protected boolean isCpuUpdatable(Cluster cluster) {
return getCpuFlagsManagerHandler().isCpuUpdatable(cluster.getCpuName(), cluster.getCompatibilityVersion());
}
private boolean areAllVdssInMaintenance(List<VDS> vdss) {
boolean allInMaintenance = true;
for (VDS vds : vdss) {
if (vds.getStatus() != VDSStatus.Maintenance) {
allInMaintenance = false;
break;
}
}
return allInMaintenance;
}
protected int compareCpuLevels(Cluster otherGroup) {
return getCpuFlagsManagerHandler().compareCpuLevels(getCluster().getCpuName(),
otherGroup.getCpuName(),
otherGroup.getCompatibilityVersion());
}
@Override
protected List<Class<?>> getValidationGroups() {
addValidationGroup(UpdateEntity.class);
return super.getValidationGroups();
}
@Override
public String getEntityType() {
return VdcObjectType.Cluster.getVdcObjectTranslation();
}
@Override
public String getEntityOldName() {
return oldCluster.getName();
}
@Override
public String getEntityNewName() {
return getParameters().getCluster().getName();
}
@Override
public void setEntityId(AuditLogableBase logable) {
logable.setClusterId(oldCluster.getId());
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
final List<PermissionSubject> result = new ArrayList<>(super.getPermissionCheckSubjects());
final Guid macPoolId = getNewMacPoolId();
final boolean changingPoolDefinition = macPoolId != null && !macPoolId.equals(getOldMacPoolId());
if (changingPoolDefinition) {
result.add(new PermissionSubject(macPoolId, VdcObjectType.MacPool, ActionGroup.CONFIGURE_MAC_POOL));
}
return result;
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
final LockMessage lockMessage = createLockMessage();
return templatesLockedForUpdate.stream()
.collect(Collectors.toMap(
template -> template.getId().toString(),
template -> LockMessagesMatchUtil.makeLockingPair(LockingGroup.TEMPLATE, lockMessage)));
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
final LockMessage lockMessage = createLockMessage();
return vmsLockedForUpdate.stream()
.collect(Collectors.toMap(
vm -> vm.getId().toString(),
vm -> LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, lockMessage)));
}
private LockMessage createLockMessage() {
return new LockMessage(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_IS_BEING_UPDATED)
.with("clusterName", oldCluster.getName());
}
@Override
protected LockProperties getLockProperties() {
return LockProperties.create(LockProperties.Scope.Command).withWait(false);
}
@Override
protected boolean validateInputManagementNetwork(NetworkClusterValidatorBase networkClusterValidator) {
return validate(networkClusterValidator.networkBelongsToClusterDataCenter(getCluster(), getManagementNetwork()))
&& validate(networkClusterValidator.managementNetworkNotExternal(getManagementNetwork()));
}
}