/*
* Copyright (c) Fabien Hermenier
*
* This file is part of Entropy.
*
* Entropy is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Entropy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Entropy. If not, see <http://www.gnu.org/licenses/>.
*/
package entropy.vjob;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import choco.cp.solver.constraints.set.MemberXY;
import choco.kernel.solver.ContradictionException;
import choco.kernel.solver.variables.integer.IntDomainVar;
import choco.kernel.solver.variables.set.SetVar;
import entropy.configuration.Configuration;
import entropy.configuration.ManagedElementSet;
import entropy.configuration.Node;
import entropy.configuration.VirtualMachine;
import entropy.plan.Plan;
import entropy.plan.choco.ReconfigurationProblem;
import entropy.plan.choco.actionModel.ActionModels;
import entropy.plan.choco.actionModel.slice.DemandingSlice;
import entropy.plan.choco.actionModel.slice.Slice;
import entropy.plan.choco.actionModel.slice.Slices;
/**
* A placement constraint to ensure the given set of VMs will not be hosted
* on nodes that host other VMs
*
* @author Fabien Hermenier
*/
public class Lonely implements PlacementConstraint {
private VJobSet<VirtualMachine> vms;
public Lonely(VJobSet<VirtualMachine> vms) {
this.vms = vms;
}
@Override
public void inject(ReconfigurationProblem core) {
//Remove non future-running VMs
ManagedElementSet<VirtualMachine> goods = vms.getElements().clone();
goods.retainAll(core.getFutureRunnings());
//Two set variables. One denotes the nodes hosting the VMs, the other, the nodes hosting the other VMs.
SetVar myNodes = core.createEnumSetVar("nodes4(" + vms + ")", 0, core.getNodes().length - 1);
SetVar otherNodes = core.createEnumSetVar("nodes!4(" + vms + ")", 0, core.getNodes().length - 1);
ManagedElementSet<VirtualMachine> otherVMs = core.getFutureRunnings().clone();
otherVMs.removeAll(goods);
//Link the assignment variables with the set
//TODO: propose a MemberXY() constraints that takes an array of integer variables to improve performance
List<DemandingSlice> myDSlices = ActionModels.extractDemandingSlices(core.getAssociatedActions(goods));
IntDomainVar[] myAssigns = Slices.extractHosters(myDSlices.toArray(new Slice[myDSlices.size()]));
List<DemandingSlice> otherDSlices = ActionModels.extractDemandingSlices(core.getAssociatedActions(otherVMs));
IntDomainVar[] otherAssigns = Slices.extractHosters(otherDSlices.toArray(new Slice[otherDSlices.size()]));
for (IntDomainVar v : otherAssigns) {
if (v.isInstantiated()) {
try {
otherNodes.addToKernel(v.getVal(), null, false);
} catch (ContradictionException e) {
Plan.logger.error(e.getMessage());
}
} else {
core.post(new MemberXY(otherNodes, v));
}
}
for (IntDomainVar v : myAssigns) {
if (v.isInstantiated()) {
try {
myNodes.addToKernel(v.getVal(), null, false);
} catch (ContradictionException e) {
Plan.logger.error(e.getMessage());
}
} else {
core.post(new MemberXY(myNodes, v));
}
}
core.post(new choco.cp.solver.constraints.set.Disjoint(myNodes, otherNodes));
// core.post(new Disjoint(core.getEnvironment(), myAssigns, otherAssigns, core.getNodes().length + 1));
}
@Override
public boolean isSatisfied(Configuration cfg) {
Set<Node> s1 = new HashSet<Node>();
for (VirtualMachine vm : vms) {
if (cfg.isRunning(vm)) {
s1.add(cfg.getLocation(vm));
}
}
//If one of the other VMs is running into the nodes, then fail
for (VirtualMachine vm : cfg.getRunnings()) {
if (!vms.contains(vm) && s1.contains(cfg.getLocation(vm))) {
return false;
}
}
return true;
}
@Override
public ExplodedSet<VirtualMachine> getAllVirtualMachines() {
return vms.flatten();
}
@Override
public ExplodedSet<Node> getNodes() {
return new ExplodedSet<Node>();
}
/**
* If the constraint is not satisfied, then misplaced VMs are those given
* as a parameter that share nodes with other VMs.
*
* @param cfg the configuration
* @return a set of virtual machines that may be empty
*/
@Override
public ExplodedSet<VirtualMachine> getMisPlaced(Configuration cfg) {
ExplodedSet<VirtualMachine> bad = new ExplodedSet<VirtualMachine>();
Set<Node> s1 = new HashSet<Node>();
for (VirtualMachine vm : vms) {
if (cfg.isRunning(vm)) {
s1.add(cfg.getLocation(vm));
}
}
//If one of the other VMs is running into the nodes, then fail
for (VirtualMachine vm : cfg.getRunnings()) {
if (!vms.contains(vm)) {
Node n = cfg.getLocation(vm);
if (s1.contains(n)) { //Other VMs share reserved nodes
for (VirtualMachine vm2 : cfg.getRunnings(n)) {
if (vms.contains(vm2)) {
bad.add(vm2);
}
}
}
}
}
return bad;
}
@Override
public String toString() {
return new StringBuilder("lonely(").append(vms).append(")").toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Lonely lonely = (Lonely) o;
return lonely.equals(vms);
}
@Override
public int hashCode() {
return vms.hashCode();
}
}