/*
* 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.plan.choco.actionModel;
import choco.cp.solver.constraints.reified.FastIFFEq;
import choco.cp.solver.constraints.reified.FastImpliesEq;
import choco.cp.solver.variables.integer.BoolVarNot;
import entropy.configuration.Configuration;
import entropy.configuration.VirtualMachine;
import entropy.plan.action.Migration;
import entropy.plan.choco.ReconfigurationProblem;
import entropy.plan.choco.actionModel.slice.ConsumingSlice;
import entropy.plan.choco.actionModel.slice.DemandingSlice;
/**
* Model a action that may potentially migrate a VM.
* The action is modeled with one consuming slice and one demanding slice.
* If the demanding slice is hosted on a different node than the consuming slice
* it will result in a migration. In this case, the action starts at the beginning of
* the demanding slice and ends at the end of the consuming slice. Otherwise,
* the VM will stay on the node.
*
* @author Fabien Hermenier
*/
public class MigratableActionModel extends VirtualMachineActionModel {
/**
* The global cost of the action.
*/
private IntVar cost;
/**
* Make a new action.
*
* @param model the model
* @param vm the virtual machine to make moveable
* @param d the duration of the migration if it is performed
* @param moveable {@code true} to indicates the VM can be migrated
*/
/* public MigratableActionModel(ReconfigurationProblem model, VirtualMachine vm, int d, boolean moveable) {
super(vm);
cSlice = new ConsumingSlice(model, "migS(" + vm.getName() + ")", model.getSourceConfiguration().getLocation(vm), vm.getCPUConsumption(), vm.getMemoryConsumption());
dSlice = null;
if (moveable) {
dSlice = new DemandingSlice(model, "migD(" + vm.getName() + ")", vm.getCPUDemand(), vm.getMemoryDemand());
duration = model.createEnumIntVar("d(migrate(" + getVirtualMachine().getName() + "))", new int[]{0, d});
this.cost = model.createBoundIntVar("k(migrate(" + getVirtualMachine().getName() + "))", 0, ReconfigurationProblem.MAX_TIME);
} else {
dSlice = new DemandingSlice(model, "migD(" + vm.getName() + ")", model.getNode(model.getSourceConfiguration().getLocation(vm)), vm.getCPUDemand(), vm.getMemoryDemand());
duration = model.createIntegerConstant("d(migrate(" + getVirtualMachine().getName() + "))", 0);
this.cost = model.createIntegerConstant("c(migrate(" + getVirtualMachine().getName() + "))", 0);
}
if (moveable) {
BooleanVarImpl move = (BooleanVarImpl) model.createBooleanVar("mv(" + getVirtualMachine().getName() + ")");
model.post(ReifiedFactory.builder(move, model.neq(cSlice.hoster(), dSlice.hoster()), model));
BooleanVarImpl stay = (BooleanVarImpl) model.createBooleanVar("rt(" + getVirtualMachine().getName() + ")");
model.post(model.neq(move, stay));
//IntDomainVar stay = ((DefaultReconfigurationProblem)model).createNotBooleanVar("", move);
model.post(new TimesXYZ(move, cSlice.end(), cost));
model.post(model.ifOnlyIf(stay, model.eq(duration, 0)));
//model.post(new FastIFFEq(stay, duration, 0));
if (dSlice.getCPUheight() <= cSlice.getCPUheight()) {
//model.post(new FastImpliesEq(stay, cSlice.duration(), 0));
model.post(model.implies(stay, model.eq(cSlice.duration(), 0)));
}
if (dSlice.getCPUheight() > cSlice.getCPUheight()) {
//model.post(new FastImpliesEq(stay, dSlice.duration(), 0));
model.post(model.implies(stay, model.eq(dSlice.duration(), 0)));
}
model.post(model.leq(duration, cSlice.duration()));
model.post(model.leq(duration, dSlice.duration()));
model.post(model.eq(this.end(), model.plus(this.start(), duration)));
//model.post(model.eq(this.end(), new IntDomainVarAddCste(model,"", this.start(), duration));
} else {
if (dSlice.getCPUheight() <= cSlice.getCPUheight()) {
cSlice.fixDuration(0);
}
if (dSlice.getCPUheight() > cSlice.getCPUheight()) {
dSlice.fixDuration(0);
}
model.post(model.eq(this.end(), this.start()));
}
cSlice.addToModel(model);
dSlice.addToModel(model);
} */
public MigratableActionModel(ReconfigurationProblem model, VirtualMachine vm, int d, boolean moveable) {
super(vm);
assert d > 0 : "The cost of migration for " + vm + " equals 0 !";
//
// cSlice: default: 0 + var(duration) = var(end)
// dSlice: default: var(start) + var(duration) = var(end)
//
// !moveable:
// si cpu increase: dSlice: var(start) + 0 = var(end) -> Slice(end, 0, end) -> pas de contraintes
// else cSlice: 0 + 0 = var(end) -> Slice(0,0,0), pas de contraintes
//
// moveable: cSlice -> Slice(0, var(end), var(end)), pas de plus
//
//
if (moveable) {
this.cost = model.createBoundIntVar("k(migrate(" + getVirtualMachine().getName() + "))", 0, ReconfigurationProblem.MAX_TIME);
duration = model.createEnumIntVar("d(migrate(" + getVirtualMachine().getName() + "))", new int[]{0, d});
cSlice = new ConsumingSlice(model, "migS(" + vm.getName() + ")", model.getSourceConfiguration().getLocation(vm), vm.getCPUConsumption(), vm.getMemoryConsumption());
dSlice = new DemandingSlice(model, "migD(" + vm.getName() + ")", vm.getCPUDemand(), vm.getMemoryDemand());
IntDomainVar move = model.createBooleanVar("mv(" + getVirtualMachine().getName() + ")");
model.post(ReifiedFactory.builder(move, model.neq(cSlice.hoster(), dSlice.hoster()), model));
IntDomainVar stay = new BoolVarNot(model, "", move);//(DefaultReconfigurationProblem) model).createNotBooleanVar("", move);
model.post(new TimesXYZ(move, cSlice.end(), cost));
model.post(new FastIFFEq(stay, duration, 0));
if (dSlice.getCPUheight() <= cSlice.getCPUheight()) {
model.post(new FastImpliesEq(stay, cSlice.duration(), 0));
}
if (dSlice.getCPUheight() > cSlice.getCPUheight()) {
model.post(new FastImpliesEq(stay, dSlice.duration(), 0));
}
model.post(model.eq(dSlice.end(), model.plus(dSlice.start(), dSlice.duration())));
model.post(model.eq(cSlice.end(), model.plus(cSlice.start(), cSlice.duration())));
model.post(model.leq(duration, cSlice.duration()));
model.post(model.leq(duration, dSlice.duration()));
model.post(model.eq(this.end(), model.plus(this.start(), duration)));
} else {
boolean neadIncrease = vm.getCPUConsumption() <= vm.getCPUDemand();
this.cost = model.createIntegerConstant("c(migrate(" + getVirtualMachine().getName() + "))", 0);
if (neadIncrease) {
cSlice = new ConsumingSlice("",
model.createIntegerConstant("", model.getNode(model.getSourceConfiguration().getLocation(vm))),
model.createTaskVar("", model.getStart(), model.getEnd(), model.getEnd()),
vm.getCPUConsumption(),
vm.getMemoryConsumption()
);
dSlice = new DemandingSlice("migD(" + vm.getName() + ")",
model.createIntegerConstant("", model.getNode(model.getSourceConfiguration().getLocation(vm))),
model.createTaskVar("", model.getEnd(), model.getEnd(), model.createIntegerConstant("", 0)),
vm.getCPUDemand(),
vm.getMemoryDemand()
);
} else {
cSlice = new ConsumingSlice("",
model.createIntegerConstant("", model.getNode(model.getSourceConfiguration().getLocation(vm))),
model.createTaskVar("", model.getStart(), model.getStart(), model.getStart()),
vm.getCPUConsumption(),
vm.getMemoryConsumption()
);
dSlice = new DemandingSlice("",
model.createIntegerConstant("", model.getNode(model.getSourceConfiguration().getLocation(vm))),
model.createTaskVar("", model.getStart(), model.getEnd(), model.getEnd()),
vm.getCPUDemand(),
vm.getMemoryDemand()
);
}
model.post(model.eq(this.end(), this.start()));
}
model.post(model.leq(cSlice.duration(), model.getEnd()));
model.post(model.leq(dSlice.duration(), model.getEnd()));
}
/**
* Get the moment the action ends. The action ends at the moment
* the slice on the source node ends.
*
* @return <code>getConsumingSlice().end()</code>
*/
@Override
public final IntVar end() {
return this.getConsumingSlice().end();
}
/**
* Get the moment the action starts. The action starts at the moment
* the slice on the source node starts.
*
* @return <code>getDemandingSlice().start()</code>
*/
@Override
public final IntVar start() {
return this.getDemandingSlice().start();
}
/**
* Return the migration action if the VM have to move.
*
* @return a Migration if the source node and the destination node are different. null otherwise
*/
@Override
public Migration getDefinedAction(ReconfigurationProblem solver) {
if (getConsumingSlice().hoster().getVal()
!= getDemandingSlice().hoster().getVal()) {
return new Migration(getVirtualMachine(),
solver.getNode(getConsumingSlice().hoster().getVal()),
solver.getNode(getDemandingSlice().hoster().getVal()),
start().getVal(),
end().getVal());
}
return null;
}
@Override
public boolean putResult(ReconfigurationProblem solver, Configuration cfg) {
cfg.addOnline(solver.getNode(getDemandingSlice().hoster().getVal()));
return cfg.setRunOn(getVirtualMachine(), solver.getNode(getDemandingSlice().hoster().getVal()));
}
@Override
public String toString() {
return "migration(" + getVirtualMachine().getName() + ")";
}
@Override
public IntVar getGlobalCost() {
return this.cost;
}
}