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

Reply via email to