Tomer Saban has uploaded a new change for review. Change subject: core: Added affinity rules enforcement manager ......................................................................
core: Added affinity rules enforcement manager A new engine internal manager that will enforce affinity rules. The manager will periodically query a list of VMs that break affinity rules, and will try to resolve the conflicts by migrating problematic VMs. One VM each period. Each cluster will have a separate task in the manager. Change-Id: I674666a4e84ca0f4bb3ed7eb96654f2362020d4d Bug-Url: https://bugzilla.redhat.com/1112332 Signed-off-by: Tomer Saban <tsa...@redhat.com> --- M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/InitBackendServicesOnStartupBean.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManager.java D backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AresService.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/AffinityRulesEnforcementPerCluster.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/MigrationEntryDS.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/commands/AffinityGroupCRUDCommand.java A backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManagerTest.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/AuditLogType.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java M backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties M frontend/webadmin/modules/frontend/src/main/java/org/ovirt/engine/ui/frontend/AppErrors.java M frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties M frontend/webadmin/modules/webadmin/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties 15 files changed, 1,125 insertions(+), 61 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/92/41092/7 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/InitBackendServicesOnStartupBean.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/InitBackendServicesOnStartupBean.java index 4494d8f..f3d667d 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/InitBackendServicesOnStartupBean.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/InitBackendServicesOnStartupBean.java @@ -17,7 +17,7 @@ import org.ovirt.engine.core.bll.job.ExecutionHandler; import org.ovirt.engine.core.bll.network.macpoolmanager.MacPoolPerDcSingleton; import org.ovirt.engine.core.bll.pm.PmHealthCheckManager; -import org.ovirt.engine.core.bll.scheduling.AresService; +import org.ovirt.engine.core.bll.scheduling.AffinityRulesEnforcementManager; import org.ovirt.engine.core.bll.scheduling.MigrationHandler; import org.ovirt.engine.core.bll.scheduling.SchedulingManager; import org.ovirt.engine.core.bll.storage.StoragePoolStatusHandler; @@ -47,9 +47,9 @@ @Inject private Instance<BackendService> services; - + @Inject - private AresService aresService; + private AffinityRulesEnforcementManager aremManager; /** * This method is called upon the bean creation as part diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManager.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManager.java new file mode 100644 index 0000000..f5c071a --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManager.java @@ -0,0 +1,213 @@ +package org.ovirt.engine.core.bll.scheduling; + +import org.ovirt.engine.core.bll.Backend; +import org.ovirt.engine.core.bll.job.ExecutionHandler; +import org.ovirt.engine.core.bll.scheduling.arem.AffinityRulesEnforcementPerCluster; +import org.ovirt.engine.core.common.AuditLogType; +import org.ovirt.engine.core.common.action.MigrateVmParameters; +import org.ovirt.engine.core.common.action.VdcActionType; +import org.ovirt.engine.core.common.action.VdcReturnValueBase; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSGroup; +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.config.Config; +import org.ovirt.engine.core.common.config.ConfigValues; +import org.ovirt.engine.core.common.di.qualifier.Created; +import org.ovirt.engine.core.common.di.qualifier.Deleted; +import org.ovirt.engine.core.common.di.qualifier.Updated; + +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; +import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase; +import org.ovirt.engine.core.dao.VdsDAO; +import org.ovirt.engine.core.dao.VdsGroupDAO; +import org.ovirt.engine.core.di.Injector; +import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; +import org.ovirt.engine.core.utils.timer.SchedulerUtilQuartzImpl; + +import javax.annotation.PostConstruct; +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Singleton +public class AffinityRulesEnforcementManager { + //Definitions + //Fields + Map<VDSGroup, AffinityRulesEnforcementPerCluster> perClusterMap; + Iterator<AffinityRulesEnforcementPerCluster> AreClusterIterator = Collections.emptyIterator(); + Integer currentInterval; + + private String jobId; + + @Inject + protected AuditLogDirector auditLogDirector; + @Inject + protected VdsDAO vdsDao; + @Inject + protected VdsGroupDAO vdsGroupDao; + + @Inject + Instance<AffinityRulesEnforcementPerCluster> perClusterProvider; + + @PostConstruct + protected void wakeup() { + long regularInterval = getRegularInterval(); + long initialDelay = getInitialInterval(); + currentInterval = new Long(regularInterval).intValue(); + perClusterMap = new HashMap<>(); + + //Check that AffinityRulesEnforcementPerCluster exist in perClusterMap for each cluster in the engine. + List<VDSGroup> vdsGroups = getClusters(); + + for(VDSGroup vdsGroup : vdsGroups) { + if(!perClusterMap.containsKey(vdsGroup)) { + AffinityRulesEnforcementPerCluster perCluster = perClusterProvider.get(); + perCluster.setClusterId(vdsGroup.getId()); + perClusterMap.put(vdsGroup, perCluster); + } + } + + // Initialize Migrations in perClusters. + for(AffinityRulesEnforcementPerCluster perCluster : perClusterMap.values()) { + perCluster.initMigrations(); + } + + AuditLogableBase logable = new AuditLogableBase(); + auditLogDirector.log(logable, AuditLogType.AFFINITY_RULES_ENFORCEMENT_MANAGER_START); + + + scheduleJobs(regularInterval, initialDelay); + } + + protected List<VDSGroup> getClusters() { + return vdsGroupDao.getAll(); + } + + private Integer getInitialInterval() { + return Config.<Integer> getValue(ConfigValues.AffinityRulesEnforcementManagerInitialDelay); + } + + private Integer getRegularInterval() { + return Config.<Integer> getValue(ConfigValues.AffinityRulesEnforcementManagerRegularInterval); + } + + private Integer getMaximumMigrationTries() { + return Config.<Integer>getValue(ConfigValues.AffinityRulesEnforcementManagerMaximumMigrationTries); + } + + private Integer getLongInterval() { + return Config.<Integer> getValue(ConfigValues.AffinityRulesEnforcementManagerLongInterval); + } + + //Called each interval. + @OnTimerMethodAnnotation("refresh") + public void refresh() { + AuditLogableBase logable = new AuditLogableBase(); + auditLogDirector.log(logable, AuditLogType.AFFINITY_RULES_ENFORCEMENT_MANAGER_INTERVAL_REACHED); + + if (AreClusterIterator == null || !AreClusterIterator.hasNext()) { + AreClusterIterator = perClusterMap.values().iterator(); + } + + AffinityRulesEnforcementPerCluster perCluster = AreClusterIterator.next(); + + if (perCluster.checkIfCurrentlyMigrating()) { + currentInterval = getRegularInterval(); + return; + } + + //Current migration complete. + if (perCluster.lastMigrationFailed()) { + perCluster.updateMigrationFailure(); + } + + if (perCluster.getMigrationTries() >= getMaximumMigrationTries()) { + currentInterval = getLongInterval(); + perCluster.setMigrationTries(0); + return; + } + else if(currentInterval.equals(getLongInterval())) { + currentInterval = getRegularInterval(); + perCluster.setMigrationTries(0); + } + + VM vm; + vm = perCluster.chooseNextVmToMigrate(); + + while(vm == null && AreClusterIterator.hasNext()) { + vm = perCluster.chooseNextVmToMigrate(); + } + //All affinity groups are enforced. Waiting for long interval. + if(vm == null) { + currentInterval = getLongInterval(); + return; + } + + Guid vmToMigrate = vm.getId(); + MigrateVmParameters parameters = new MigrateVmParameters(false, vmToMigrate); + parameters.setInitialHosts(new ArrayList<Guid>(getInitialHosts())); + + VdcReturnValueBase migrationStatus = Backend.getInstance().runInternalAction(VdcActionType.MigrateVm, + parameters, + ExecutionHandler.createInternalJobContext()); + + perCluster.updateMigrationStatus(migrationStatus); + + currentInterval = getRegularInterval(); + } + + private List<Guid> getInitialHosts() { + List<Guid> initialHosts = new ArrayList<Guid>(); + + for(VDS host : vdsDao.getAll()) { + initialHosts.add(host.getId()); + } + + return initialHosts; + } + + //TODO: Make perClusterMap add the new change instead of initializing the entire perClusterMap again. + public void onChange(@Observes @Updated @Created @Deleted VDSGroup cluster) { + // handle cluster changes + perClusterMap = new HashMap<>(); + + /* initialize structures */ + + //Check that AffinityRulesEnforcementPerCluster exist in perClusterMap for each cluster in the engine. + List<VDSGroup> vdsGroups = getClusters(); + + for(VDSGroup vdsGroup : vdsGroups) { + if(!perClusterMap.containsKey(vdsGroup)) { + AffinityRulesEnforcementPerCluster perCluster = perClusterProvider.get(); + perCluster.setClusterId(vdsGroup.getId()); + perClusterMap.put(vdsGroup, perCluster); + } + } + + // Initialize Migrations in perClusters. + for(AffinityRulesEnforcementPerCluster perCluster : perClusterMap.values()) { + perCluster.initMigrations(); + } + } + + private void scheduleJobs(long regularInterval, long longInterval) { + /* start the interval refreshing */ + jobId = Injector.get(SchedulerUtilQuartzImpl.class).scheduleAFixedDelayJob( + this, + "refresh", + new Class[] {}, + new Object[] {}, + regularInterval, + longInterval, + TimeUnit.MINUTES); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AresService.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AresService.java deleted file mode 100644 index 635aa13..0000000 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/AresService.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.ovirt.engine.core.bll.scheduling; - -import org.ovirt.engine.core.common.businessentities.VDSGroup; -import org.ovirt.engine.core.common.di.qualifier.Created; -import org.ovirt.engine.core.common.di.qualifier.Deleted; -import org.ovirt.engine.core.common.di.qualifier.Updated; -import org.ovirt.engine.core.common.scheduling.AffinityGroup; -import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation; -import org.ovirt.engine.core.utils.timer.SchedulerUtilQuartzImpl; - -import javax.annotation.PostConstruct; -import javax.enterprise.event.Observes; -import javax.inject.Singleton; -import java.util.concurrent.TimeUnit; - -@Singleton -public class AresService { - - private String jobId; - - @PostConstruct - private void init() { - - long x = 3000; //TODO take from config - ConfigValues.AresServiceInterval - long y = 3000; //TODO take from config - ConfigValues.AresServiceInterval - - /* initialize structures */ - scheduleJobs(x, y); - - } - - @OnTimerMethodAnnotation("refresh") - public void refresh() { - // - } - - public void onChange(@Observes @Updated @Created @Deleted AffinityGroup affinityGroup) { - // handle affinity group changes - } - - public void onChange(@Observes @Updated @Created @Deleted VDSGroup cluster) { - // handle cluster changes - } - - private void scheduleJobs(long x, long y) { - /* start the interval refreshing */ - jobId = SchedulerUtilQuartzImpl.getInstance().scheduleAFixedDelayJob( - this, - "onTimer", - new Class[0], - new Object[0], - x, - y, - TimeUnit.MILLISECONDS); - } - -} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/AffinityRulesEnforcementPerCluster.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/AffinityRulesEnforcementPerCluster.java new file mode 100644 index 0000000..239da9c --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/AffinityRulesEnforcementPerCluster.java @@ -0,0 +1,406 @@ +package org.ovirt.engine.core.bll.scheduling.arem; + +import org.ovirt.engine.core.common.action.VdcReturnValueBase; +import org.ovirt.engine.core.common.businessentities.VDS; + +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.businessentities.VMStatus; +import org.ovirt.engine.core.common.scheduling.AffinityGroup; +import org.ovirt.engine.core.compat.Guid; + +import org.ovirt.engine.core.dao.VdsDAO; +import org.ovirt.engine.core.dao.VdsGroupDAO; +import org.ovirt.engine.core.dao.VmDAO; +import org.ovirt.engine.core.dao.scheduling.AffinityGroupDao; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Created by tsaban on 5/18/15. + */ +public class AffinityRulesEnforcementPerCluster { + //Definitions + //Fields + List<MigrationEntryDS> lastMigrations; + Integer migrationTries; + Guid clusterId; + + private static final Logger log = LoggerFactory.getLogger(AffinityRulesEnforcementPerCluster.class); + + @Inject + protected AffinityGroupDao affinityGroupDao; + @Inject + protected VmDAO vmDao; + @Inject + protected VdsDAO vdsDao; + @Inject + protected VdsGroupDAO vdsGroupDao; + + //Constructors + @PostConstruct + public void wakeup() { + this.lastMigrations = new ArrayList<>(); + this.migrationTries = 0; + } + + + //Methods + public void setClusterId(Guid clusterId) { + this.clusterId = clusterId; + } + + public Guid getClusterId() { + return clusterId; + } + + public void initMigrations() { + this.lastMigrations = new ArrayList<MigrationEntryDS>(); + this.migrationTries = 0; + } + + public Boolean checkIfCurrentlyMigrating() { + if(this.lastMigrations.isEmpty()) { + return false; + } + + VdcReturnValueBase migrationStatus = this.lastMigrations.get(0).getMigrationStatus(); + if(migrationStatus == null) { + return true; //null migrationStatus is an indicator means migration is still running. + } + + return migrationStatus.getSucceeded(); + } + + public boolean lastMigrationFailed() { + + //Checking last migration tail existence and that it's status is failure. + if( lastMigrations.isEmpty()) { + return false; //lastMigrations empty so migration didn't fail. + } + + MigrationEntryDS lastMigrationTail = lastMigrations.get(0); + + if(!lastMigrationTail.getMigrationStatus().getSucceeded()) { + return true; + } + + return false; //Migration succeeded. Therefore, it's not failed. + } + + public void updateMigrationFailure() { + migrationTries++; + lastMigrations.get(0).setMigrationReturnValue(null); //To avoid considering it a migration failure again. + } + + public Integer getMigrationTries() { + return migrationTries; + } + + public void setMigrationTries(Integer migrationTries) { + this.migrationTries = migrationTries; + } + + public VM chooseNextVmToMigrate() { + List<AffinityGroup> allAffinityGroups = getAllAffinityGroups(); + List<AffinityGroup> unifiedAffinityGroups = getUnifiedAffinityGroups(allAffinityGroups); + + Arrays.sort(unifiedAffinityGroups.toArray(), new AffinityGroupComparator()); + + Map<AffinityGroup, VDS> candidateHostsMap = new HashMap<>(); + + + for (AffinityGroup affinityGroup : unifiedAffinityGroups) { + Guid candidateId = chooseCandidateHostForMigration(affinityGroup); + VDS candidateHost = vdsDao.get(candidateId); + + //Not candidateHost was found. + if(candidateHost == null) { + log.error("No candidate host was found for affinity group."); + continue; + } + + candidateHostsMap.put(affinityGroup, candidateHost); + + for (Guid vmId : affinityGroup.getEntityIds()) { + VM vm = vmDao.get(vmId); + + if(!vm.isRunning()) { + continue; + } + + VDS host = vdsDao.get(vm.getRunOnVds()); + + if (!host.equals(candidateHost) && !vm.getStatus().equals(VMStatus.NotResponding)) { + MigrationEntryDS currentMigration = new MigrationEntryDS(vmId, vm.getRunOnVds()); + currentMigration.setTargetHost(candidateHost); + + if (lastMigrations.contains(currentMigration) || + lastMigrations.contains(currentMigration.oppositeMigration())) { + log.error("Migrations loop occurred. Shutting down manager."); + //Shutdown manager. + } else { + lastMigrations.add(currentMigration); + return vmDao.get(currentMigration.getCurrentVm()); + } + } + } + } + + //Checking negative affinity collisions + for(Map.Entry<AffinityGroup, VDS> entry : candidateHostsMap.entrySet()) { + for(Map.Entry<AffinityGroup, VDS> otherEntry: candidateHostsMap.entrySet()) { + if(otherEntry.getKey().equals(entry.getKey())) { + continue; //Same affinity group is irrelevant. + } + + if(otherEntry.getValue() == null ^ entry.getValue() == null) { + continue; + } + if(!otherEntry.getValue().equals(entry.getValue())) { + continue; //Different hosts can't be a collision. + } + + //Different affinity groups with the same hosts is a collision. + //TODO: Add the involved affinity groups to the error message. + log.error("Negative affinity group collision detected!"); + + } + } + + return null; + } + + private List<AffinityGroup> sortUnifiedAffinityGroups(List<AffinityGroup> unifiedAffinityGroups) { + Arrays.sort(unifiedAffinityGroups.toArray(), new AffinityGroupComparator()); + + List<AffinityGroup> output = new ArrayList<AffinityGroup>(); + + return unifiedAffinityGroups; + } + + + /** + ''' chooseCandidateHostForMigration(Unified Affinity Group - UAG) ''' + # hosts = createMapOfHostsAndVms() + # Return host with max number of Vms on it(More Vms from the same UAG means less migrations needed to enforce the UAG). + */ + protected Guid chooseCandidateHostForMigration(AffinityGroup ag) { + Map<Guid, Integer> mapOfHostsAndNumberOfVms = createMapOfHostsAndVms(ag); + + int maxNumberOfVms = -1; + for(Integer numberOfVms: mapOfHostsAndNumberOfVms.values()) { + maxNumberOfVms = Math.max(maxNumberOfVms, numberOfVms); + } + + for(Map.Entry<Guid, Integer> vdsToVmsEntry : mapOfHostsAndNumberOfVms.entrySet()) { + Integer numberOfVms = vdsToVmsEntry.getValue(); + + if(numberOfVms == maxNumberOfVms) { + return vdsToVmsEntry.getKey(); + } + } + + return null; + } + + /** + ''' createMapOfHostsAndVms() ''' + # map = {} + # for AG in UAG: + ## for VM in AG: + ### host = null + ### if vm in the middle of migration: + #### [9]get host that vm is migrating to. + ### Else if Vm’s host is available: + #### get the host. + ### if not host == null: + #### if host is key in map: + ##### map[host]++ + #### else: + ##### map[host] = 1 + # return map. + */ + protected Map<Guid, Integer> createMapOfHostsAndVms(AffinityGroup ag) { + Map<Guid, Integer> output = new HashMap<>(); + for(Guid vmId : ag.getEntityIds()) { + VM vm = vmDao.get(vmId); + Guid vdsId = vm.getRunOnVds(); + + //We will not add Vms which there are not run on any host. + if(vdsId == null) { + continue; + } + + if(!output.containsKey(vdsId)) { + output.put(vdsId, 1); + } + else { + Integer numberOfVms = output.get(vdsId); + output.put(vdsId, numberOfVms + 1); + } + } + + return output; + } + + /** + # UAG = {{vm} for each vm} + # For each (+) affinity group(Sorted by group id): + ## unify VMs from the group in UAG(Sorted by vm id). + ## For each (-) affinity group(Sorted by group id): + ### For each group in UAG(Sorted by first vm uuid): + #### if size of the intersection of group from UAG and the negative group is > 1: + ##### throw exception “Affinity group contradiction detected” (With associated groups). + */ + protected List<AffinityGroup> getUnifiedAffinityGroups(List<AffinityGroup> affinityGroups) { + Set<Set<Guid>> uag = new HashSet(); + + //uag = {{vm} for each vm in any affinity group(Either negative or positive)} + for(AffinityGroup ag: affinityGroups) { + for(Guid id : ag.getEntityIds()) { + Set<Guid> temp = new HashSet<>(); + temp.add(id); + uag.add(temp); + } + } + + //# For each (+) affinity group(Sorted by group id) - ag: + //# create empty Set<Vm> vmSet + //## For each vm in ag: + //### remove the group contains vm from uag + //### merge the group with vmSet + //# add vmSet back to uag. + for(AffinityGroup ag : affinityGroups) { + if(ag.isPositive()) { + Set<Guid> mergedSet = new HashSet<>(); + + for(Guid id : ag.getEntityIds()) { + Set<Guid> vmGroup = popVmGroupByGuid(uag, id); + mergedSet.addAll(vmGroup); + } + + uag.add(mergedSet); + } + } + + //Checking negative affinity group collisions + for(AffinityGroup ag : affinityGroups) { + if(ag.isPositive()) { + continue; + } + + for (Set<Guid> positiveGroup : uag) { + Set<Guid> intersection = new HashSet<>(ag.getEntityIds()); + intersection.retainAll(positiveGroup); + + if(intersection.size() > 1) { + return null; //Affinity rules collision detected. + } + } + } + + //Adding to UAG VMs that are not in it's Sets yet. + for(AffinityGroup ag : affinityGroups) { + if(ag.isPositive()) { + continue; + } + + //Getting the VMs which are not in group in UAG. + Set<Guid> vmSet = new HashSet<>(ag.getEntityIds()); + for (Set<Guid> positiveGroup : uag) { + Set<Guid> intersection = new HashSet<>(positiveGroup); + + intersection.retainAll(vmSet); + + for(Guid vm : intersection) { + vmSet.remove(vm); + } + } + + //Adding for each VM a set with it to the uag. + for(Guid vm : vmSet) { + Set<Guid> newSet = new HashSet<>(); + newSet.add(vm); + uag.add(newSet); + } + } + + //Sorting affinityGroups by group id. + return setsToUnifiedAffinityGroups(uag); + } + + private Set<Guid> popVmGroupByGuid(Set<Set<Guid>> uag, Guid id) { + for(Set<Guid> s : uag) { + if(s.contains(id)) { + uag.remove(s); + return s; + } + } + + log.error("Impossible situation happened. No vm group found for a vm during UnifiedAffinityGroups creation(Although all VMs should be added at the start).\n"); + return null; //Not really possible because we added all VMs at the start. + } + + private List<AffinityGroup> setsToUnifiedAffinityGroups(Set<Set<Guid>> uag) { + List<AffinityGroup> output = new ArrayList<AffinityGroup>(); + + for(Set<Guid> s : uag) { + AffinityGroup temp = new AffinityGroup(); + temp.setPositive(true); + List<Guid> entities = new ArrayList<Guid>(); + + for(Guid vm : s) { + entities.add(vm); + } + + temp.setEntityIds(entities); + output.add(temp); + } + + return output; + } + + public List<AffinityGroup> getAllAffinityGroups() { + return affinityGroupDao.getAllAffinityGroupsByClusterId(clusterId); + } + + public void updateMigrationStatus(VdcReturnValueBase migrationStatus) { + lastMigrations.get(0).setMigrationReturnValue(migrationStatus); + } + + private static class AffinityGroupComparator implements Comparator<Object> { + @Override + public int compare(Object o1, Object o2) { + AffinityGroup ag1 = (AffinityGroup)o1; + AffinityGroup ag2 = (AffinityGroup)o2; + + Arrays.sort(ag1.getEntityIds().toArray(), new GuidComparator()); + Arrays.sort(ag2.getEntityIds().toArray(), new GuidComparator()); + + return ag1.getEntityIds().get(0).compareTo(ag2.getEntityIds().get(0)); + } + + private static class GuidComparator implements Comparator<Object> { + @Override + public int compare(Object o1, Object o2) { + Guid g1 = (Guid) o1; + Guid g2 = (Guid) o2; + + int z = g1.compareTo(g2); + return z; + } + } + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/MigrationEntryDS.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/MigrationEntryDS.java new file mode 100644 index 0000000..0c84318 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/arem/MigrationEntryDS.java @@ -0,0 +1,63 @@ +package org.ovirt.engine.core.bll.scheduling.arem; + +import org.ovirt.engine.core.common.action.VdcReturnValueBase; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.compat.Guid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MigrationEntryDS { + //Definitions + //Fields + Guid vm; + Guid sourceHost; + VdcReturnValueBase migrationReturnValue; + VDS targetHost; + + private static final Logger log = LoggerFactory.getLogger(MigrationEntryDS.class); + + //Constructors + public MigrationEntryDS(Guid vm, Guid sourceHost) { + this.sourceHost = sourceHost; + this.vm = vm; + } + + //Methods + public Guid getCurrentVm() { + return vm; + } + + public MigrationEntryDS oppositeMigration() { + MigrationEntryDS oppositeMigration = new MigrationEntryDS(getCurrentVm(), getMigrationHost().getId()); + + return oppositeMigration; + } + + public void setMigrationReturnValue(VdcReturnValueBase returnValue) { + this.migrationReturnValue = returnValue; + } + + /** + * + * @return - Null if no targetHost specified or the targetHost if it is. + */ + public VDS getMigrationHost() { + return this.targetHost; + } + + /** + * + * @return - Null if there is no migration status yet or the migration status if there is. + */ + public VdcReturnValueBase getMigrationStatus() { + return this.migrationReturnValue; + } + + public void setTargetHost(VDS targetHost) { + this.targetHost = targetHost; + + if(targetHost.getId().equals(sourceHost)) { + log.error("Trying to set target host to be source host."); + } + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/commands/AffinityGroupCRUDCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/commands/AffinityGroupCRUDCommand.java index ad0b157..c7836a8 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/commands/AffinityGroupCRUDCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/scheduling/commands/AffinityGroupCRUDCommand.java @@ -9,12 +9,14 @@ import org.ovirt.engine.core.bll.CommandBase; import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.common.VdcObjectType; +import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VmStatic; import org.ovirt.engine.core.common.errors.VdcBllMessages; import org.ovirt.engine.core.common.scheduling.AffinityGroup; import org.ovirt.engine.core.common.scheduling.parameters.AffinityGroupCRUDParameters; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.DbFacade; +import org.ovirt.engine.core.dao.VmDAO; import org.ovirt.engine.core.dao.scheduling.AffinityGroupDao; public abstract class AffinityGroupCRUDCommand extends CommandBase<AffinityGroupCRUDParameters> { @@ -33,6 +35,7 @@ if (getVdsGroup() == null) { return failCanDoAction(VdcBllMessages.ACTION_TYPE_FAILED_INVALID_CLUSTER_FOR_AFFINITY_GROUP); } + if (getParameters().getAffinityGroup().getEntityIds() != null) { VmStatic vmStatic = null; Set<Guid> vmSet = new HashSet<>(); @@ -51,9 +54,132 @@ } } } + + return hasAffinityCollisions(getParameters()); + } + + private Boolean hasAffinityCollisions(AffinityGroupCRUDParameters parameters) { + List<AffinityGroup> affinity_groups = getAllAffinityGroups(parameters.getAffinityGroup().getClusterId()); + affinity_groups.add(parameters.getAffinityGroup()); + return getUnifiedAffinityGroups(affinity_groups); + } + + private List<AffinityGroup> getAllAffinityGroups(Guid clusterId) { + return DbFacade.getInstance().getAffinityGroupDao().getAllAffinityGroupsByClusterId(clusterId); + } + + /** + # UAG = {{vm} for each vm} + # For each (+) affinity group(Sorted by group id): + ## unify VMs from the group in UAG(Sorted by vm id). + ## For each (-) affinity group(Sorted by group id): + ### For each group in UAG(Sorted by first vm uuid): + #### if size of the intersection of group from UAG and the negative group is > 1: + ##### throw exception “Affinity group contradiction detected” (With associated groups). + */ + private Boolean getUnifiedAffinityGroups(List<AffinityGroup> affinityGroups) { + Set<Set<VM>> uag = new HashSet(); + + VmDAO vm_dao = DbFacade.getInstance().getVmDao(); + + //uag = {{vm} for each vm} + for(AffinityGroup ag: affinityGroups) { + for(Guid id : ag.getEntityIds()) { + Set<VM> temp = new HashSet<VM>(); + temp.add(vm_dao.get(id)); + uag.add(temp); + } + } + + //# For each (+) affinity group(Sorted by group id): + //## unify VMs from the group in UAG(Sorted by vm id). + for(AffinityGroup ag : affinityGroups) { + if(ag.isPositive()) { + Set<VM> vm_set; + vm_set = new HashSet<VM>(); + + for(Guid id : ag.getEntityIds()) { + Set<VM> vm_group = getVmGroupByGuid(uag, vm_dao.get(id)); + vm_set.addAll(vm_group); + } + + uag.add(vm_set); + } + } + + //Checking negative affinity group collisions + for(AffinityGroup ag : affinityGroups) { + if(ag.isPositive()) { + continue; + } + + Set<VM> vm_set = createVmsSetFromAffinityGroup(vm_dao, ag); + for (Set<VM> positive_group : uag) { + Set<VM> intersection = new HashSet<VM>(positive_group); + + intersection.retainAll(vm_set); + + if(intersection.size() > 1) { + return failCanDoAction(VdcBllMessages.ACTION_TYPE_FAILED_AFFINITY_RULES_COLLISION, + String.format("$UAG %s", positive_group.toString()), + String.format("$negativeAR %s", vm_set.toString())); + } + } + } + + //Adding to UAG VMs that are not in it's Sets yet. + for(AffinityGroup ag : affinityGroups) { + if(ag.isPositive()) { + continue; + } + + //Getting the VMs which are not in group in UAG. + Set<VM> vm_set = createVmsSetFromAffinityGroup(vm_dao, ag); + for (Set<VM> positive_group : uag) { + Set<VM> intersection = new HashSet<VM>(positive_group); + + intersection.retainAll(vm_set); + + for(VM vm : intersection) { + vm_set.remove(vm); + } + } + + //Adding for each VM a set with it to the uag. + for(VM vm : vm_set) { + Set<VM> new_set = new HashSet<VM>(); + new_set.add(vm); + uag.add(new_set); + } + } + + //Sorting affinityGroups by group id. return true; } + private Set<VM> getVmGroupByGuid(Set<Set<VM>> uag, VM vm) { + for(Set<VM> s : uag) { + if(s.contains(vm)) { + return s; + } + } + + log.error("Impossible situation happened. No vm group found for a vm during UnifiedAffinityGroups creation(Although all VMs should be added at the start).\n"); + return null; //Not really possible because we added all VMs at the start. + } + + private Set<VM> createVmsSetFromAffinityGroup(VmDAO vm_dao, AffinityGroup ag) { + Set<VM> vm_set; + VM vm; + vm_set = new HashSet<VM>(); + + for(Guid id : ag.getEntityIds()) { + vm = vm_dao.get(id); + vm_set.add(vm); + } + return vm_set; + } + protected AffinityGroup getAffinityGroup() { if (affinityGroup == null) { affinityGroup = getAffinityGroupDao().get(getParameters().getAffinityGroupId()); diff --git a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManagerTest.java b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManagerTest.java new file mode 100644 index 0000000..6e60492 --- /dev/null +++ b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/scheduling/AffinityRulesEnforcementManagerTest.java @@ -0,0 +1,283 @@ +package org.ovirt.engine.core.bll.scheduling; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.ovirt.engine.core.bll.InjectorRule; +import org.ovirt.engine.core.bll.scheduling.arem.AffinityRulesEnforcementPerCluster; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSGroup; +import org.ovirt.engine.core.common.businessentities.VM; +import org.ovirt.engine.core.common.businessentities.VMStatus; +import org.ovirt.engine.core.common.config.ConfigValues; +import org.ovirt.engine.core.common.scheduling.AffinityGroup; +import org.ovirt.engine.core.compat.Guid; + +import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector; +import org.ovirt.engine.core.dao.VdsDAO; +import org.ovirt.engine.core.dao.VdsGroupDAO; +import org.ovirt.engine.core.dao.VmDAO; +import org.ovirt.engine.core.dao.scheduling.AffinityGroupDao; +import org.ovirt.engine.core.utils.MockConfigRule; +import org.ovirt.engine.core.utils.timer.SchedulerUtilQuartzImpl; + + +import javax.enterprise.inject.Instance; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class AffinityRulesEnforcementManagerTest { + AffinityRulesEnforcementManager arem; + + @Rule + public MockConfigRule mockConfigRule = new MockConfigRule( + MockConfigRule.mockConfig(ConfigValues.AffinityRulesEnforcementManagerRegularInterval, 1), + MockConfigRule.mockConfig(ConfigValues.AffinityRulesEnforcementManagerInitialDelay, 1), + MockConfigRule.mockConfig(ConfigValues.AffinityRulesEnforcementManagerMaximumMigrationTries, 1), + MockConfigRule.mockConfig(ConfigValues.AffinityRulesEnforcementManagerLongInterval, 1) + ); + + @Rule + public InjectorRule injectorRule = new InjectorRule(); + + @Mock + AffinityRulesEnforcementPerCluster perCluster; + + @Mock + protected VdsGroupDAO _vdsGroupDao; //Clusters + + @Mock + protected AffinityGroupDao _affinityGroupDao; + + protected VDSGroup cluster; + + @Mock + public VdsDAO _vdsDao; + + @Mock + VmDAO _vmDao; + + @Mock + AuditLogDirector _auditLogDirector; + + @Mock + private SchedulerUtilQuartzImpl scheduler; + + @Mock + protected Instance<AffinityRulesEnforcementPerCluster> _perClusterProvider; + + @Before + public void setup() { + initVdsGroup(); + initMocks(); + + arem = new AffinityRulesEnforcementManager() { + @Override + public void wakeup() { + this.auditLogDirector = _auditLogDirector; + this.vdsDao = _vdsDao; + this.vdsGroupDao = _vdsGroupDao; + + when(_perClusterProvider.get()).thenReturn(Mockito.spy(new AffinityRulesEnforcementPerCluster() { + @Override + public void wakeup() { + this.affinityGroupDao = _affinityGroupDao; + this.vmDao = _vmDao; + this.vdsDao = _vdsDao; + this.vdsGroupDao = _vdsGroupDao; + super.wakeup(); + } + })); + + this.perClusterProvider = _perClusterProvider; + + super.wakeup(); + + addInjectionsToPerClusterObjects(); + } + + @Override + public void refresh() { + addInjectionsToPerClusterObjects(); + super.refresh(); + } + + private void addInjectionsToPerClusterObjects() { + //Adding affinity group dao to all perCluster objects in the maps. + Map<VDSGroup, AffinityRulesEnforcementPerCluster> iterator = new HashMap(arem.perClusterMap); + for(VDSGroup vdsGroup : iterator.keySet()) { + AffinityRulesEnforcementPerCluster perCluster = perClusterProvider.get(); + perCluster.setClusterId(vdsGroup.getId()); + arem.perClusterMap.put(vdsGroup, perCluster); + } + } + + @Override + protected List<VDSGroup> getClusters() { + List<VDSGroup> vdsGroups = new ArrayList<>(); + vdsGroups.add(cluster); + return vdsGroups; + } + + }; + arem.wakeup(); + arem.refresh(); + + for(AffinityRulesEnforcementPerCluster perCluster : arem.perClusterMap.values()) { + perCluster.wakeup(); + } + } + + protected void initVdsGroup() { + //Initiating cluster + Guid id = Guid.newGuid(); + cluster = new VDSGroup(); + cluster.setVdsGroupId(id); + cluster.setId(id); + cluster.setName("Default cluster"); + } + + protected void initMocks() { + injectorRule.bind(SchedulerUtilQuartzImpl.class, scheduler); + when(scheduler.scheduleAFixedDelayJob(any(), + anyString(), + any(Class[].class), + any(Object[].class), + anyInt(), + anyInt(), + any(TimeUnit.class) + )).thenReturn("jobId"); + } + + @Test + public void simplePositiveEnforcementUAGTest() { + VDSGroup vdsGroup = arem.perClusterMap.keySet().iterator().next(); + + //Add 2 hosts + VDS vdsId1 = _vdsDao.get(addHost(vdsGroup.getId())); + VDS vdsId2 = _vdsDao.get(addHost(vdsGroup.getId())); + + //Creating Affinity Group list with one positive affinity groups. + List<Guid> agList = new ArrayList<>(); + + List<Guid> vmList = new ArrayList<>(); + vmList.add(addNewVm(vdsId1.getId(), true)); + vmList.add(addNewVm(vdsId2.getId(), true)); + agList.add(addAffinityGroup(vmList, vdsGroup.getId(), true)); + + arem.refresh(); + } + + @Test + public void positiveEnforcementUAGTest() { + VDSGroup vdsGroup = arem.perClusterMap.keySet().iterator().next(); + AffinityRulesEnforcementPerCluster perCluster = arem.perClusterMap.get(vdsGroup); + + //Creating Affinity Group list with two positive affinity groups. + List<Guid> agList = new ArrayList<>(); + + List<Guid> vmList = new ArrayList<>(); + vmList.add(addNewVm(vdsGroup.getId(), true)); + agList.add(addAffinityGroup(vmList, vdsGroup.getId(), true)); + + vmList = new ArrayList<>(); + vmList.add(addNewVm(vdsGroup.getId(), true)); + agList.add(addAffinityGroup(vmList, vdsGroup.getId(), true)); + + //Adding new Vm for both affinity groups + Guid vmId = addNewVm(vdsGroup.getId(), true); + for(Guid id : agList) { + AffinityGroup ag = _affinityGroupDao.get(id); + List<Guid> entities = ag.getEntityIds(); + entities.add(vmId); + ag.setEntityIds(entities); + } + + Assert.assertNotNull(perCluster.chooseNextVmToMigrate()); + } + + private Guid addNewVm(Guid vdsToRunOn, Boolean isRunning) { + Guid guid = Guid.newGuid(); + + VMStatus isResponding = VMStatus.Up; //All vms status is up + + VM vm = mock(VM.class); + when(_vmDao.get(guid)).thenReturn(vm); + doReturn(vdsToRunOn).when(vm).getRunOnVds(); + doReturn(isRunning).when(vm).isRunning(); + doReturn(isResponding).when(vm).getStatus(); + + + _vmDao.saveIsInitialized(guid, true); + + String outputStr = String.format("New VM[%s] sits on host[%s]", guid, vdsToRunOn); + System.out.println(outputStr); + return guid; + } + + private Guid addHost(Guid vdsGroupId) { + Guid id = Guid.newGuid(); + + VDS vds = mock(VDS.class); + doReturn(id).when(vds).getId(); + doReturn(vdsGroupId).when(vds).getVdsGroupId(); + + when(_vdsDao.get(id)).thenReturn(vds); + + List<VDS> vdsList = _vdsDao.getAllForVdsGroup(vdsGroupId); + vdsList.add(vds); + + when(_vdsDao.getAllForVdsGroup(vdsGroupId)).thenReturn(vdsList); + + String outputStr = String.format("New Host[%s] sits on cluster[%s]", id, vdsGroupId); + System.out.println(outputStr); + + return id; + } + + private Guid addAffinityGroup(List<Guid> vmList, Guid vdsGroupId, Boolean isPositive) { + Guid id = Guid.newGuid(); + AffinityGroup ag = mock(AffinityGroup.class); + doReturn(id).when(ag).getId(); + doReturn(isPositive).when(ag).isPositive(); + doReturn(vmList).when(ag).getEntityIds(); + doReturn(vdsGroupId).when(ag).getClusterId(); + + when(_affinityGroupDao.get(id)).thenReturn(ag); + + //Adding when() for getAllAffinityGroupsByClusterId() to return affinity groups list. + List<AffinityGroup> agList = _affinityGroupDao.getAllAffinityGroupsByClusterId(vdsGroupId); + agList.add(ag); + + when(_affinityGroupDao.getAllAffinityGroupsByClusterId(vdsGroupId)).thenReturn(agList); + + //Adding when() for getAll() + agList = _affinityGroupDao.getAll(); + agList.add(ag); + + when(_affinityGroupDao.getAll()).thenReturn(agList); + + String outputStr = String.format("New AffinityGroup[%s] sits on cluster[%s]\nwith Vms[%s]", id, vdsGroupId, vmList); + System.out.println(outputStr); + + return id; + } +} 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 786e8e4..b6a6672 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 @@ -1229,6 +1229,10 @@ USER_REMOVED_LIBVIRT_SECRET(10758), USER_FAILED_TO_REMOVE_LIBVIRT_SECRET(10759, AuditLogSeverity.ERROR), + //Affinity Rules Enforcement Manager + AFFINITY_RULES_ENFORCEMENT_MANAGER_START(10753, AuditLogSeverity.NORMAL), + AFFINITY_RULES_ENFORCEMENT_MANAGER_INTERVAL_REACHED(10754, AuditLogSeverity.NORMAL), + // Host Devices VM_ADD_HOST_DEVICES(10800), VM_REMOVE_HOST_DEVICES(10801), diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java index 0f15d4f..2bedeed 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/action/VdcActionType.java @@ -467,7 +467,9 @@ // Host Devices RefreshHostDevices(4000, ActionGroup.MANIPULATE_HOST, false, QuotaDependency.NONE), - RefreshHost(4001, ActionGroup.MANIPULATE_HOST, false, QuotaDependency.NONE); + RefreshHost(4001, ActionGroup.MANIPULATE_HOST, false, QuotaDependency.NONE), + + AffinityRulesEnforcementManagerStart(10753, ActionGroup.MANIPULATE_AFFINITY_GROUPS, false, QuotaDependency.NONE); private int intValue; private ActionGroup actionGroup; diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java index e49739a..638ecba 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java @@ -460,6 +460,21 @@ @TypeConverterAttribute(Integer.class) @DefaultValueAttribute("1") VdsLoadBalancingIntervalInMinutes, + + //AffinityRulesEnforcementManager + @TypeConverterAttribute(Integer.class) + @DefaultValueAttribute("1") + AffinityRulesEnforcementManagerRegularInterval, + @TypeConverterAttribute(Integer.class) + @DefaultValueAttribute("15") + AffinityRulesEnforcementManagerLongInterval, + @TypeConverterAttribute(Integer.class) + @DefaultValueAttribute("1") + AffinityRulesEnforcementManagerInitialDelay, + @TypeConverterAttribute(Integer.class) + @DefaultValueAttribute("5") + AffinityRulesEnforcementManagerMaximumMigrationTries, + @TypeConverterAttribute(Integer.class) @DefaultValueAttribute("5") VdsHaReservationIntervalInMinutes, 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 ce4dd67..fa39d57 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 @@ -1213,6 +1213,7 @@ ACTION_TYPE_FAILED_POSITIVE_AFFINITY_GROUP(ErrorType.CONFLICT), ACTION_TYPE_FAILED_NEGATIVE_AFFINITY_GROUP(ErrorType.CONFLICT), ACTION_TYPE_FAILED_MIX_POSITIVE_NEGATIVE_AFFINITY_GROUP(ErrorType.CONFLICT), + ACTION_TYPE_FAILED_AFFINITY_RULES_COLLISION(ErrorType.CONFLICT), // Iscsi bonds ISCSI_BOND_WITH_SAME_NAME_EXIST_IN_DATA_CENTER(ErrorType.CONFLICT), 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 0910c52..f09f9ea 100644 --- a/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties +++ b/backend/manager/modules/dal/src/main/resources/bundles/AuditLogMessages.properties @@ -1044,3 +1044,6 @@ IRS_BROKER_COMMAND_FAILURE=VDSM command failed: ${message} SYSTEM_CHANGE_STORAGE_POOL_STATUS_UP_REPORTING_HOSTS=Data Center ${StoragePoolName} status was changed to UP as some of its hosts are in status UP. SYSTEM_CHANGE_STORAGE_POOL_STATUS_NON_RESPONSIVE_NO_REPORTING_HOSTS=Data Center ${StoragePoolName} status was changed to Non Responsive as none of its hosts are in status UP. +AFFINITY_RULES_ENFORCEMENT_MANAGER_START=Affinity Rules Enforcement Manager started. +AFFINITY_RULES_ENFORCEMENT_MANAGER_INTERVAL_REACHED=Affinity Rules Enforcement Manager interval reached. + 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 ebcb763..40c4d55 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 @@ -3725,6 +3725,9 @@ @DefaultStringValue("VM is associated with both positive and negative Affinity Groups, please reconfigure VM's affinity groups") String ACTION_TYPE_FAILED_MIX_POSITIVE_NEGATIVE_AFFINITY_GROUP(); + @DefaultStringValue("Affinity Group contradiction detected between UAG:\n${UAG}\nand negative Affinity Group:\n${negativeAR}") + String ACTION_TYPE_FAILED_AFFINITY_RULES_COLLISION(); + @DefaultStringValue("iSCSI bond name must not exceed 50 characters") String VALIDATION_ISCSI_BOND_NAME_MAX(); diff --git a/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties b/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties index 7da8ca9..a07b834 100644 --- a/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties +++ b/frontend/webadmin/modules/userportal-gwtp/src/main/resources/org/ovirt/engine/ui/frontend/AppErrors.properties @@ -1059,6 +1059,7 @@ ACTION_TYPE_FAILED_POSITIVE_AFFINITY_GROUP=VM is associated with a positive Affinity Group (${affinityGroupName}) and require to run on the same Host (${hostName}) as the other group VMs ACTION_TYPE_FAILED_NEGATIVE_AFFINITY_GROUP=VM is associated with a negative Affinity Group and require to run on separate Host, which doesn't run other group VMs ACTION_TYPE_FAILED_MIX_POSITIVE_NEGATIVE_AFFINITY_GROUP=VM is associated with both positive and negative Affinity Groups, please contact your system administrator +ACTION_TYPE_FAILED_AFFINITY_RULES_COLLISION=Affinity Group contradiction detected between UAG:\n${UAG}\nand negative Affinity Group:\n${negativeAR} # Iscsi Bond VALIDATION_ISCSI_BOND_NAME_MAX=iSCSI bond name must not exceed 50 characters 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 7f5b551..bed2fdc 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 @@ -1333,6 +1333,7 @@ ACTION_TYPE_FAILED_POSITIVE_AFFINITY_GROUP=VM is associated with a positive Affinity Group (${affinityGroupName}) and require to run on the same Host (${hostName}) as the other group VMs ACTION_TYPE_FAILED_NEGATIVE_AFFINITY_GROUP=VM is associated with a negative Affinity Group and require to run on separte Host, which doesn't run the other group VMs ACTION_TYPE_FAILED_MIX_POSITIVE_NEGATIVE_AFFINITY_GROUP=VM is associated with both positive and negative Affinity Groups, please reconfigure VM's affinity groups +ACTION_TYPE_FAILED_AFFINITY_RULES_COLLISION=Affinity Group contradiction detected between UAG:\n${UAG}\nand negative Affinity Group:\n${negativeAR} # Iscsi Bond VALIDATION_ISCSI_BOND_NAME_MAX=iSCSI bond name must not exceed 50 characters -- To view, visit https://gerrit.ovirt.org/41092 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I674666a4e84ca0f4bb3ed7eb96654f2362020d4d Gerrit-PatchSet: 7 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Tomer Saban <tsa...@redhat.com> Gerrit-Reviewer: Dudi Maroshi <d...@redhat.com> Gerrit-Reviewer: Gilad Chaplik <gchap...@redhat.com> Gerrit-Reviewer: Jenkins CI Gerrit-Reviewer: Martin Sivák <msi...@redhat.com> Gerrit-Reviewer: Roy Golan <rgo...@redhat.com> Gerrit-Reviewer: Tomer Saban <tsa...@redhat.com> Gerrit-Reviewer: automat...@ovirt.org _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches