Ramesh N has uploaded a new change for review. Change subject: engine: add feature comptability check for VDS ......................................................................
engine: add feature comptability check for VDS Some times checking for the cluster compatibility version is not enough to support some features across multiple comptability versions. So we need to have some machanism while adding a node to cluster to check that vdsm supports all the required features required for the cluster. This patch adds feature compatability check for VDS. List of all additional features will be maintained in a master table. List of features will be shown in the new/edit cluster dialog based on the compatability version selected as well as the services gluster/virt enabled. User can select the features he wants in the cluster and cluster will be initialized with that set of feature during cluster creation. When a node is added to the cluster, it checks that all the required features for the cluster are supported by vdsm. This is done in a generic way so that it can used in different features in future. Currently it is used to check whether the vdsm supports gluster37 features. gluster37 features are supported in 3.6 cluster by default. It is also supported in 3.5 cluster with this feature check. Change-Id: Icba02b189a169bc676e0c5f47f7aaf394f0b49a6 Signed-off-by: Ramesh Nachimuthu <[email protected]> --- M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVdsGroupCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByClusterIdQuery.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByVersionAndCategoryQuery.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HandleVdsVersionCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SetNonOperationalVdsCommand.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommand.java M backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommandTest.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java A backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/AdditionalFeature.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/NonOperationalReason.java A backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/SupportedAdditionalClusterFeature.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDSGroup.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java A backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/GetClusterFeaturesByVersionAndCategoryParameters.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/VdcQueryType.java M backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dal/dbbroker/DbFacade.java A backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDao.java A backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDaoImpl.java A backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDao.java A backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoImpl.java M backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties M backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties A backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/ClusterFeatureDaoTest.java A backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoTest.java M backend/manager/modules/dal/src/test/resources/fixtures.xml M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/VdsManager.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java M backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java M frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/core/Common.gwt.xml M frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/LocalizedEnums.java M frontend/webadmin/modules/uicompat/src/main/resources/org/ovirt/engine/ui/uicompat/LocalizedEnums.properties M frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties A packaging/dbscripts/cluster_features_sp.sql M packaging/dbscripts/create_views.sql A packaging/dbscripts/upgrade/03_05_1470_add_cluster_features_table.sql 38 files changed, 1,306 insertions(+), 17 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/25/41025/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVdsGroupCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVdsGroupCommand.java index 134eac1..63ce67a 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVdsGroupCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/AddVdsGroupCommand.java @@ -3,6 +3,9 @@ import java.util.Collections; import java.util.List; +import javax.inject.Inject; + +import org.apache.commons.collections.CollectionUtils; import org.ovirt.engine.core.bll.profiles.CpuProfileHelper; import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.bll.utils.VersionSupport; @@ -12,6 +15,8 @@ import org.ovirt.engine.core.common.action.VdsGroupOperationParameters; import org.ovirt.engine.core.common.businessentities.MigrateOnErrorOptions; import org.ovirt.engine.core.common.businessentities.StoragePool; +import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature; +import org.ovirt.engine.core.common.businessentities.VDSGroup; import org.ovirt.engine.core.common.businessentities.network.Network; import org.ovirt.engine.core.common.businessentities.network.NetworkCluster; import org.ovirt.engine.core.common.businessentities.network.NetworkStatus; @@ -22,6 +27,8 @@ import org.ovirt.engine.core.common.validation.group.CreateEntity; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.dao.ClusterFeatureDao; +import org.ovirt.engine.core.dao.VdsGroupDAO; import org.ovirt.engine.core.utils.NetworkUtils; import org.ovirt.engine.core.utils.linq.LinqUtils; import org.ovirt.engine.core.utils.linq.Predicate; @@ -29,6 +36,12 @@ public class AddVdsGroupCommand<T extends VdsGroupOperationParameters> extends VdsGroupOperationCommandBase<T> { public static final String DefaultNetworkDescription = "Management Network"; + + @Inject + private ClusterFeatureDao clusterFeatureDao; + + @Inject + private VdsGroupDAO vdsGroupDao; public AddVdsGroupCommand(T parameters) { super(parameters); @@ -38,11 +51,12 @@ @Override protected void executeCommand() { - getVdsGroup().setArchitecture(getArchitecture()); + VDSGroup cluster = getVdsGroup(); + cluster.setArchitecture(getArchitecture()); checkMaxMemoryOverCommitValue(); - getVdsGroup().setDetectEmulatedMachine(true); - DbFacade.getInstance().getVdsGroupDao().save(getVdsGroup()); + cluster.setDetectEmulatedMachine(true); + vdsGroupDao.save(cluster); alertIfFencingDisabled(); @@ -74,7 +88,14 @@ getParameters().getVdsGroup().getName())); } - setActionReturnValue(getVdsGroup().getId()); + if (CollectionUtils.isNotEmpty(cluster.getAddtionalFeaturesSupported())) { + for (SupportedAdditionalClusterFeature feature : cluster.getAddtionalFeaturesSupported()) { + feature.setClusterId(cluster.getId()); + } + clusterFeatureDao.addAllSupportedClusterFeature(cluster.getAddtionalFeaturesSupported()); + } + + setActionReturnValue(cluster.getId()); setSucceeded(true); } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByClusterIdQuery.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByClusterIdQuery.java new file mode 100644 index 0000000..17c6016 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByClusterIdQuery.java @@ -0,0 +1,25 @@ +package org.ovirt.engine.core.bll; + +import java.util.Set; + +import javax.inject.Inject; + +import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature; +import org.ovirt.engine.core.common.queries.IdQueryParameters; +import org.ovirt.engine.core.dao.ClusterFeatureDao; + +public class GetClusterFeaturesByClusterIdQuery<P extends IdQueryParameters> extends QueriesCommandBase<P> { + @Inject + private ClusterFeatureDao clusterFeatureDao; + + public GetClusterFeaturesByClusterIdQuery(P parameters) { + super(parameters); + } + + @Override + protected void executeQueryCommand() { + Set<SupportedAdditionalClusterFeature> additionalClusterFeatures = + clusterFeatureDao.getSupportedFeaturesByClusterId(getParameters().getId()); + getQueryReturnValue().setReturnValue(additionalClusterFeatures); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByVersionAndCategoryQuery.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByVersionAndCategoryQuery.java new file mode 100644 index 0000000..6e64480 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/GetClusterFeaturesByVersionAndCategoryQuery.java @@ -0,0 +1,26 @@ +package org.ovirt.engine.core.bll; + +import java.util.Set; + +import javax.inject.Inject; + +import org.ovirt.engine.core.common.businessentities.AdditionalFeature; +import org.ovirt.engine.core.common.queries.GetClusterFeaturesByVersionAndCategoryParameters; +import org.ovirt.engine.core.dao.ClusterFeatureDao; + +public class GetClusterFeaturesByVersionAndCategoryQuery<P extends GetClusterFeaturesByVersionAndCategoryParameters> extends QueriesCommandBase<P> { + @Inject + private ClusterFeatureDao clusterFeatureDao; + + public GetClusterFeaturesByVersionAndCategoryQuery(P parameters) { + super(parameters); + } + + @Override + protected void executeQueryCommand() { + Set<AdditionalFeature> additionalClusterFeatures = + clusterFeatureDao.getClusterFeaturesForVersionAndCategory(getParameters().getVersion().getValue(), + getParameters().getCategory()); + getQueryReturnValue().setReturnValue(additionalClusterFeatures); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HandleVdsVersionCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HandleVdsVersionCommand.java index cd15d68..3f84839 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HandleVdsVersionCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/HandleVdsVersionCommand.java @@ -5,6 +5,9 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.job.ExecutionHandler; @@ -13,6 +16,7 @@ import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdsActionParameters; import org.ovirt.engine.core.common.businessentities.NonOperationalReason; +import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VDSGroup; import org.ovirt.engine.core.common.businessentities.VDSStatus; @@ -21,10 +25,18 @@ import org.ovirt.engine.core.common.errors.VdcBllMessages; import org.ovirt.engine.core.compat.RpmVersion; import org.ovirt.engine.core.compat.Version; +import org.ovirt.engine.core.dao.ClusterFeatureDao; +import org.ovirt.engine.core.dao.SupportedHostFeatureDao; @InternalCommandAttribute @NonTransactiveCommandAttribute public class HandleVdsVersionCommand<T extends VdsActionParameters> extends VdsCommand<T> { + + @Inject + private ClusterFeatureDao clusterFeatureDao; + + @Inject + private SupportedHostFeatureDao hostFeatureDao; public HandleVdsVersionCommand(T parameters) { this(parameters, null); @@ -77,8 +89,26 @@ reportNonOperationReason(NonOperationalReason.CLUSTER_VERSION_INCOMPATIBLE_WITH_CLUSTER, cluster.getcompatibility_version().toString(), vds.getSupportedClusterLevels().toString()); + } else { + checkClusterAdditionalFeaturesSupported(cluster, vds); } setSucceeded(true); + } + + private void checkClusterAdditionalFeaturesSupported(VDSGroup cluster, VDS vds) { + Set<SupportedAdditionalClusterFeature> clusterSupportedFeatures = + clusterFeatureDao.getSupportedFeaturesByClusterId(cluster.getId()); + Set<String> hostSupportedFeatures = + hostFeatureDao.getSupportedHostFeaturesByHostId(vds.getId()); + for (SupportedAdditionalClusterFeature feature : clusterSupportedFeatures) { + if (feature.isEnabled() && !hostSupportedFeatures.contains(feature.getFeature().getName())) { + Map<String, String> customLogValues = new HashMap<>(); + customLogValues.put("UnSupportedFeature", feature.getFeature().getName()); + reportNonOperationReason(NonOperationalReason.HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER, customLogValues); + return; + } + } + return; } private void reportNonOperationReason(NonOperationalReason reason, String compatibleVersions, @@ -86,6 +116,10 @@ Map<String, String> customLogValues = new HashMap<>(); customLogValues.put("CompatibilityVersion", compatibleVersions); customLogValues.put("VdsSupportedVersions", vdsSupportedVersions); + reportNonOperationReason(reason, customLogValues); + } + + private void reportNonOperationReason(NonOperationalReason reason, Map<String, String> customLogValues) { SetNonOperationalVdsParameters tempVar = new SetNonOperationalVdsParameters(getVdsId(), reason, customLogValues); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SetNonOperationalVdsCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SetNonOperationalVdsCommand.java index b20cdcf..53aac87 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SetNonOperationalVdsCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SetNonOperationalVdsCommand.java @@ -143,6 +143,8 @@ return AuditLogType.MIXING_RHEL_VERSIONS_IN_CLUSTER; case UNTRUSTED: return AuditLogType.VDS_UNTRUSTED; + case HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER: + return AuditLogType.HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER; default: return (getSucceeded()) ? AuditLogType.VDS_SET_NONOPERATIONAL : AuditLogType.VDS_SET_NONOPERATIONAL_FAILED; } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommand.java index 910e7df..b33ba2c 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommand.java @@ -1,9 +1,16 @@ package org.ovirt.engine.core.bll; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; +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.profiles.CpuProfileHelper; @@ -18,6 +25,7 @@ import org.ovirt.engine.core.common.businessentities.ArchitectureType; import org.ovirt.engine.core.common.businessentities.MigrateOnErrorOptions; 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.VDSGroup; import org.ovirt.engine.core.common.businessentities.VDSStatus; @@ -38,12 +46,20 @@ import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase; +import org.ovirt.engine.core.dao.ClusterFeatureDao; +import org.ovirt.engine.core.dao.SupportedHostFeatureDao; import org.ovirt.engine.core.dao.VdsStaticDAO; import org.ovirt.engine.core.dao.network.NetworkDao; import org.ovirt.engine.core.utils.NetworkUtils; public class UpdateVdsGroupCommand<T extends VdsGroupOperationParameters> extends VdsGroupOperationCommandBase<T> implements RenamedEntityInfoProvider{ + + @Inject + private SupportedHostFeatureDao hostFeatureDao; + + @Inject + private ClusterFeatureDao clusterFeatureDao; private List<VDS> allForVdsGroup; private VDSGroup oldGroup; @@ -105,6 +121,7 @@ } getVdsGroupDAO().update(getParameters().getVdsGroup()); + addOrUpdateAddtionalClusterFeatures(); if (oldGroup.getStoragePoolId() == null && getVdsGroup().getStoragePoolId() != null) { for (VDS vds : allForVdsGroup) { @@ -134,6 +151,34 @@ alertIfFencingDisabled(); setSucceeded(true); + } + + private void addOrUpdateAddtionalClusterFeatures() { + Set<SupportedAdditionalClusterFeature> featuresInDb = + getClusterFeatureDao().getSupportedFeaturesByClusterId(getVdsGroup().getId()); + Map<Guid, SupportedAdditionalClusterFeature> featuresEnabled = new HashMap<>(); + + for (SupportedAdditionalClusterFeature feature : getVdsGroup().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); + getClusterFeatureDao().updateSupportedClusterFeature(featureInDb); + } else if (!featureInDb.isEnabled() && featuresEnabled.containsKey(featureInDb.getFeature().getId())) { + // Enable the features which are selected in update cluster + featureInDb.setEnabled(true); + getClusterFeatureDao().updateSupportedClusterFeature(featureInDb); + } + featuresEnabled.remove(featureInDb.getFeature().getId()); + } + // Add the newly add cluster features + if (CollectionUtils.isNotEmpty(featuresEnabled.values())) { + getClusterFeatureDao().addAllSupportedClusterFeature(featuresEnabled.values()); + } + } @Override @@ -276,6 +321,18 @@ break; } } + + 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)) { + addCanDoActionMessage(VdcBllMessages.VDS_GROUP_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS); + result = false; + } + } + if (result) { boolean notDownVms = false; boolean suspendedVms = false; @@ -387,6 +444,31 @@ return result; } + private Set<SupportedAdditionalClusterFeature> getAdditionalClusterFeaturesAdded() { + // Lets not modify the existing collection. Hence creating a new hashset. + Set<SupportedAdditionalClusterFeature> featuresSupported = + new HashSet<>(getVdsGroup().getAddtionalFeaturesSupported()); + featuresSupported.removeAll(getClusterFeatureDao().getSupportedFeaturesByClusterId(getVdsGroup().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 = getHostFeatureDao().getSupportedHostFeaturesByHostId(vds.getId()); + if (!featuresSupportedByVds.containsAll(featuresNamesEnabled)) { + return false; + } + } + + return true; + } + @Override protected void setActionMessageParameters() { addCanDoActionMessage(VdcBllMessages.VAR__TYPE__CLUSTER); @@ -471,4 +553,12 @@ public void setEntityId(AuditLogableBase logable) { logable.setVdsGroupId(oldGroup.getId()); } + + public SupportedHostFeatureDao getHostFeatureDao() { + return hostFeatureDao; + } + + public ClusterFeatureDao getClusterFeatureDao() { + return clusterFeatureDao; + } } diff --git a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommandTest.java b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommandTest.java index a270948..2f0c580 100644 --- a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommandTest.java +++ b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/UpdateVdsGroupCommandTest.java @@ -11,6 +11,7 @@ import static org.ovirt.engine.core.utils.MockConfigRule.mockConfig; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -26,8 +27,10 @@ import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; import org.ovirt.engine.core.common.action.VdsGroupOperationParameters; +import org.ovirt.engine.core.common.businessentities.AdditionalFeature; import org.ovirt.engine.core.common.businessentities.ArchitectureType; 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.VDSGroup; import org.ovirt.engine.core.common.businessentities.VDSStatus; @@ -35,9 +38,12 @@ import org.ovirt.engine.core.common.businessentities.gluster.GlusterVolumeEntity; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.VdcBllMessages; +import org.ovirt.engine.core.common.mode.ApplicationMode; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.Version; +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.VdsGroupDAO; import org.ovirt.engine.core.dao.VmDAO; @@ -52,6 +58,7 @@ private static final Version VERSION_1_2 = new Version(1, 2); private static final Guid STORAGE_POOL_ID = Guid.newGuid(); private static final Guid DEFAULT_VDS_GROUP_ID = new Guid("99408929-82CF-4DC7-A532-9D998063FA95"); + private static final Guid DEFAULT_FEATURE_ID = new Guid("99408929-82CF-4DC7-A532-9D998063FA96"); private static final Map<String, String> migrationMap = new HashMap<>(); @@ -78,6 +85,10 @@ private GlusterVolumeDao glusterVolumeDao; @Mock private VmDAO vmDao; + @Mock + private ClusterFeatureDao clusterFeatureDao; + @Mock + private SupportedHostFeatureDao hostFeatureDao; private UpdateVdsGroupCommand<VdsGroupOperationParameters> cmd; @@ -318,6 +329,34 @@ canDoActionFailedWithReason(VdcBllMessages.VDS_GROUP_CANNOT_DISABLE_GLUSTER_WHEN_CLUSTER_CONTAINS_VOLUMES); } + @Test + public void enableNewAddtionalFeatureWhenHostDoesnotSupport() { + createCommandWithAddtionalFeature(); + when(vdsGroupDAO.get(any(Guid.class))).thenReturn(createVdsGroupWithNoCpuName()); + when(vdsGroupDAO.getByName(anyString())).thenReturn(createVdsGroupWithNoCpuName()); + cpuExists(); + cpuFlagsNotMissing(); + allQueriesForVms(); + clusterHasVds(); + when(clusterFeatureDao.getSupportedFeaturesByClusterId(any(Guid.class))).thenReturn(Collections.EMPTY_SET); + when(hostFeatureDao.getSupportedHostFeaturesByHostId(any(Guid.class))).thenReturn(Collections.EMPTY_SET); + canDoActionFailedWithReason(VdcBllMessages.VDS_GROUP_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS); + } + + @Test + public void enableNewAddtionalFeatureWhenHostSupports() { + createCommandWithAddtionalFeature(); + when(vdsGroupDAO.get(any(Guid.class))).thenReturn(createVdsGroupWithNoCpuName()); + when(vdsGroupDAO.getByName(anyString())).thenReturn(createVdsGroupWithNoCpuName()); + cpuExists(); + cpuFlagsNotMissing(); + allQueriesForVms(); + clusterHasVds(); + when(clusterFeatureDao.getSupportedFeaturesByClusterId(any(Guid.class))).thenReturn(Collections.EMPTY_SET); + when(hostFeatureDao.getSupportedHostFeaturesByHostId(any(Guid.class))).thenReturn(new HashSet(Arrays.asList("TEST_FEATURE"))); + assertTrue(cmd.canDoAction()); + } + private void createSimpleCommand() { createCommand(createNewVdsGroup()); } @@ -355,6 +394,10 @@ createCommand(createVdsGroupWith(true, false)); } + private void createCommandWithAddtionalFeature() { + createCommand(createVdsGroupWithAddtionalFeature()); + } + private void createCommandWithGlusterEnabled() { createCommand(createVdsGroupWith(false, true)); } @@ -375,6 +418,8 @@ doReturn(storagePoolDAO).when(cmd).getStoragePoolDAO(); doReturn(glusterVolumeDao).when(cmd).getGlusterVolumeDao(); doReturn(vmDao).when(cmd).getVmDAO(); + doReturn(clusterFeatureDao).when(cmd).getClusterFeatureDao(); + doReturn(hostFeatureDao).when(cmd).getHostFeatureDao(); doReturn(true).when(cmd).validateClusterPolicy(); if (StringUtils.isEmpty(group.getcpu_name())) { @@ -470,6 +515,21 @@ return group; } + private static VDSGroup createVdsGroupWithAddtionalFeature() { + VDSGroup group = createDefaultVdsGroup(); + group.setcompatibility_version(VERSION_1_1); + Set<SupportedAdditionalClusterFeature> addtionalFeaturesSupported = new HashSet<>(); + AdditionalFeature feature = + new AdditionalFeature(DEFAULT_FEATURE_ID, + "TEST_FEATURE", + VERSION_1_1, + "Test Feature", + ApplicationMode.AllModes); + addtionalFeaturesSupported.add(new SupportedAdditionalClusterFeature(group.getId(), true, feature)); + group.setAddtionalFeaturesSupported(addtionalFeaturesSupported); + return group; + } + private static StoragePool createStoragePoolLocalFSOldVersion() { StoragePool pool = new StoragePool(); pool.setIsLocal(true); diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java index 0f132e0..3c782ef 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java @@ -440,6 +440,7 @@ GLUSTER_GEOREP_SESSION_CREATE_FAILED(4146, AuditLogSeverity.ERROR), CREATE_GLUSTER_VOLUME_GEOREP_SESSION(4147), GLUSTER_VOLUME_SNAPSHOT_SOFT_LIMIT_REACHED(4148, AuditLogSeverity.ALERT), + HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER(4149, AuditLogSeverity.ERROR), USER_FORCE_SELECTED_SPM(159), USER_VDS_RESTART(41), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/AdditionalFeature.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/AdditionalFeature.java new file mode 100644 index 0000000..c3329b7 --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/AdditionalFeature.java @@ -0,0 +1,110 @@ +package org.ovirt.engine.core.common.businessentities; + +import java.io.Serializable; +import java.util.Objects; + +import org.ovirt.engine.core.common.mode.ApplicationMode; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.compat.Version; + +public class AdditionalFeature implements Serializable { + + private static final long serialVersionUID = -8387930346405670858L; + private Guid id; + private String name; + private Version version; + private String description; + private ApplicationMode category; + + public AdditionalFeature() { + } + + public AdditionalFeature(Guid id, + String name, + Version version, + String description, + ApplicationMode category) { + this.id = id; + this.name = name; + this.version = version; + this.description = description; + this.category = category; + } + + public Guid getId() { + return id; + } + + public void setId(Guid id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public ApplicationMode getCategory() { + return category; + } + + public void setCategory(ApplicationMode category) { + this.category = category; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, version, description, category); + } + + @Override + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof AdditionalFeature)) { + AdditionalFeature clusterFeature = (AdditionalFeature) obj; + if (Objects.equals(getId(), clusterFeature.getId()) + && Objects.equals(getName(), clusterFeature.getName()) + && Objects.equals(getVersion(), clusterFeature.getVersion()) + && Objects.equals(getDescription(), clusterFeature.getDescription()) + && Objects.equals(getCategory(), clusterFeature.getCategory())) { + return true; + } + } + return false; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("AdditionalFeature {"); + sb.append("id="); + sb.append(getId()); + sb.append(", name="); + sb.append(getName()); + sb.append(", version="); + sb.append(getVersion()); + sb.append(", description="); + sb.append(getDescription()); + sb.append(", category="); + sb.append(getCategory()); + sb.append("}"); + return sb.toString(); + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/NonOperationalReason.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/NonOperationalReason.java index 079540a..9a334dc 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/NonOperationalReason.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/NonOperationalReason.java @@ -24,7 +24,8 @@ ARCHITECTURE_INCOMPATIBLE_WITH_CLUSTER(16), NETWORK_INTERFACE_IS_DOWN(17), RNG_SOURCES_INCOMPATIBLE_WITH_CLUSTER(18), - MIXING_RHEL_VERSIONS_IN_CLUSTER(20); + MIXING_RHEL_VERSIONS_IN_CLUSTER(20), + HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER(21); private final int value; diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/SupportedAdditionalClusterFeature.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/SupportedAdditionalClusterFeature.java new file mode 100644 index 0000000..5a9389f --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/SupportedAdditionalClusterFeature.java @@ -0,0 +1,72 @@ +package org.ovirt.engine.core.common.businessentities; + +import java.io.Serializable; +import java.util.Objects; + +import org.ovirt.engine.core.compat.Guid; + +public class SupportedAdditionalClusterFeature implements Serializable { + + private static final long serialVersionUID = -1063480824650271898L; + private Guid clusterId; + private boolean enabled; + private AdditionalFeature feature; + + public SupportedAdditionalClusterFeature() { + } + + public SupportedAdditionalClusterFeature(Guid clusterId, boolean enabled, AdditionalFeature feature) { + this.clusterId = clusterId; + this.setEnabled(enabled); + this.feature = feature; + } + + public Guid getClusterId() { + return clusterId; + } + + public void setClusterId(Guid clusterId) { + this.clusterId = clusterId; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public AdditionalFeature getFeature() { + return feature; + } + + public void setFeature(AdditionalFeature feature) { + this.feature = feature; + } + + @Override + public int hashCode() { + return Objects.hash(clusterId, feature, enabled); + } + + @Override + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof SupportedAdditionalClusterFeature)) { + SupportedAdditionalClusterFeature feature = (SupportedAdditionalClusterFeature) obj; + if (enabled == feature.isEnabled() + && Objects.equals(getClusterId(), feature.getClusterId()) + && Objects.equals(getFeature(), feature.getFeature())) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "SupportedAdditionalClusterFeature [clusterId=" + getClusterId() + ", feature=" + getFeature() + + ", enabled=" + isEnabled() + "]"; + } + +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java index 9855790..b60ed2c 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDS.java @@ -7,8 +7,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; - import java.util.Set; + import org.ovirt.engine.core.common.businessentities.network.Network; import org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface; import org.ovirt.engine.core.common.utils.ObjectUtils; @@ -1442,4 +1442,12 @@ return countThreadsAsCores; } + public Set<String> getAdditionalFeatures() { + return this.mVdsDynamic.getAdditionalFeatures(); + } + + public void setAdditionalFeatures(Set<String> additionalFeatures) { + this.mVdsDynamic.setAdditionalFeatures(additionalFeatures); + } + } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDSGroup.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDSGroup.java index 1d577b7..206d937 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDSGroup.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VDSGroup.java @@ -82,6 +82,8 @@ private String clusterPolicyName; + private Set<SupportedAdditionalClusterFeature> addtionalFeaturesSupported; + @ValidUri(message = "VALIDATION.VDS_GROUP.SPICE_PROXY.HOSTNAME_OR_IP", groups = { CreateEntity.class, UpdateEntity.class }) @Size(max = BusinessEntitiesDefinitions.SPICE_PROXY_ADDR_SIZE) @@ -112,6 +114,7 @@ optimizationType = OptimizationType.NONE; requiredRngSources = new HashSet<VmRngDevice.Source>(); fencingPolicy = new FencingPolicy(); + addtionalFeaturesSupported = new HashSet<>(); } @Override @@ -395,6 +398,14 @@ this.glusterTunedProfile = glusterTunedProfile; } + public Set<SupportedAdditionalClusterFeature> getAddtionalFeaturesSupported() { + return addtionalFeaturesSupported; + } + + public void setAddtionalFeaturesSupported(Set<SupportedAdditionalClusterFeature> addtionalFeaturesSupported) { + this.addtionalFeaturesSupported = addtionalFeaturesSupported; + } + @Override public int hashCode() { final int prime = 31; @@ -428,6 +439,7 @@ result = prime * result + (groupHostsAndVms == null ? 0 : groupHostsAndVms.hashCode()); result = prime * result + (fencingPolicy == null ? 0 : fencingPolicy.hashCode()); result = prime * result + (glusterTunedProfile == null ? 0 : glusterTunedProfile.hashCode()); + result = prime * result + (addtionalFeaturesSupported == null ? 0 : addtionalFeaturesSupported.hashCode()); return result; } @@ -483,7 +495,8 @@ && ObjectUtils.objectsEqual(groupHostsAndVms, other.groupHostsAndVms) && ObjectUtils.objectsEqual(requiredRngSources, other.requiredRngSources) && ObjectUtils.objectsEqual(fencingPolicy, other.fencingPolicy) - && ObjectUtils.objectsEqual(glusterTunedProfile, other.glusterTunedProfile); + && ObjectUtils.objectsEqual(glusterTunedProfile, other.glusterTunedProfile) + && ObjectUtils.objectsEqual(addtionalFeaturesSupported, other.addtionalFeaturesSupported); } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java index d417987..b256f3e 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/businessentities/VdsDynamic.java @@ -5,8 +5,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; - import java.util.Set; + import javax.validation.constraints.Size; import org.ovirt.engine.core.common.utils.ObjectUtils; @@ -179,6 +179,9 @@ this.supportedEmulatedMachines = supportedEmulatedMachines; } + // Set of additional features supported by the VDSM. + private Set<String> additionalFeatures; + public VdsDynamic() { rpmVersion = new RpmVersion(); libvirt_version = new RpmVersion(); @@ -202,6 +205,7 @@ supportedRngSources = new HashSet<VmRngDevice.Source>(); liveSnapshotSupport = true; // usually supported, exceptional case if it isn't. liveMergeSupport = true; + additionalFeatures = new HashSet<>(); } public Integer getcpu_cores() { @@ -667,6 +671,14 @@ return supportedRngSources; } + public Set<String> getAdditionalFeatures() { + return additionalFeatures; + } + + public void setAdditionalFeatures(Set<String> additionalFeatures) { + this.additionalFeatures = additionalFeatures; + } + @Override public int hashCode() { final int prime = 31; @@ -728,6 +740,7 @@ result = prime * result + (numaSupport ? 0 : 1); result = prime * result + (liveSnapshotSupport ? 0 : 1); result = prime * result + (liveMergeSupport ? 0 : 1); + result = prime * result + (additionalFeatures == null ? 0 : additionalFeatures.hashCode()); return result; } @@ -803,7 +816,8 @@ && powerManagementControlledByPolicy == other.powerManagementControlledByPolicy && ObjectUtils.objectsEqual(supportedRngSources, other.supportedRngSources) && liveSnapshotSupport == other.liveSnapshotSupport - && liveMergeSupport == other.liveMergeSupport; + && liveMergeSupport == other.liveMergeSupport + && ObjectUtils.objectsEqual(additionalFeatures, other.additionalFeatures); } } diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java index 2251393..7a57b17 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java @@ -500,6 +500,7 @@ VDS_GROUP_CANNOT_CHANGE_STORAGE_POOL(ErrorType.CONFLICT), VDS_GROUP_CANNOT_UPDATE_CPU_WITH_LOWER_HOSTS(ErrorType.CONFLICT), VDS_GROUP_CANNOT_UPDATE_COMPATIBILITY_VERSION_WITH_LOWER_HOSTS(ErrorType.INCOMPATIBLE_VERSION), + VDS_GROUP_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS(ErrorType.INCOMPATIBLE_VERSION), VDS_GROUP_CANNOT_UPDATE_VDS_UP(ErrorType.CONFLICT), VDS_GROUP_CANNOT_ADD_COMPATIBILITY_VERSION_WITH_LOWER_STORAGE_POOL(ErrorType.INCOMPATIBLE_VERSION), VDS_GROUP_CANNOT_REMOVE_HAS_VM_POOLS(ErrorType.CONFLICT), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/GetClusterFeaturesByVersionAndCategoryParameters.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/GetClusterFeaturesByVersionAndCategoryParameters.java new file mode 100644 index 0000000..154f8c8 --- /dev/null +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/GetClusterFeaturesByVersionAndCategoryParameters.java @@ -0,0 +1,46 @@ +package org.ovirt.engine.core.common.queries; + +import org.ovirt.engine.core.common.mode.ApplicationMode; +import org.ovirt.engine.core.compat.Version; + +public class GetClusterFeaturesByVersionAndCategoryParameters extends VdcQueryParametersBase { + private static final long serialVersionUID = 4623643366378282119L; + private Version version; + private ApplicationMode category; + + public GetClusterFeaturesByVersionAndCategoryParameters() { + + } + + public GetClusterFeaturesByVersionAndCategoryParameters(Version version, ApplicationMode category) { + this.version = version; + this.category = category; + setRefresh(false); + } + public Version getVersion() { + return version; + } + + public void setVersion(Version version) { + this.version = version; + } + + public ApplicationMode getCategory() { + return category; + } + + public void setCategory(ApplicationMode category) { + this.category = category; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("GetClusterFeaturesByVersionAndCategoryParameters {"); + sb.append("version="); + sb.append(getVersion()); + sb.append(", category="); + sb.append(getCategory()); + sb.append("}"); + return sb.toString(); + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/VdcQueryType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/VdcQueryType.java index e4062fa..b3bd1f7 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/VdcQueryType.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/queries/VdcQueryType.java @@ -112,6 +112,8 @@ GetVdsGroupsByStoragePoolId(VdcQueryAuthType.User), GetNumberOfActiveVmsInVdsGroupByVdsGroupId, GetNumberOfVmsInVdsGroupByVdsGroupId, + GetClusterFeaturesByVersionAndCategory, + GetClusterFeaturesByClusterId, // Certificate GetCACertificate(VdcQueryAuthType.User), diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dal/dbbroker/DbFacade.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dal/dbbroker/DbFacade.java index dcd93d3..d0f56c8 100644 --- a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dal/dbbroker/DbFacade.java +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dal/dbbroker/DbFacade.java @@ -62,6 +62,8 @@ import org.ovirt.engine.core.dao.BaseDiskDao; import org.ovirt.engine.core.dao.BookmarkDAO; import org.ovirt.engine.core.dao.BusinessEntitySnapshotDAO; +import org.ovirt.engine.core.dao.ClusterFeatureDao; +import org.ovirt.engine.core.dao.SupportedHostFeatureDao; import org.ovirt.engine.core.dao.CommandEntityDao; import org.ovirt.engine.core.dao.DAO; import org.ovirt.engine.core.dao.DaoFactory; @@ -1185,4 +1187,12 @@ public CpuProfileDao getCpuProfileDao() { return getDao(CpuProfileDao.class); } + + public ClusterFeatureDao getClusterFeatureDao() { + return getDao(ClusterFeatureDao.class); + } + + public SupportedHostFeatureDao getSupportedHostFeatureDao() { + return getDao(SupportedHostFeatureDao.class); + } } diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDao.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDao.java new file mode 100644 index 0000000..a3aecaa --- /dev/null +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDao.java @@ -0,0 +1,51 @@ +package org.ovirt.engine.core.dao; + +import java.util.Collection; +import java.util.Set; + +import org.ovirt.engine.core.common.businessentities.AdditionalFeature; +import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature; +import org.ovirt.engine.core.common.mode.ApplicationMode; +import org.ovirt.engine.core.compat.Guid; + + +public interface ClusterFeatureDao extends DAO { + + /** + * Add the given feature as a supported for the cluster + * + * @param feature + */ + public void addSupportedClusterFeature(SupportedAdditionalClusterFeature feature); + + /** + * Update the supported cluster feature. + * + * @param feature + */ + public void updateSupportedClusterFeature(SupportedAdditionalClusterFeature feature); + + /** + * Add all the supported features in batch + * + * @param features + */ + public void addAllSupportedClusterFeature(Collection<SupportedAdditionalClusterFeature> features); + + /** + * get all the features supported by the cluster + * + * @param clusterId + * @return + */ + public Set<SupportedAdditionalClusterFeature> getSupportedFeaturesByClusterId(Guid clusterId); + + /** + * get all the features for the given version + * + * @param version + * @return + */ + public Set<AdditionalFeature> getClusterFeaturesForVersionAndCategory(String version, ApplicationMode category); + +} diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDaoImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDaoImpl.java new file mode 100644 index 0000000..c0db2b6 --- /dev/null +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/ClusterFeatureDaoImpl.java @@ -0,0 +1,106 @@ +package org.ovirt.engine.core.dao; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.ovirt.engine.core.common.businessentities.AdditionalFeature; +import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature; +import org.ovirt.engine.core.common.mode.ApplicationMode; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.compat.Version; +import org.ovirt.engine.core.dal.dbbroker.MapSqlParameterMapper; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; + +@Named +@Singleton +public class ClusterFeatureDaoImpl extends BaseDAODbFacade implements ClusterFeatureDao { + + private static final RowMapper<SupportedAdditionalClusterFeature> supportedClusterFeatureRowMapper = + new SupportedClusterFeatureRowMapper(); + private static final RowMapper<AdditionalFeature> clusterFeatureRowMapper = new ClusterFeatureRowMapper(); + + private static class ClusterFeatureRowMapper implements RowMapper<AdditionalFeature> { + @Override + public AdditionalFeature mapRow(ResultSet rs, int rowNum) throws SQLException { + AdditionalFeature feature = new AdditionalFeature(); + feature.setId(getGuidDefaultEmpty(rs, "feature_id")); + feature.setName(rs.getString("feature_name")); + feature.setDescription(rs.getString("description")); + feature.setCategory(ApplicationMode.from(rs.getInt("category"))); + feature.setVersion(new Version(rs.getString("version"))); + return feature; + } + } + + private static final class SupportedClusterFeatureRowMapper implements RowMapper<SupportedAdditionalClusterFeature> { + @Override + public SupportedAdditionalClusterFeature mapRow(ResultSet rs, int rowNum) + throws SQLException { + AdditionalFeature feature = clusterFeatureRowMapper.mapRow(rs, rowNum); + SupportedAdditionalClusterFeature supportedClusterFeature = new SupportedAdditionalClusterFeature(); + supportedClusterFeature.setFeature(feature); + supportedClusterFeature.setClusterId(getGuidDefaultEmpty(rs, "cluster_id")); + supportedClusterFeature.setEnabled(rs.getBoolean("is_enabled")); + return supportedClusterFeature; + } + } + + @Override + public void addSupportedClusterFeature(SupportedAdditionalClusterFeature feature) { + getCallsHandler().executeModification("InsertSupportedClusterFeature", + createSupportedClusterFeatureParameterMapper(feature)); + } + + @Override + public void updateSupportedClusterFeature(SupportedAdditionalClusterFeature feature) { + getCallsHandler().executeModification("UpdateSupportedClusterFeature", + createSupportedClusterFeatureParameterMapper(feature)); + } + + private MapSqlParameterSource createSupportedClusterFeatureParameterMapper(SupportedAdditionalClusterFeature clusterFeature) { + return getCustomMapSqlParameterSource() + .addValue("cluster_id", clusterFeature.getClusterId()) + .addValue("feature_id", clusterFeature.getFeature().getId()) + .addValue("is_enabled", clusterFeature.isEnabled()); + } + + @Override + public void addAllSupportedClusterFeature(Collection<SupportedAdditionalClusterFeature> features) { + getCallsHandler().executeStoredProcAsBatch("InsertSupportedClusterFeature", + features, + new MapSqlParameterMapper<SupportedAdditionalClusterFeature>() { + @Override + public MapSqlParameterSource map(SupportedAdditionalClusterFeature feature) { + return createSupportedClusterFeatureParameterMapper(feature); + } + }); + } + + @Override + public Set<SupportedAdditionalClusterFeature> getSupportedFeaturesByClusterId(Guid clusterId) { + List<SupportedAdditionalClusterFeature> features = + getCallsHandler().executeReadList("GetSupportedClusterFeaturesByClusterId", + supportedClusterFeatureRowMapper, + getCustomMapSqlParameterSource().addValue("cluster_id", clusterId)); + return new HashSet<>(features); + } + + @Override + public Set<AdditionalFeature> getClusterFeaturesForVersionAndCategory(String version, ApplicationMode category) { + List<AdditionalFeature> features = + getCallsHandler().executeReadList("GetClusterFeaturesByVersionAndCategory", + clusterFeatureRowMapper, + getCustomMapSqlParameterSource().addValue("version", version) + .addValue("category", category)); + return new HashSet<>(features); + } + +} diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDao.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDao.java new file mode 100644 index 0000000..51544f3 --- /dev/null +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDao.java @@ -0,0 +1,41 @@ +package org.ovirt.engine.core.dao; + +import java.util.Set; + +import org.ovirt.engine.core.compat.Guid; + + +public interface SupportedHostFeatureDao extends DAO { + + /** + * Add the given feature to the supported_host_features table. + * + * @param hostId + * @param feature + */ + void addSupportedHostFeature(Guid hostId, String feature); + + /** + * Add all the given features to the supported_host_features table. + * + * @param hostId + * @param features + */ + void addAllSupportedHostFeature(Guid hostId, Set<String> features); + + /** + * Remove all the given features from the supported_host_features table. + * + * @param hostId + * @param features + */ + void removeAllSupportedHostFeature(Guid hostId, Set<String> features); + + /** + * Returns the list of features supported by the host + * + * @param hostId + * @return + */ + Set<String> getSupportedHostFeaturesByHostId(Guid hostId); +} diff --git a/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoImpl.java b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoImpl.java new file mode 100644 index 0000000..12b5cd0 --- /dev/null +++ b/backend/manager/modules/dal/src/main/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoImpl.java @@ -0,0 +1,71 @@ +package org.ovirt.engine.core.dao; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.dal.dbbroker.MapSqlParameterMapper; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; + +@Named +@Singleton +public class SupportedHostFeatureDaoImpl extends BaseDAODbFacade implements SupportedHostFeatureDao { + + private static class SupportedHostFeatureRowMapper implements RowMapper<String> { + public static final SupportedHostFeatureRowMapper instance = new SupportedHostFeatureRowMapper(); + @Override + public String mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getString("feature_name"); + } + } + + private MapSqlParameterSource createSupportedHostFeatureParameterMapper(String feature, Guid hostId) { + return getCustomMapSqlParameterSource() + .addValue("host_id", hostId) + .addValue("feature_name", feature); + } + + @Override + public void addSupportedHostFeature(Guid hostId, String feature) { + getCallsHandler().executeModification("InsertSupportedHostFeature", + createSupportedHostFeatureParameterMapper(feature, hostId)); + } + + @Override + public void addAllSupportedHostFeature(final Guid hostId, Set<String> features) { + getCallsHandler().executeStoredProcAsBatch("InsertSupportedHostFeature", features, + new MapSqlParameterMapper<String>() { + @Override + public MapSqlParameterSource map(String feature) { + return createSupportedHostFeatureParameterMapper(feature, hostId); + } + }); + } + + @Override + public Set<String> getSupportedHostFeaturesByHostId(Guid hostId) { + List<String> featureList = getCallsHandler().executeReadList("GetSupportedHostFeaturesByHostId", + SupportedHostFeatureRowMapper.instance, + getCustomMapSqlParameterSource().addValue("host_id", hostId)); + return new HashSet<>(featureList); + } + + @Override + public void removeAllSupportedHostFeature(final Guid hostId, Set<String> features) { + getCallsHandler().executeStoredProcAsBatch("RemoveSupportedHostFeature", features, + new MapSqlParameterMapper<String>() { + @Override + public MapSqlParameterSource map(String feature) { + return createSupportedHostFeatureParameterMapper(feature, hostId); + } + }); + + } +} diff --git a/backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties b/backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties index 6657bde..df3006d 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/AppErrors.properties @@ -457,6 +457,8 @@ -Please move Hosts with lower CPU to maintenance first. VDS_GROUP_CANNOT_UPDATE_COMPATIBILITY_VERSION_WITH_LOWER_HOSTS=Cannot change Cluster Compatibility Version to higher version when there are active Hosts with lower version.\n\ -Please move Hosts with lower version to maintenance first. +VDS_GROUP_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS=Cannot enable new Cluster feature when there are active hosts which don't support the selected feature.\n\ + -Please move hosts with lower version to maintenance first. VDS_GROUP_CANNOT_UPDATE_VDS_UP=Cannot change Cluster.Trying to connect Cluster to Data Center with Hosts that are up. VDS_GROUP_CANNOT_ADD_COMPATIBILITY_VERSION_WITH_LOWER_STORAGE_POOL=Cannot add Cluster with Compatibility Version that is lower than the Data Center Compatibility Version.\n\ -Please upgrade your Cluster to a later Compatibility version first. diff --git a/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties b/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties index 703da244..018131d 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties @@ -696,6 +696,8 @@ EMULATED_MACHINES_INCOMPATIBLE_WITH_CLUSTER=Host ${VdsName} does not comply with the cluster ${VdsGroupName} emulated machines. The Hosts emulated machines are ${hostSupportedEmulatedMachines} and the cluster is ${clusterEmulatedMachines}} MIXING_RHEL_VERSIONS_IN_CLUSTER=Not possible to mix RHEL 6.x and 7.x hosts in one cluster. Tried adding ${addingRhel} host to a cluster with ${previousRhel} hosts. RNG_SOURCES_INCOMPATIBLE_WITH_CLUSTER=Host ${VdsName} does not comply with the cluster ${VdsGroupName} Random Number Generator sources. The Hosts supported sources are: ${hostSupportedRngSources}; and the cluster requirements are: ${clusterRequiredRngSources}. +HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER=Host ${VdsName} does not comply with the list of features supported by cluster ${VdsGroupName}. ${UnSupportedFeature} is not supported by the Host + # NUMA Messages NUMA_ADD_VM_NUMA_NODE_SUCCESS=Add VM NUMA node successfully. NUMA_ADD_VM_NUMA_NODE_FAILED=Add VM NUMA node failed. diff --git a/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/ClusterFeatureDaoTest.java b/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/ClusterFeatureDaoTest.java new file mode 100644 index 0000000..c5e9e1f --- /dev/null +++ b/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/ClusterFeatureDaoTest.java @@ -0,0 +1,114 @@ +package org.ovirt.engine.core.dao; + +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Test; +import org.ovirt.engine.core.common.businessentities.AdditionalFeature; +import org.ovirt.engine.core.common.businessentities.SupportedAdditionalClusterFeature; +import org.ovirt.engine.core.common.mode.ApplicationMode; +import org.ovirt.engine.core.compat.Guid; + +public class ClusterFeatureDaoTest extends BaseDAOTestCase { + private static final Guid EXISTING_VDS_GROUP = FixturesTool.VDS_GROUP_RHEL6_ISCSI; + + private static final Guid NEW_SUPPORTED_FEATURE_1 = new Guid("00000000-0000-0000-0000-000000000004"); + private static final Guid NEW_SUPPORTED_FEATURE_2 = new Guid("00000000-0000-0000-0000-000000000005"); + + private ClusterFeatureDao dao; + + @Override + public void setUp() throws Exception { + super.setUp(); + dao = dbFacade.getClusterFeatureDao(); + } + + @Test + public void testGetClusterFeaturesForVersionAndCategory() { + verifyFeaturesReturned(dao.getClusterFeaturesForVersionAndCategory("3.5", ApplicationMode.VirtOnly), + Arrays.asList("TEST_FEATURE_1", "TEST_FEATURE_3", "TEST_FEATURE_4", "TEST_FEATURE_5")); + verifyFeaturesReturned(dao.getClusterFeaturesForVersionAndCategory("3.5", ApplicationMode.GlusterOnly), + Arrays.asList("TEST_FEATURE_2", "TEST_FEATURE_3", "TEST_FEATURE_4", "TEST_FEATURE_5")); + verifyFeaturesReturned(dao.getClusterFeaturesForVersionAndCategory("3.5", ApplicationMode.AllModes), + Arrays.asList("TEST_FEATURE_1", "TEST_FEATURE_2", "TEST_FEATURE_3", "TEST_FEATURE_4", "TEST_FEATURE_5")); + } + + private void verifyFeaturesReturned(Set<AdditionalFeature> featuresFromDb, List<String> featuresExpdected) { + assertNotNull("Failed to retrive additional features for the version and category", featuresFromDb); + assertTrue("Failed to retrive correct set of features for the given version and category", + featuresFromDb.size() == featuresExpdected.size()); + for (AdditionalFeature feature : featuresFromDb) { + assertThat("Wrong feature returned from DB", featuresExpdected, hasItem(feature.getName())); + } + } + + @Test + public void testGetSupportedFeaturesByClusterId() { + Set<SupportedAdditionalClusterFeature> featuresSupportedInCluster = dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + List<String> expectedFeatures = Arrays.asList("TEST_FEATURE_1", "TEST_FEATURE_2", "TEST_FEATURE_3"); + assertNotNull("Failed to retrive supported additional features for the cluster", featuresSupportedInCluster); + assertThat("Failed to retrive correct set of features for the given version and category", + featuresSupportedInCluster, hasSize(expectedFeatures.size())); + for (SupportedAdditionalClusterFeature supportedFeatures : featuresSupportedInCluster) { + assertThat("Wrong feature returned from DB", + expectedFeatures, hasItem(supportedFeatures.getFeature().getName())); + } + } + + private SupportedAdditionalClusterFeature buildSupportedFeature(Guid featureId, Guid clusterId, boolean enabled) { + SupportedAdditionalClusterFeature supportedAdditionalClusterFeature = new SupportedAdditionalClusterFeature(); + supportedAdditionalClusterFeature.setClusterId(clusterId); + supportedAdditionalClusterFeature.setEnabled(enabled); + supportedAdditionalClusterFeature.setFeature(new AdditionalFeature()); + supportedAdditionalClusterFeature.getFeature().setId(featureId); + return supportedAdditionalClusterFeature; + } + + @Test + public void testAddSupportedClusterFeature() { + Set<SupportedAdditionalClusterFeature> previouslySupportedFeatures = + dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + dao.addSupportedClusterFeature(buildSupportedFeature(NEW_SUPPORTED_FEATURE_1, EXISTING_VDS_GROUP, true)); + Set<SupportedAdditionalClusterFeature> supportedFeatures = + dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + assertThat(supportedFeatures, hasSize(previouslySupportedFeatures.size() + 1)); + } + + @Test + public void testAddAllSupportedClusterFeature() { + Set<SupportedAdditionalClusterFeature> previouslySupportedFeatures = + dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + Set<SupportedAdditionalClusterFeature> newFeatures = new HashSet<>(); + newFeatures.add(buildSupportedFeature(NEW_SUPPORTED_FEATURE_1, EXISTING_VDS_GROUP, true)); + newFeatures.add(buildSupportedFeature(NEW_SUPPORTED_FEATURE_2, EXISTING_VDS_GROUP, true)); + dao.addAllSupportedClusterFeature(newFeatures); + Set<SupportedAdditionalClusterFeature> supportedFeatures = + dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + assertThat("Failed to add all the supported feature", + supportedFeatures, hasSize(previouslySupportedFeatures.size() + 2)); + } + + @Test + public void testUpdateSupportedClusterFeature() { + Set<SupportedAdditionalClusterFeature> features = + dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + for (SupportedAdditionalClusterFeature feature : features) { + feature.setEnabled(false); + dao.updateSupportedClusterFeature(feature); + // Lets stop after testing the first feature. + break; + } + Set<SupportedAdditionalClusterFeature> newFeatureSet = + dao.getSupportedFeaturesByClusterId(EXISTING_VDS_GROUP); + assertEquals("Failed to update the feature set", new HashSet<>(features), newFeatureSet); + } +} diff --git a/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoTest.java b/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoTest.java new file mode 100644 index 0000000..bcccb64 --- /dev/null +++ b/backend/manager/modules/dal/src/test/java/org/ovirt/engine/core/dao/SupportedHostFeatureDaoTest.java @@ -0,0 +1,64 @@ +package org.ovirt.engine.core.dao; + +import static org.hamcrest.Matchers.hasItem; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; +import org.ovirt.engine.core.compat.Guid; + + +public class SupportedHostFeatureDaoTest extends BaseDAOTestCase { + + private static final Guid EXISTING_HOST_ID_1 = new Guid("afce7a39-8e8c-4819-ba9c-796d316592e7"); + + private SupportedHostFeatureDao dao; + + @Override + public void setUp() throws Exception { + super.setUp(); + dao = dbFacade.getSupportedHostFeatureDao(); + } + + @Test + public void testGetSupportedHostFeaturesByHostId() { + Set<String> featuresSupported = dao.getSupportedHostFeaturesByHostId(EXISTING_HOST_ID_1); + assertNotNull("Failed to retrive supported addtional features in the host", featuresSupported); + Set<String> expectedFeatures = new HashSet<>(Arrays.asList("TEST_FEATURE_1", "TEST_FEATURE_2")); + assertEquals("Failed to retrive supported addtional features in the host", + expectedFeatures, + featuresSupported); + } + + @Test + public void testAddSupportedHostFeature() { + String newFeature = "NEW_FEATURE_1"; + dao.addSupportedHostFeature(EXISTING_HOST_ID_1, newFeature); + Set<String> featuresSupported = dao.getSupportedHostFeaturesByHostId(EXISTING_HOST_ID_1); + assertThat("Failed to add the feature", featuresSupported, hasItem(newFeature)); + } + + @Test + public void testAddAllSupportedHostFeature() { + Set<String> newFatures = new HashSet<>(Arrays.asList("NEW__FEATURE_1", "NEW__FEATURE_2", "NEW__FEATURE_3")); + dao.addAllSupportedHostFeature(EXISTING_HOST_ID_1, newFatures); + Set<String> featuresSupported = dao.getSupportedHostFeaturesByHostId(EXISTING_HOST_ID_1); + assertTrue("Failed to add the feature", featuresSupported.containsAll(newFatures)); + } + + @Test + public void testRemoveAllSupportedHostFeature() { + Set<String> featuresSupported = dao.getSupportedHostFeaturesByHostId(EXISTING_HOST_ID_1); + assertFalse(featuresSupported.isEmpty()); + dao.removeAllSupportedHostFeature(EXISTING_HOST_ID_1, featuresSupported); + Set<String> featuresSupportedAfterDeletion = dao.getSupportedHostFeaturesByHostId(EXISTING_HOST_ID_1); + assertTrue("Failed to remove addtional supported features", featuresSupportedAfterDeletion.isEmpty()); + } +} diff --git a/backend/manager/modules/dal/src/test/resources/fixtures.xml b/backend/manager/modules/dal/src/test/resources/fixtures.xml index f55a400..98ccfe8 100644 --- a/backend/manager/modules/dal/src/test/resources/fixtures.xml +++ b/backend/manager/modules/dal/src/test/resources/fixtures.xml @@ -7452,4 +7452,78 @@ <value>2015-02-10 16:35:15.362494+05:30</value> </row> </table> + <table name="cluster_features"> + <column>feature_id</column> + <column>feature_name</column> + <column>version</column> + <column>category</column> + <column>description</column> + <row> + <value>00000000-0000-0000-0000-000000000001</value> + <value>TEST_FEATURE_1</value> + <value>3.5</value> + <value>1</value> + <value>Test Feature-1 for Virt </value> + </row> + <row> + <value>00000000-0000-0000-0000-000000000002</value> + <value>TEST_FEATURE_2</value> + <value>3.5</value> + <value>2</value> + <value>Test Feature-2 for Gluster</value> + </row> + <row> + <value>00000000-0000-0000-0000-000000000003</value> + <value>TEST_FEATURE_3</value> + <value>3.5</value> + <value>255</value> + <value>Test Feature-3 for both Virt and Gluster</value> + </row> + <row> + <value>00000000-0000-0000-0000-000000000004</value> + <value>TEST_FEATURE_4</value> + <value>3.5</value> + <value>255</value> + <value>Test Feature-4 for both Virt and Gluster</value> + </row> + <row> + <value>00000000-0000-0000-0000-000000000005</value> + <value>TEST_FEATURE_5</value> + <value>3.5</value> + <value>255</value> + <value>Test Feature-5 for both Virt and Gluster</value> + </row> + </table> + <table name="supported_cluster_features"> + <column>cluster_id</column> + <column>feature_id</column> + <column>is_enabled</column> + <row> + <value>b399944a-81ab-4ec5-8266-e19ba7c3c9d1</value> + <value>00000000-0000-0000-0000-000000000001</value> + <value>true</value> + </row> + <row> + <value>b399944a-81ab-4ec5-8266-e19ba7c3c9d1</value> + <value>00000000-0000-0000-0000-000000000002</value> + <value>true</value> + </row> + <row> + <value>b399944a-81ab-4ec5-8266-e19ba7c3c9d1</value> + <value>00000000-0000-0000-0000-000000000003</value> + <value>true</value> + </row> + </table> + <table name="supported_host_features"> + <column>host_id</column> + <column>feature_name</column> + <row> + <value>afce7a39-8e8c-4819-ba9c-796d316592e7</value> + <value>TEST_FEATURE_1</value> + </row> + <row> + <value>afce7a39-8e8c-4819-ba9c-796d316592e7</value> + <value>TEST_FEATURE_2</value> + </row> + </table> </dataset> diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/VdsManager.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/VdsManager.java index 3a8a43a..d014e03 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/VdsManager.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/VdsManager.java @@ -6,6 +6,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -42,6 +43,7 @@ import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase; +import org.ovirt.engine.core.dao.SupportedHostFeatureDao; import org.ovirt.engine.core.utils.NumaUtils; import org.ovirt.engine.core.utils.crypt.EngineEncryptionUtils; import org.ovirt.engine.core.utils.lock.EngineLock; @@ -619,6 +621,7 @@ // persist to db the host's cpu_flags. // TODO this needs to be revisited - either all the logic is in-memory or based on db DbFacade.getInstance().getVdsDynamicDao().updateCpuFlags(vds.getId(), vds.getCpuFlags()); + processHostFeaturesReported(vds); monitoringStrategy.processHardwareCapabilities(vds); } monitoringStrategy.processSoftwareCapabilities(vds); @@ -648,6 +651,20 @@ } } + private void processHostFeaturesReported(VDS host) { + SupportedHostFeatureDao hostFeatureDao = DbFacade.getInstance().getSupportedHostFeatureDao(); + Set<String> supportedHostFeatures = hostFeatureDao.getSupportedHostFeaturesByHostId(host.getId()); + Set<String> featuresReturendByVdsCaps = new HashSet<String>(host.getAdditionalFeatures()); + host.getAdditionalFeatures().removeAll(supportedHostFeatures); + if (!host.getAdditionalFeatures().isEmpty()) { + hostFeatureDao.addAllSupportedHostFeature(host.getId(), host.getAdditionalFeatures()); + } + supportedHostFeatures.removeAll(featuresReturendByVdsCaps); + if (!supportedHostFeatures.isEmpty()) { + hostFeatureDao.removeAllSupportedHostFeature(host.getId(), supportedHostFeatures); + } + } + private long calcTimeoutToFence(int vmCount, VdsSpmStatus spmStatus) { int spmIndicator = 0; if (spmStatus != VdsSpmStatus.None) { diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java index 9305232..f36562f 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsBrokerObjectsBuilder.java @@ -517,6 +517,18 @@ } else { vds.setLiveMergeSupport(false); } + + updateAdditionalFeatures(vds, xmlRpcStruct); + } + + private static void updateAdditionalFeatures(VDS vds, Map<String, Object> xmlRpcStruct) { + String[] addtionalFeaturesSupportedByHost = + AssignStringArrayValue(xmlRpcStruct, VdsProperties.ADDITIONAL_FEATURES); + if (addtionalFeaturesSupportedByHost != null) { + for (String feature : addtionalFeaturesSupportedByHost) { + vds.getAdditionalFeatures().add(feature); + } + } } private static void setRngSupportedSourcesToVds(VDS vds, Map<String, Object> xmlRpcStruct) { @@ -1031,20 +1043,26 @@ return null; } - private static String AssignStringValueFromArray(Map<String, Object> input, String name) { + private static String[] AssignStringArrayValue(Map<String, Object> input, String name) { + String[] array = null; if (input.containsKey(name)) { - String[] arr = (String[]) ((input.get(name) instanceof String[]) ? input.get(name) : null); - if (arr == null) { + array = (String[]) ((input.get(name) instanceof String[]) ? input.get(name) : null); + if (array == null) { Object[] arr2 = (Object[]) ((input.get(name) instanceof Object[]) ? input.get(name) : null); if (arr2 != null) { - arr = new String[arr2.length]; + array = new String[arr2.length]; for (int i = 0; i < arr2.length; i++) - arr[i] = arr2[i].toString(); + array[i] = arr2[i].toString(); } } - if (arr != null) { - return StringUtils.join(arr, ','); - } + } + return array; + } + + private static String AssignStringValueFromArray(Map<String, Object> input, String name) { + String[] arr = AssignStringArrayValue(input, name); + if (arr != null) { + return StringUtils.join(arr, ','); } return null; } diff --git a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java index 675c0c9..fb43f83 100644 --- a/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java +++ b/backend/manager/modules/vdsbroker/src/main/java/org/ovirt/engine/core/vdsbroker/vdsbroker/VdsProperties.java @@ -355,6 +355,7 @@ public static final String TUNNELED = "tunneled"; public static final String DST_QEMU = "dstqemu"; public static final String MIGRATION_DOWNTIME = "downtime"; + public static final String ADDITIONAL_FEATURES = "additionalFeatures"; // storage domains public static final String code = "code"; diff --git a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java index ca0ea57..e5227dc 100644 --- a/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java +++ b/frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java @@ -1291,6 +1291,9 @@ @DefaultStringValue("Cannot change Cluster Compatibility Version to higher version when there are active Hosts with lower version.\n-Please move Hosts with lower version to maintenance first.") String VDS_GROUP_CANNOT_UPDATE_COMPATIBILITY_VERSION_WITH_LOWER_HOSTS(); + @DefaultStringValue("Cannot enable new Cluster feature when there are active hosts which don't support the selected feature.\n-Please move hosts with lower version to maintenance first.") + String VDS_GROUP_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS(); + @DefaultStringValue("Cannot change Cluster.Trying to connect Cluster to Data Center with Hosts that are up.") String VDS_GROUP_CANNOT_UPDATE_VDS_UP(); diff --git a/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/core/Common.gwt.xml b/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/core/Common.gwt.xml index 1cd355f..3d8aceb 100644 --- a/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/core/Common.gwt.xml +++ b/frontend/webadmin/modules/gwt-common/src/main/resources/org/ovirt/engine/core/Common.gwt.xml @@ -130,6 +130,8 @@ <include name="common/businessentities/VDSDomainsData.java" /> <include name="common/businessentities/VdsDynamic.java" /> <include name="common/businessentities/VDSGroup.java" /> + <include name="common/businessentities/AdditionalFeature.java" /> + <include name="common/businessentities/SupportedAdditionalClusterFeature.java" /> <include name="common/businessentities/FencingPolicy.java" /> <include name="common/businessentities/VDSGroupHostsAndVMs.java" /> <include name="common/businessentities/VdsProtocol.java" /> diff --git a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/LocalizedEnums.java b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/LocalizedEnums.java index 4eb14b9..0e6ee29 100644 --- a/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/LocalizedEnums.java +++ b/frontend/webadmin/modules/uicompat/src/main/java/org/ovirt/engine/ui/uicompat/LocalizedEnums.java @@ -46,6 +46,8 @@ String NonOperationalReason___RNG_SOURCES_INCOMPATIBLE_WITH_CLUSTER(); + String NonOperationalReason___HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER(); + String UsbPolicy___ENABLED_LEGACY(); String UsbPolicy___ENABLED_NATIVE(); diff --git a/frontend/webadmin/modules/uicompat/src/main/resources/org/ovirt/engine/ui/uicompat/LocalizedEnums.properties b/frontend/webadmin/modules/uicompat/src/main/resources/org/ovirt/engine/ui/uicompat/LocalizedEnums.properties index 1c45713..b0217e1 100644 --- a/frontend/webadmin/modules/uicompat/src/main/resources/org/ovirt/engine/ui/uicompat/LocalizedEnums.properties +++ b/frontend/webadmin/modules/uicompat/src/main/resources/org/ovirt/engine/ui/uicompat/LocalizedEnums.properties @@ -18,6 +18,7 @@ NonOperationalReason___UNINITIALIZED=Host is uninitialized as it is not attested yet. NonOperationalReason___ARCHITECTURE_INCOMPATIBLE_WITH_CLUSTER=Host's architecture doesn't match the Cluster's architecture. NonOperationalReason___NETWORK_INTERFACE_IS_DOWN=One or more interfaces required by one or more networks is down. +NonOperationalReason___HOST_FEATURES_INCOMPATIBILE_WITH_CLUSTER=The Host doesn't support all the features required by the cluster. UsbPolicy___ENABLED_LEGACY=Legacy UsbPolicy___DISABLED=Disabled UsbPolicy___ENABLED_NATIVE=Native diff --git a/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties b/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties index 3bbfd4c..3ce9929 100644 --- a/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties +++ b/frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties @@ -461,6 +461,8 @@ -Please move Hosts with lower CPU to maintenance first. VDS_GROUP_CANNOT_UPDATE_COMPATIBILITY_VERSION_WITH_LOWER_HOSTS=Cannot change Cluster Compatibility Version to higher version when there are active Hosts with lower version.\n\ -Please move Hosts with lower version to maintenance first. +VDS_GROUP_CANNOT_UPDATE_SUPPORTED_FEATURES_WITH_LOWER_HOSTS=Cannot enable new Cluster feature when there are active hosts which don't support the selected feature.\n\ + -Please move hosts with lower version to maintenance first. VDS_GROUP_CANNOT_UPDATE_VDS_UP=Cannot change Cluster.Trying to connect Cluster to Data Center with Hosts that are up. VDS_GROUP_CANNOT_ADD_COMPATIBILITY_VERSION_WITH_LOWER_STORAGE_POOL=Cannot add Cluster with Compatibility Version that is lower than the Data Center Compatibility Version.\n\ -Please upgrade your Cluster to a later Compatibility version first. diff --git a/packaging/dbscripts/cluster_features_sp.sql b/packaging/dbscripts/cluster_features_sp.sql new file mode 100644 index 0000000..99ca205 --- /dev/null +++ b/packaging/dbscripts/cluster_features_sp.sql @@ -0,0 +1,126 @@ +/* ---------------------------------------------------------------- + Stored procedures for database operations on Cluster Features + related tables: cluster_features, supported_cluster_features, supported_host_features +----------------------------------------------------------------*/ +Create or replace function InsertClusterFeature(v_feature_id UUID, + v_feature_name VARCHAR(256), + v_version VARCHAR(40), + v_category INTEGER, + v_description TEXT) +RETURNS VOID +AS $procedure$ +BEGIN + INSERT INTO cluster_features(feature_id, feature_name, version, category, description) + VALUES(v_feature_id, v_feature_name, v_version, v_category, v_description); +END; $procedure$ +LANGUAGE plpgsql; + +Create or replace function UpdateClusterFeature(v_feature_id UUID, + v_feature_name VARCHAR(256), + v_version VARCHAR(40), + v_category INTEGER, + v_description TEXT) +RETURNS VOID +AS $procedure$ +BEGIN + UPDATE cluster_features + SET feature_name = v_feature_name, + version = v_version, + description = v_description, + category = v_category + where feature_id = v_feature_id; +END; $procedure$ +LANGUAGE plpgsql; + +-- The logic to get the applicable set of features for a given category is as below- +-- Calculate the bitwise AND of inpuyt v_category and category value of current role from roles table. +-- If the calculated value is greater than 0, the role is applicable. +-- +-- To explain with an example- +-- Currently supported category values which can be set for a feature- +-- 1. Virt 0000 0001 +-- 2. Gluster 0000 0010 +-- 3. All 1111 1111 +-- +-- Now suppose the value of input category is Gluster - 2 (0000 0010), then +-- set of features would include all the roles with category values either 2 or 255 +-- Now start doing bitwise AND for valid category values 1, 2 and 255 with input category. +-- Only bitwise AND with 2 and 255 would result in a value greater +-- than ZERO (0) and applicable set of features are identified. +-- +-- 1 & 2 (0000 0001 & 0000 0010) = 0000 0000 = 0 Features with this category would NOT be listed +-- 2 & 2 (0000 0010 & 0000 0010) = 0000 0010 = 2 > 0 Features with this category would be listed +-- 255 & 2 (1111 1111 & 0000 0010) = 0000 0010 = 2 > 0 Features with this category would be listed + +Create or replace FUNCTION GetClusterFeaturesByVersionAndCategory(v_version VARCHAR(256), v_category INTEGER) +RETURNS SETOF cluster_features STABLE +AS $procedure$ +BEGIN + RETURN QUERY SELECT * + FROM cluster_features + WHERE cluster_features.version = v_version and (cluster_features.category & v_category) > 0; +END; $procedure$ +LANGUAGE plpgsql; + + +Create or replace function InsertSupportedClusterFeature(v_feature_id UUID, + v_cluster_id UUID, + v_is_enabled BOOLEAN) +RETURNS VOID +AS $procedure$ +BEGIN + INSERT INTO supported_cluster_features(cluster_id, feature_id, is_enabled) + VALUES(v_cluster_id, v_feature_id, v_is_enabled); +END; $procedure$ +LANGUAGE plpgsql; + +Create or replace function UpdateSupportedClusterFeature(v_feature_id UUID, + v_cluster_id UUID, + v_is_enabled BOOLEAN) +RETURNS VOID +AS $procedure$ +BEGIN + UPDATE supported_cluster_features + SET is_enabled = v_is_enabled + where cluster_id = v_cluster_id and feature_id = v_feature_id; +END; $procedure$ +LANGUAGE plpgsql; + +Create or replace FUNCTION GetSupportedClusterFeaturesByClusterId(v_cluster_id UUID) +RETURNS SETOF supported_cluster_features_view STABLE +AS $procedure$ +BEGIN + RETURN QUERY SELECT * + FROM supported_cluster_features_view + WHERE cluster_id = v_cluster_id; +END; $procedure$ +LANGUAGE plpgsql; + +Create or replace function InsertSupportedHostFeature(v_host_id UUID, + v_feature_name VARCHAR(256)) +RETURNS VOID +AS $procedure$ +BEGIN + INSERT INTO supported_host_features(host_id, feature_name) + VALUES(v_host_id, v_feature_name); +END; $procedure$ +LANGUAGE plpgsql; + +Create or replace FUNCTION GetSupportedHostFeaturesByHostId(v_host_id UUID) +RETURNS SETOF supported_host_features STABLE +AS $procedure$ +BEGIN + RETURN QUERY SELECT * + FROM supported_host_features + WHERE host_id = v_host_id; +END; $procedure$ +LANGUAGE plpgsql; + +Create or replace function RemoveSupportedHostFeature(v_host_id UUID, + v_feature_name VARCHAR(256)) +RETURNS VOID +AS $procedure$ +BEGIN + DELETE FROM supported_host_features WHERE host_id = v_host_id and feature_name = v_feature_name; +END; $procedure$ +LANGUAGE plpgsql; diff --git a/packaging/dbscripts/create_views.sql b/packaging/dbscripts/create_views.sql index 69a410c..33f84e4 100644 --- a/packaging/dbscripts/create_views.sql +++ b/packaging/dbscripts/create_views.sql @@ -1814,6 +1814,14 @@ FROM gluster_georep_config georepConfig LEFT OUTER JOIN gluster_config_master ON gluster_config_master.config_key = georepConfig.config_key AND gluster_config_master.config_feature='geo_replication'; +CREATE OR REPLACE VIEW supported_cluster_features_view +AS +SELECT cluster_features.*, +supported_cluster_features.cluster_id, +supported_cluster_features.is_enabled +FROM cluster_features +INNER JOIN supported_cluster_features ON supported_cluster_features.feature_id = cluster_features.feature_id; + -- Affinity Groups view, including members CREATE OR REPLACE VIEW affinity_groups_view AS diff --git a/packaging/dbscripts/upgrade/03_05_1470_add_cluster_features_table.sql b/packaging/dbscripts/upgrade/03_05_1470_add_cluster_features_table.sql new file mode 100644 index 0000000..9ba9443 --- /dev/null +++ b/packaging/dbscripts/upgrade/03_05_1470_add_cluster_features_table.sql @@ -0,0 +1,48 @@ +-- ---------------------------------------------------------------------- +-- tables cluster_features, supported_cluster_features and supported_host_features +-- Maintains the list of features which are supported by Engine. This is on top of the +-- standard cluster compatibility version check. +-- ---------------------------------------------------------------------- + +-- The value of category for a feature decides if that feature is applicable for (GLuster or Virt or Both). +-- The value of category is represented by a unique binary number. Value of category should be a power of 2. +-- Current valid values of category which can be set for a feature are - +-- 1. Virt 0000 0001 +-- 2. Gluster 0000 0010 +-- 3. All 1111 1111 + +CREATE TABLE cluster_features +( + feature_id UUID NOT NULL, + feature_name VARCHAR(256) NOT NULL, + version VARCHAR(40), + category INTEGER NOT NULL, + description TEXT, + CONSTRAINT PK_cluster_features PRIMARY KEY (feature_id) +); + +CREATE INDEX IDX_cluster_features_version_and_category ON cluster_features(category, version); + +CREATE TABLE supported_cluster_features +( + cluster_id UUID NOT NULL, + feature_id UUID NOT NULL, + is_enabled BOOLEAN, + CONSTRAINT PK_supported_cluster_features PRIMARY KEY (cluster_id, feature_id), + FOREIGN KEY (cluster_id) REFERENCES vds_groups(vds_group_id) ON DELETE CASCADE, + FOREIGN KEY (feature_id) REFERENCES cluster_features(feature_id) ON DELETE CASCADE +) ; + +CREATE UNIQUE INDEX IDX_supported_cluster_features ON supported_cluster_features(cluster_id, feature_id); + +CREATE TABLE supported_host_features +( + host_id UUID NOT NULL, + feature_name VARCHAR(256) NOT NULL, + CONSTRAINT PK_supported_host_features PRIMARY KEY (host_id, feature_name), + FOREIGN KEY (host_id) REFERENCES vds_static(vds_id) ON DELETE CASCADE +) ; + +INSERT INTO cluster_features VALUES ('00000017-0017-0017-0017-000000000066', 'GLUSTER_GEO_REPLICATION', '3.5', 2, 'Gluster Geo-Replication'); +INSERT INTO cluster_features VALUES ('00000018-0018-0018-0018-000000000093', 'GLUSTER_SNAPSHOT', '3.5', 2, 'Gluster Volume Snapshot Management'); +INSERT INTO cluster_features VALUES ('00000019-0019-0019-0019-000000000300', 'GLUSTER_BRICK_MANAGEMENT', '3.5', 2, 'Gluster Brick Provisioning'); -- To view, visit https://gerrit.ovirt.org/41025 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Icba02b189a169bc676e0c5f47f7aaf394f0b49a6 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: ovirt-engine-3.5-gluster Gerrit-Owner: Ramesh N <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
