/*
* 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 gipad.placementconstraint;
import gipad.configuration.configuration.Configuration;
import gipad.configuration.configuration.Node;
import gipad.configuration.configuration.VirtualMachine;
import gipad.plan.choco.ReconfigurationProblem;
import gipad.plan.choco.actionmodel.ActionModel;
import gipad.plan.choco.actionmodel.slice.DemandingSlice;
import gipad.plan.choco.actionmodel.slice.Slice;
import gipad.tools.ManagedElementList;
import gipad.tools.SimpleManagedElementList;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import solver.constraints.set.SCF;
import solver.exception.ContradictionException;
import solver.variables.IntVar;
import solver.variables.SetVar;
import solver.variables.VF;
/**
* 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 ManagedElementList<VirtualMachine> vms;
public Lonely(ManagedElementList<VirtualMachine> vms) {
this.vms = vms;
}
@Override
public void inject(ReconfigurationProblem core) {
//Remove non future-running VMs
ManagedElementList<VirtualMachine> goods = vms.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 = VF.set("nodes4(" + vms + ")", 0, core.getNodes().length - 1, core.getSolver());//core.createEnumSetVar("nodes4(" + vms + ")", 0, core.getNodes().length - 1);
SetVar otherNodes = VF.set("nodes!4(" + vms + ")", 0, core.getNodes().length - 1, core.getSolver());// core.createEnumSetVar("nodes!4(" + vms + ")", 0, core.getNodes().length - 1);
ManagedElementList<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 = extractDemandingSlices(core.getAssociatedActions(goods));
IntVar<?>[] myAssigns = extractHosters(myDSlices.toArray(new Slice[myDSlices.size()]));
List<DemandingSlice> otherDSlices = extractDemandingSlices(core.getAssociatedActions(otherVMs));
IntVar<?>[] otherAssigns = extractHosters(otherDSlices.toArray(new Slice[otherDSlices.size()]));
for (IntVar<?> v : otherAssigns) {
if (v.instantiated()) {
try {
otherNodes.addToKernel(v.getValue(), null);
} catch (ContradictionException e) {
//FIXME Plan.logger.error(e.getMessage());
}
} else {
core.getSolver().post(SCF.member(v, otherNodes));
}
}
for (IntVar<?> v : myAssigns) {
if (v.instantiated()) {
try {
myNodes.addToKernel(v.getValue(), null);
} catch (ContradictionException e) {
//FIXME debug log Plan.logger.error(e.getMessage());
}
} else {
core.getSolver().post(SCF.member(v,myNodes));
}
}
core.getSolver().post(SCF.disjoint(myNodes, otherNodes));
// core.post(new Disjoint(core.getEnvironment(), myAssigns, otherAssigns, core.getNodes().length + 1));
}
/**
* Extract all the demanding slices of a list of actions.
*
* @param actions the list of action
* @return a list of demanding slice. May be empty
*/
public static List<DemandingSlice> extractDemandingSlices(List<? extends ActionModel> actions) {
List<DemandingSlice> slices = new ArrayList<DemandingSlice>();
for (ActionModel a : actions) {
if (a.getDemandingSlice() != null) {
slices.add(a.getDemandingSlice());
}
}
return slices;
}
/**
* Extract all the hosters of an array of slices.
*
* @param slices the slices to consider
* @return an array of assignement var, in an order similar to slices
*/
public static IntVar<?>[] extractHosters(Slice[] slices) {
IntVar<?>[] l = new IntVar[slices.length];
for (int i = 0; i < slices.length; i++) {
l[i] = slices[i].hoster();
}
return l;
}
@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 ManagedElementList<VirtualMachine> getAllVirtualMachines() {
return (ManagedElementList<VirtualMachine>) vms;
}
@Override
public ManagedElementList<Node> getNodes() {
return new SimpleManagedElementList<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 ManagedElementList<VirtualMachine> getMisPlaced(Configuration cfg) {
ManagedElementList<VirtualMachine> bad = new SimpleManagedElementList<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();
}
}