/* * 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; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import solver.search.measure.IMeasures; import solver.search.solution.Solution; import solver.variables.SetVar; import entropy.configuration.Configuration; import entropy.configuration.Configurations; import entropy.configuration.ManagedElementSet; import entropy.configuration.Node; import entropy.configuration.SimpleManagedElementSet; import entropy.configuration.VirtualMachine; import entropy.plan.DefaultTimedReconfigurationPlan; import entropy.plan.MultipleResultingStateException; import entropy.plan.NoAvailableTransitionException; import entropy.plan.NonViableSourceConfigurationException; import entropy.plan.Plan; import entropy.plan.PlanException; import entropy.plan.SolutionStatistics; import entropy.plan.SolvingStatistics; import entropy.plan.TimedReconfigurationPlan; import entropy.plan.UnknownResultingStateException; import entropy.plan.action.Action; import entropy.plan.choco.actionModel.ActionModels; import entropy.plan.choco.actionModel.BootNodeActionModel; import entropy.plan.choco.actionModel.MigratableActionModel; import entropy.plan.choco.actionModel.NodeActionModel; import entropy.plan.choco.actionModel.ResumeActionModel; import entropy.plan.choco.actionModel.RunActionModel; import entropy.plan.choco.actionModel.ShutdownNodeActionModel; import entropy.plan.choco.actionModel.StayOfflineNodeActionModel; import entropy.plan.choco.actionModel.StopActionModel; import entropy.plan.choco.actionModel.SuspendActionModel; import entropy.plan.choco.actionModel.VirtualMachineActionModel; import entropy.plan.choco.actionModel.slice.ConsumingSlice; import entropy.plan.choco.actionModel.slice.DemandingSlice; import entropy.plan.choco.actionModel.slice.Slice; import entropy.plan.choco.actionModel.slice.Slices; import entropy.plan.choco.constraint.pack.SatisfyDemandingSliceHeights; import entropy.plan.choco.constraint.pack.SatisfyDemandingSlicesHeightsFastBP; import entropy.plan.choco.constraint.sliceScheduling.SlicesPlanner; import entropy.plan.durationEvaluator.DurationEvaluationException; import entropy.plan.durationEvaluator.DurationEvaluator; import gnu.trove.map.hash.TIntIntHashMap; /** * A CSP to model a reconfiguration plan composed of time bounded actions. * In this model, regarding to the current configuration and the sample destination configuration, * the model create the different actions that aims to perform the transition to the destination configuration. In addition, * several actions acting on the placement of the virtual machines can be added. * * @author Fabien Hermenier */ public final class DefaultReconfigurationProblem extends CPSolver implements ReconfigurationProblem { private ManagedElementSet<VirtualMachine> manageable; /** * The maximum number of group of nodes. */ public static final Integer MAX_NB_GRP = 1000; /** * The moment the reconfiguration starts. Equals to 0. */ private IntDomainVar start; /** * The moment the reconfiguration ends. Variable. */ private IntDomainVar end; /** * All the virtual machines' action to perform that implies regular actions. */ private List<VirtualMachineActionModel> vmActions; /** * All the actions of the nodes that manage their state. */ private List<NodeActionModel> nodesActions; /** * The source configuration. */ private Configuration source; /** * The future running VMs. */ private ManagedElementSet<VirtualMachine> runnings; /** * The future waiting VMs. */ private ManagedElementSet<VirtualMachine> waitings; /** * The future sleeping VMs. */ private ManagedElementSet<VirtualMachine> sleepings; /** * The future terminated VMs. */ private ManagedElementSet<VirtualMachine> terminated; /** * The future online nodes. */ private ManagedElementSet<Node> onlines; /** * The future offline nodes. */ private ManagedElementSet<Node> offlines; /** * All the nodes managed by the model. */ private Node[] nodes; private TIntIntHashMap revNodes; /** * A set model for each node. */ private SetVar[] sets; /** * Cpu usage indexed by the index of the node. */ private IntDomainVar[] cpuCapacities; /** * Mem usage indexed by the index of the node. */ private IntDomainVar[] memCapacities; /** * All the virtual machines managed by the model. */ private VirtualMachine[] vms; private int[] currentLocation; private TIntIntHashMap revVMs; /** * The duration evaluator. */ private DurationEvaluator durationEval; /** * The group variable associated to each virtual machine. */ private List<IntDomainVar> vmGrp; /** * The group variable associated to each group of VMs. */ private Map<ManagedElementSet<VirtualMachine>, IntDomainVar> vmsGrp; /** * The value associated to each group of nodes. */ private Map<ManagedElementSet<Node>, Integer> nodesGrp; /** * The groups associated to each node. */ private List<List<Integer>> nodeGrps; /** * The group of nodes associated to each identifier. To synchronize with nodesGrp. */ private List<ManagedElementSet<Node>> revNodesGrp; /** * The next value to use when creating a nodeGrp. */ private int nextNodeGroupVal = 0; /** * All the consuming slices in the model. */ private List<ConsumingSlice> consumingSlices; /** * All the demanding slices in the model. */ private List<DemandingSlice> demandingSlices; //private SatisfyDemandingSlicesHeightsCustomBP packing; private SatisfyDemandingSliceHeights packing; private int[] grpId; //The group ID of each node /** * Build a reconfiguration problem. All the VMs are candidate * for management * * @param src the source configuration * @param run the virtual machines to run * @param wait the virtual machines that will stay waiting * @param sleep the virtual machines to turn into the sleeping state * @param stop the virtual machines to stop * @param on the nodes to turn on * @param off the nodes to turn off * @param eval the duration evaluator * @throws PlanException if an error occurs */ public DefaultReconfigurationProblem(Configuration src, ManagedElementSet<VirtualMachine> run, ManagedElementSet<VirtualMachine> wait, ManagedElementSet<VirtualMachine> sleep, ManagedElementSet<VirtualMachine> stop, ManagedElementSet<Node> on, ManagedElementSet<Node> off, DurationEvaluator eval) throws PlanException { this(src, run, wait, sleep, stop, src.getAllVirtualMachines(), on, off, eval); } /** * Make a new model. * * @param src The source configuration. It must be viable. * @param run The set of virtual machines that must be running at the end of the process * @param wait The set of virtual machines that must be waiting at the end of the process * @param sleep The set of virtual machines that must be sleeping at the end of the process * @param stop The set of virtual machines that must be terminated at the end of the process * @param manageable the set of virtual machines to consider as manageable in the problem * @param on The set of nodes that must be online at the end of the process * @param off The set of nodes that must be offline at the end of the process * @param eval the evaluator to estimate the duration of an action. * @throws entropy.plan.PlanException if an error occurred while building the model */ public DefaultReconfigurationProblem(Configuration src, ManagedElementSet<VirtualMachine> run, ManagedElementSet<VirtualMachine> wait, ManagedElementSet<VirtualMachine> sleep, ManagedElementSet<VirtualMachine> stop, ManagedElementSet<VirtualMachine> manageable, ManagedElementSet<Node> on, ManagedElementSet<Node> off, DurationEvaluator eval) throws PlanException { this.source = src; this.manageable = manageable; runnings = run; waitings = wait; sleepings = sleep; terminated = stop; onlines = on; offlines = off; this.durationEval = eval; this.checkDisjointSet(); if (Configurations.currentlyOverloadedNodes(this.source).size() > 0) { throw new NonViableSourceConfigurationException(source, Configurations.currentlyOverloadedNodes(source).get(0)); } start = this.makeConstantIntVar(0); end = createBoundIntVar("end", 0, MAX_TIME); post(geq(end, start)); this.vms = source.getAllVirtualMachines().toArray(new VirtualMachine[source.getAllVirtualMachines().size()]); this.revVMs = new TIntIntHashMap(vms.length); for (int i = 0; i < vms.length; i++) { revVMs.put(vms[i].hashCode(), i); } ManagedElementSet<Node> ns = source.getAllNodes(); this.nodes = ns.toArray(new Node[ns.size()]); this.grpId = new int[ns.size()]; this.revNodes = new TIntIntHashMap(ns.size()); for (int i = 0; i < nodes.length; i++) { revNodes.put(nodes[i].hashCode(), i); } try { this.makeBasicActions(); } catch (DurationEvaluationException e) { throw new PlanException(e.getMessage(), e); } this.makeResourcesCapacities(); this.vmGrp = new ArrayList<IntDomainVar>(this.vms.length); for (int i = 0; i < vms.length; i++) { this.vmGrp.add(i, null); } this.vmsGrp = new HashMap<ManagedElementSet<VirtualMachine>, IntDomainVar>(); this.nodeGrps = new ArrayList<List<Integer>>(this.nodes.length); for (int i = 0; i < this.nodes.length; i++) { this.nodeGrps.add(i, new LinkedList<Integer>()); } this.nodesGrp = new HashMap<ManagedElementSet<Node>, Integer>(); this.revNodesGrp = new ArrayList<ManagedElementSet<Node>>(MAX_NB_GRP); packing = new SatisfyDemandingSlicesHeightsFastBP();//new SatisfyDemandingSlicesHeightsSimpleBP(); packing.add(this); //TODO: Uncomment for capacity /*if (!this.demandingSlices.isEmpty()) { this.makeSetModel(); } */ new SlicesPlanner().add(this); } /** * Make a set model. On set per node, that indicates the VMs it will run */ private void makeSetModel() { if (this.sets == null) { //A set variable for each future online nodes this.sets = new SetVar[nodes.length]; for (int i = 0; i < sets.length; i++) { Node n = nodes[i]; SetVar s = createEnumSetVar("host(" + n.getName() + ")", 0, demandingSlices.size() - 1); sets[i] = s; } //Make the channeling with the assignment variable of all the d-slices IntDomainVar[] assigns = Slices.extractHosters(demandingSlices.toArray(new Slice[demandingSlices.size()])); post(new InverseSetInt(assigns, sets)); } } @Override public SatisfyDemandingSliceHeights getSatisfyDSlicesHeightConstraint() { return this.packing; } /** * Set the resources capacity of the nodes. */ private void makeResourcesCapacities() { this.cpuCapacities = new IntDomainVar[nodes.length]; this.memCapacities = new IntDomainVar[nodes.length]; ManagedElementSet<Node> involvedNodes = new SimpleManagedElementSet<Node>(); for (Node n : getFutureOfflines()) { NodeActionModel action = getAssociatedAction(n); if (action != null) { involvedNodes.add(n); } } involvedNodes.addAll(getFutureOnlines()); for (Node n : involvedNodes) { IntDomainVar capaCPU = createBoundIntVar(n.getName() + "#cpuCapacity", 0, n.getCPUCapacity()); IntDomainVar capaMem = createBoundIntVar(n.getName() + "#memCapacity", 0, n.getMemoryCapacity()); cpuCapacities[getNode(n)] = capaCPU; memCapacities[getNode(n)] = capaMem; } } /** * Check all the nodes belong to only on set. * * @throws entropy.plan.UnknownResultingStateException * if the state of an element is not defined * @throws entropy.plan.MultipleResultingStateException * if an element has two state */ private void checkDisjointSet() throws UnknownResultingStateException, MultipleResultingStateException { for (Node n : getSourceConfiguration().getAllNodes()) { boolean inOnlines = this.getFutureOnlines().contains(n); boolean inOfflines = this.getFutureOfflines().contains(n); if (inOnlines && inOfflines) { throw new MultipleResultingStateException(n, getFutureOnlines(), getFutureOfflines()); } else if (!inOnlines && !inOfflines) { throw new UnknownResultingStateException(n); } } for (VirtualMachine vm : getSourceConfiguration().getAllVirtualMachines()) { int nbIn = this.getFutureRunnings().contains(vm) ? 1 : 0; if (this.getFutureWaitings().contains(vm) || this.getFutureSleepings().contains(vm) || this.getFutureTerminated().contains(vm)) { nbIn++; } if (nbIn == 0) { throw new UnknownResultingStateException(vm); } else if (nbIn > 1) { throw new MultipleResultingStateException(vm, getFutureRunnings(), getFutureSleepings(), getFutureWaitings(), getFutureTerminated()); } } } @Override public Node[] getNodes() { return nodes; } @Override public VirtualMachine[] getVirtualMachines() { return vms; } @Override public Configuration getSourceConfiguration() { return this.source; } @Override public ManagedElementSet<VirtualMachine> getFutureRunnings() { return this.runnings; } @Override public ManagedElementSet<VirtualMachine> getFutureWaitings() { return this.waitings; } @Override public ManagedElementSet<VirtualMachine> getFutureSleepings() { return this.sleepings; } @Override public ManagedElementSet<VirtualMachine> getFutureTerminated() { return this.terminated; } @Override public ManagedElementSet<Node> getFutureOnlines() { return this.onlines; } @Override public ManagedElementSet<Node> getFutureOfflines() { return this.offlines; } @Override public IntDomainVar getStart() { return this.start; } @Override public IntDomainVar getEnd() { return this.end; } @Override public int getVirtualMachine(VirtualMachine vm) { int h = vm.hashCode(); if (!revVMs.containsKey(h)) { return -1; } return revVMs.get(h); } @Override public VirtualMachine getVirtualMachine(int idx) { if (idx < vms.length && idx >= 0) { return vms[idx]; } return null; } @Override public int getNode(Node n) { int h = n.hashCode(); if (!revNodes.containsKey(h)) { return -1; } return revNodes.get(h); } @Override public Node getNode(int idx) { if (idx < nodes.length && idx >= 0) { return nodes[idx]; } return null; } /** * Create all the basic action that manipulate the state of the virtual machine and the nodes. * * @throws entropy.plan.NoAvailableTransitionException * if the VM can not be running regarding to its current state */ private void makeBasicActions() throws DurationEvaluationException, NoAvailableTransitionException { //make the actions for the VMs this.vmActions = new ArrayList<VirtualMachineActionModel>(vms.length); for (int i = 0; i < vms.length; i++) { this.vmActions.add(i, null); } //for (VirtualMachine vm : getFutureRunnings()) { this.currentLocation = new int[vms.length]; for (int i = 0; i < runnings.size(); i++) { VirtualMachine vm = runnings.get(i); boolean dyn = manageable.contains(vm); VirtualMachineActionModel a; if (this.source.isRunning(vm)) { currentLocation[getVirtualMachine(vm)] = getNode(source.getLocation(vm)); a = new MigratableActionModel(this, vm, durationEval.evaluateMigration(vm), dyn); } else if (this.source.isSleeping(vm)) { a = new ResumeActionModel(this, vm, durationEval.evaluateLocalResume(vm), durationEval.evaluateRemoteResume(vm)); currentLocation[getVirtualMachine(vm)] = getNode(source.getLocation(vm)); } else if (this.source.isWaiting(vm)) { currentLocation[getVirtualMachine(vm)] = -1; a = new RunActionModel(this, vm, durationEval.evaluateRun(vm)); } else { throw new NoAvailableTransitionException(vm, "terminated", "running"); } vmActions.set(getVirtualMachine(vm), a); } for (int i = 0; i < waitings.size(); i++) { VirtualMachine vm = waitings.get(i); // for (VirtualMachine vm : getFutureWaitings()) { if (!this.source.isWaiting(vm)) { if (this.source.isRunning(vm)) { throw new NoAvailableTransitionException(vm, "running", "waiting"); } else if (this.source.isSleeping(vm)) { throw new NoAvailableTransitionException(vm, "sleeping", "waiting"); } else { throw new NoAvailableTransitionException(vm, "terminated", "waiting"); } } } for (int i = 0; i < sleepings.size(); i++) { VirtualMachine vm = sleepings.get(i); // for (VirtualMachine vm : getFutureSleepings()) { if (this.source.isRunning(vm)) { VirtualMachineActionModel a = new SuspendActionModel(this, vm, durationEval.evaluateLocalSuspend(vm)); vmActions.set(getVirtualMachine(vm), a); } else if (this.source.isWaiting(vm)) { throw new NoAvailableTransitionException(vm, "waiting", "sleeping"); } else if (!this.source.isSleeping(vm)) { throw new NoAvailableTransitionException(vm, "terminated", "sleeping"); } } for (int i = 0; i < terminated.size(); i++) { VirtualMachine vm = terminated.get(i); //for (VirtualMachine vm : getFutureTerminated()) { if (this.source.isRunning(vm)) { VirtualMachineActionModel a = new StopActionModel(this, vm, durationEval.evaluateStop(vm)); vmActions.set(getVirtualMachine(vm), a); } else if (this.source.isSleeping(vm)) { throw new NoAvailableTransitionException(vm, "sleeping", "terminated"); } else if (this.source.isWaiting(vm)) { throw new NoAvailableTransitionException(vm, "sleeping", "waiting"); } } //Make the actions for the nodes this.nodesActions = new ArrayList<NodeActionModel>(nodes.length); for (int i = 0; i < nodes.length; i++) { this.nodesActions.add(i, null); } for (Node n : getFutureOnlines()) { if (getSourceConfiguration().getOfflines().contains(n)) { BootNodeActionModel a = new BootNodeActionModel(this, n, durationEval.evaluateStartup(n)); nodesActions.set(getNode(a.getNode()), a); } } for (Node n : getFutureOfflines()) { if (getSourceConfiguration().getOnlines().contains(n)) { ShutdownNodeActionModel a = new ShutdownNodeActionModel(this, n, durationEval.evaluateShutdown(n)); nodesActions.set(getNode(a.getNode()), a); } else { StayOfflineNodeActionModel a = new StayOfflineNodeActionModel(this, n); /*ShutdownNodeActionModel a = new ShutdownNodeActionModel(this, n, durationEval.evaluateShutdown(n)); try { a.start().setVal(0); } catch (Exception e) { Plan.logger.error(e.getMessage(), e); } */ //a.start().setLowB(0); //a.start().setUppB(0); nodesActions.set(getNode(a.getNode()), a); } } //Get all the slices this.demandingSlices = new ArrayList<DemandingSlice>(); this.demandingSlices.addAll(ActionModels.extractDemandingSlices(getVirtualMachineActions())); this.demandingSlices.addAll(ActionModels.extractDemandingSlices(getNodeMachineActions())); this.consumingSlices = new ArrayList<ConsumingSlice>(); this.consumingSlices.addAll(ActionModels.extractConsumingSlices(getVirtualMachineActions())); this.consumingSlices.addAll(ActionModels.extractConsumingSlices(getNodeMachineActions())); } @Override public List<VirtualMachineActionModel> getVirtualMachineActions() { List<VirtualMachineActionModel> actions = new ArrayList<VirtualMachineActionModel>(); for (VirtualMachineActionModel a : vmActions) { if (a != null) { actions.add(a); } } return actions; } public VirtualMachineActionModel getAssociatedVirtualMachineAction(int idxVM) { return vmActions.get(idxVM); } @Override public VirtualMachineActionModel getAssociatedAction(VirtualMachine vm) { int idx = getVirtualMachine(vm); return vmActions.get(idx); } @Override public List<NodeActionModel> getNodeMachineActions() { List<NodeActionModel> actions = new ArrayList<NodeActionModel>(); for (NodeActionModel a : nodesActions) { if (a != null) { actions.add(a); } } return actions; } @Override public NodeActionModel getAssociatedAction(Node n) { return this.nodesActions.get(getNode(n)); } @Override public IntDomainVar getFreeCPU(Node n) { return this.cpuCapacities[getNode(n)]; } @Override public IntDomainVar getFreeMem(Node n) { return this.memCapacities[getNode(n)]; } @Override public IntDomainVar getVMGroup(ManagedElementSet<VirtualMachine> vms) { IntDomainVar v = this.vmsGrp.get(vms); if (v != null) { return v; } v = createEnumIntVar("vmset" + vms.toString(), 0, MAX_NB_GRP); for (VirtualMachine vm : vms) { this.vmGrp.set(getVirtualMachine(vm), v); } this.vmsGrp.put(vms, v); return v; } @Override public IntDomainVar makeGroup(ManagedElementSet<VirtualMachine> vms, List<ManagedElementSet<Node>> nodes) { int[] values = new int[nodes.size()]; for (int i = 0; i < values.length; i++) { values[i] = getGroup(nodes.get(i)); } IntDomainVar v = createEnumIntVar("vmset" + vms.toString(), /*0, MAX_NB_GRP*/values); this.vmsGrp.put(vms, v); //System.err.println(vmsGrp.size()); return v; } @Override public IntDomainVar getAssociatedGroup(VirtualMachine vm) { return this.vmGrp.get(getVirtualMachine(vm)); } @Override public Set<ManagedElementSet<VirtualMachine>> getVMGroups() { return this.vmsGrp.keySet(); } @Override public int getGroup(ManagedElementSet<Node> nodes) { if (this.nodesGrp.get(nodes) != null) { return this.nodesGrp.get(nodes); } else { if (nextNodeGroupVal > MAX_NB_GRP) { return -1; } int v = nextNodeGroupVal++; this.nodesGrp.put(nodes, v); this.revNodesGrp.add(v, nodes); for (Node n : nodes) { List<Integer> l = this.nodeGrps.get(getNode(n)); l.add(v); /*if (grpId[getNode(n)] != 0) { Plan.logger.error("Node group has changed"); return -1; } */ grpId[getNode(n)] = v; } //Set the group of the nodes return v; } } @Override public Set<ManagedElementSet<Node>> getNodesGroups() { return this.nodesGrp.keySet(); } @Override public List<Integer> getAssociatedGroups(Node n) { return this.nodeGrps.get(getNode(n)); } @Override public int[] getNodesGroupId() { return this.grpId; } @Override public ManagedElementSet<Node> getNodeGroup(int idx) { return this.revNodesGrp.get(idx); } @Override public DurationEvaluator getDurationEvaluator() { return this.durationEval; } @Override public List<DemandingSlice> getDemandingSlices() { return this.demandingSlices; } @Override public List<ConsumingSlice> getConsumingSlice() { return this.consumingSlices; } @Override public List<VirtualMachineActionModel> getAssociatedActions(ManagedElementSet<VirtualMachine> vms) { List<VirtualMachineActionModel> l = new LinkedList<VirtualMachineActionModel>(); for (VirtualMachine vm : vms) { VirtualMachineActionModel a = getAssociatedAction(vm); if (a != null) { l.add(a); } } return l; } public int getLocation(int vmIdx) { return currentLocation[vmIdx]; } @Override public SetVar[] getSetModels() { return new SetVar[0]; //To change body of implemented methods use File | Settings | File Templates. } @Override public SetVar getSetModel(Node n) { if (sets == null) { makeSetModel(); } int idx = getNode(n); if (idx < 0) { return null; } return sets[idx]; } @Override public SConstraint<IntDomainVar> implies(SConstraint<IntDomainVar> c1, SConstraint<IntDomainVar> c2) { //implies: or(not(c1),c2) IntDomainVar bC1 = createBooleanVar("bC1"); post(ReifiedFactory.builder(bC1, c1, this)); IntDomainVar bC2 = createBooleanVar("bC2"); post(ReifiedFactory.builder(bC2, c2, this)); SConstraint cNotC1 = BooleanFactory.not(bC1); IntDomainVar bNotC1 = createBooleanVar("!c1"); post(ReifiedFactory.builder(bNotC1, cNotC1, this)); return BooleanFactory.or(getEnvironment(), bNotC1, bC2); } @Override public SConstraint<IntDomainVar> implies(IntDomainVar b1, SConstraint<IntDomainVar> c2) { //implies: or(not(c1),c2) IntDomainVar bC2 = createBooleanVar("bC2"); post(ReifiedFactory.builder(bC2, c2, this)); IntDomainVar notB1 = createBooleanVar("!b1"); post(neq(b1, notB1)); return BooleanFactory.or(getEnvironment(), notB1, bC2); } @Override public SConstraint ifOnlyIf(IntDomainVar b1, SConstraint c2) { //and(or(b1, non b2), or(non b1, b2)) IntDomainVar notBC1 = createBooleanVar("!(" + b1.pretty() + ")"); post(neq(b1, notBC1)); IntDomainVar bC2 = createBooleanVar("boolean(" + c2.pretty() + ")"); post(ReifiedFactory.builder(bC2, c2, this)); IntDomainVar notBC2 = createBooleanVar("!(" + c2.pretty() + ")"); post(neq(notBC2, bC2)); IntDomainVar or1 = createBooleanVar("or1"); post(ReifiedFactory.builder(or1, BooleanFactory.or(getEnvironment(), b1, notBC2), this)); IntDomainVar or2 = createBooleanVar("or2"); post(ReifiedFactory.builder(or2, BooleanFactory.or(getEnvironment(), notBC1, bC2), this)); return BooleanFactory.and(or1, or2); } @Override public TimedReconfigurationPlan extractSolution() { //TODO: check if solution is found //Configuration dst = extractConfiguration(); DefaultTimedReconfigurationPlan plan = new DefaultTimedReconfigurationPlan(getSourceConfiguration()); for (NodeActionModel action : getNodeMachineActions()) { if (action instanceof BootNodeActionModel) { Action a = action.getDefinedAction(this); if (a != null) { if (!plan.add(action.getDefinedAction(this))) { Plan.logger.warn("Action " + a + " is not added into the plan"); } } else { Plan.logger.debug("No resulting action for " + action); } } } for (VirtualMachineActionModel action : getVirtualMachineActions()) { Action a = action.getDefinedAction(this); if (a != null) { plan.add(a); } } for (Action a : plan) { if (a.getStartMoment() == a.getFinishMoment()) { Plan.logger.warn("Action " + a + " has a duration equals to 0"); } } for (NodeActionModel action : getNodeMachineActions()) { if (action instanceof ShutdownNodeActionModel) { Action a = action.getDefinedAction(this); if (a != null) { if (!plan.add(action.getDefinedAction(this))) { Plan.logger.warn("Action " + a + " is not added into the plan"); } }/* else { Plan.logger.debug("No resulting action for " + action); } */ } } if (plan.getDuration() != end.getVal()) { Plan.logger.error("Theoretical duration (" + getEnd().getVal() + ") and plan duration " + plan.getDuration() + " mismatch"); return null; } return plan; } @Override public List<SolutionStatistics> getSolutionsStatistics() { List<SolutionStatistics> stats = new LinkedList<SolutionStatistics>(); for (Solution s : this.getSearchStrategy().getStoredSolutions()) { IMeasures m = s.getMeasures(); SolutionStatistics st; if (m.getObjectiveValue() != null) { st = new SolutionStatistics(m.getNodeCount(), m.getBackTrackCount(), m.getTimeCount(), this.isEncounteredLimit(), m.getObjectiveValue().intValue()); } else { st = new SolutionStatistics(m.getNodeCount(), m.getBackTrackCount(), m.getTimeCount(), this.isEncounteredLimit()); } stats.add(st); } Collections.sort(stats, SolutionStatisticsComparator); return stats; } @Override public SolvingStatistics getSolvingStatistics() { return new SolvingStatistics( this.getNodeCount(), this.getBackTrackCount(), this.getTimeCount(), this.isEncounteredLimit()); } private static Comparator SolutionStatisticsComparator = new Comparator<SolutionStatistics>() { @Override public int compare(SolutionStatistics sol1, SolutionStatistics sol2) { if (sol1.getTimeCount() == sol2.getTimeCount()) { //Compare wrt. the number of nodes or backtracks if (sol1.getNbNodes() == sol2.getTimeCount()) { return sol1.getNbBacktracks() - sol2.getNbBacktracks(); } return sol1.getNbNodes() - sol2.getNbNodes(); } return sol1.getTimeCount() - sol2.getTimeCount(); } }; }