/* * 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.plan.choco; import gipad.configuration.CostFunction; import gipad.configuration.configuration.Configuration; import gipad.configuration.configuration.ConfigurationUtils; import gipad.configuration.configuration.Node; import gipad.configuration.configuration.VirtualMachine; import gipad.exception.DurationEvaluationException; import gipad.exception.MultipleResultingStateException; import gipad.exception.NoAvailableTransitionException; import gipad.exception.NonViableSourceConfigurationException; import gipad.exception.PlanException; import gipad.exception.UnknownResultingStateException; import gipad.plan.Plan; import gipad.plan.action.Action; import gipad.plan.choco.actionmodel.MigratableActionModel; import gipad.plan.choco.actionmodel.NodeActionModel; import gipad.plan.choco.actionmodel.RunActionModel; import gipad.plan.choco.actionmodel.StopActionModel; import gipad.plan.choco.actionmodel.VirtualMachineActionModel; import gipad.tools.DC; import gipad.tools.ManagedElementList; import gipad.tools.SimpleManagedElementList; import gnu.trove.map.hash.TIntIntHashMap; import gipad.plan.choco.actionmodel.slice.*; 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.Solver; import solver.search.measure.IMeasures; import solver.search.solution.Solution; import solver.variables.IntVar; import solver.variables.SetVar; import solver.variables.VF; import solver.variables.VariableFactory; import gipad.configuration.*; import gipad.configuration.configuration.*; import gipad.configuration.configuration.Configuration; import gipad.plan.*; import gipad.plan.action.*; import gnu.trove.map.hash.TIntIntHashMap; import gipad.plan.action.Action; import gipad.plan.choco.actionmodel.*; import gipad.plan.choco.actionmodel.slice.*; import gipad.plan.choco.constraints.CumulativeMultiDim; import gipad.plan.choco.constraints.SatisfyDemandingSliceHeights; import gipad.exception.*; import gipad.tools.*; import org.discovery.DiscoveryModel.model.Node; import org.discovery.DiscoveryModel.model.VirtualMachine; import solver.*; import solver.constraints.ICF; import solver.constraints.set.SCF; import solver.variables.*; /** * 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 implements ReconfigurationProblem { private ManagedElementList<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 IntVar<?> start; /** * The moment the reconfiguration ends. Variable. */ private IntVar<?> 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 ManagedElementList<VirtualMachine> runnings; /** * The future waiting VMs. */ private ManagedElementList<VirtualMachine> waitings; /** * The future sleeping VMs. */ private ManagedElementList<VirtualMachine> sleepings; /** * The future terminated VMs. */ private ManagedElementList<VirtualMachine> terminated; /** * The future online nodes. */ private ManagedElementList<Node> onlines; /** * The future offline nodes. */ private ManagedElementList<Node> offlines; /** * All the nodes managed by the model. */ private Node[] nodes; private TIntIntHashMap revNodes; private Solver s; /** * A set model for each node. */ private SetVar[] sets; /** * Cpu usage indexed by the index of the node. */ private IntVar<?>[] cpuCapacities; /** * Mem usage indexed by the index of the node. */ private IntVar<?>[] memCapacities; /** * Network input indexed by the index of the node. */ private IntVar<?>[] netInCapacities; /** * Network output indexed by the index of the node. */ private IntVar<?>[] netOutCapacities; /** * All the virtual machines managed by the model. */ private VirtualMachine[] vms; private int[] currentLocation; private TIntIntHashMap revVMs; /** * The Cost Function */ private CostFunction costFunc; /** * The group variable associated to each virtual machine. */ private List<IntVar> vmGrp; /** * The group variable associated to each group of VMs. */ private Map<ManagedElementList<VirtualMachine>, IntVar> vmsGrp; /** * The value associated to each group of nodes. */ private Map<ManagedElementList<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<ManagedElementList<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 IncomingSlices */ private List<IncomingSlice> incomingSlices; /** * All leavingSlice */ private List<LeavingSlice> leavingSlices; /** * All the demanding slices in the model. */ private List<DemandingSlice> demandingSlices; private SatisfyDemandingSliceHeights packing; //TODO private int[] grpId; //The group ID of each node /** * 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, ManagedElementList<VirtualMachine> run, ManagedElementList<VirtualMachine> wait, ManagedElementList<VirtualMachine> sleep, ManagedElementList<VirtualMachine> stop, ManagedElementList<VirtualMachine> manageable, ManagedElementList<Node> on, ManagedElementList<Node> off, CostFunction costFunc) throws PlanException { this.source = src; this.manageable = manageable; runnings = run; waitings = wait; sleepings = sleep; terminated = stop; onlines = on; offlines = off; this.costFunc = costFunc; this.checkDisjointSet(); if (Configurations.currentlyOverloadedNodes(this.source).size() > 0) { throw new NonViableSourceConfigurationException(source, Configurations .currentlyOverloadedNodes(source).get(0)); } start = VF.fixed(0,s); end = VF.bounded("end", 0, MAX_TIME, s); s.post(ICF.arithm(start, "<=", end)); 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); } ManagedElementList<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(); // creation des actions possible pour chaque VM // En fonction de l'état actuel de la VM et de l'action qui est // demandé à la VM } catch (DurationEvaluationException e) { throw new PlanException(e.getMessage(), e); } this.makeResourcesCapacities(); // creation de toutes les variables qui représentent les sommes de // comsommations sur chaque noeud //TODO this.vmGrp = new ArrayList<IntVar>(this.vms.length); for (int i = 0; i < vms.length; i++) { this.vmGrp.add(i, null); } this.vmsGrp = new HashMap<ManagedElementList<VirtualMachine>, IntVar>(); 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<ManagedElementList<Node>, Integer>(); this.revNodesGrp = new ArrayList<ManagedElementList<Node>>(MAX_NB_GRP); //notre cumulative colorée ici packing = new SatisfyDemandingSliceHeights(this);// new SatisfyDemandingSlicesHeightsSimpleBP(); //TODO: Uncomment for capacity /* * if (!this.demandingSlices.isEmpty()) { this.makeSetModel(); } */ new SlicesPlanner().add(this); } //TODO public DefaultReconfigurationProblem(Configuration src, ManagedElementList<VirtualMachine> vms, CostFunction costFunc) throws PlanException { this.source = src; this.manageable = vms; runnings = src.getRunnings(); waitings = new SimpleManagedElementList<VirtualMachine>(); // no vm waiting sleepings = new SimpleManagedElementList<VirtualMachine>(); // no vm terminated = new SimpleManagedElementList<VirtualMachine>(); onlines = src.getOnlines(); offlines = new SimpleManagedElementList<Node>(); this.costFunc = costFunc; this.checkDisjointSet(); if (ConfigurationUtils.getOverloadedNodes(this.source).size() > 0) { throw new NonViableSourceConfigurationException(source, ConfigurationUtils .getOverloadedNodes(source).get(0)); } } /** * 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 = VF.set("host(" + n.name() + ")", 0, demandingSlices.size() - 1,this.s); sets[i] = s; } //Make the channeling with the assignment variable of all the d-slices IntVar<?>[] assigns = SliceUtils.extractHosters(demandingSlices.toArray(new Slice[demandingSlices.size()])); //TODO verify : post(new InverseSetInt(assigns, sets)); s.post(SCF.int_channel(sets, assigns, 0, 0)); } } public SatisfyDemandingSliceHeights getSatisfyDSlicesHeightConstraint() { return this.packing; } /** * Set the resources capacity of the nodes. */ private void makeResourcesCapacities() { this.cpuCapacities = new IntVar[nodes.length]; this.memCapacities = new IntVar[nodes.length]; this.netInCapacities = new IntVar[nodes.length]; this.netOutCapacities = new IntVar[nodes.length]; ManagedElementList<Node> involvedNodes = new SimpleManagedElementList<Node>(); for (Node n : getFutureOfflines()) { NodeActionModel action = getAssociatedAction(n); if (action != null) { involvedNodes.add(n); } } involvedNodes.addAll(getFutureOnlines()); for (Node n : involvedNodes) { cpuCapacities[getNode(n)] = VF.bounded(n.name()+"#cpuCapacity", 0, DC.getSumCPu(n), getSolver()); memCapacities[getNode(n)] = VF.bounded(n.name()+"#memCapacity", 0, (int)n.hardwareSpecification().memory().capacity(), getSolver()); netInCapacities[getNode(n)] = VF.bounded(n.name()+"#netInCapacity", 0, n.hardwareSpecification().networkInterfaces().get(0), s);//FIXME : getCapa netOutCapacities[getNode(n)] = VF.bounded(n.name()+"#netOutCapacity", 0, n.hardwareSpecification().networkInterfaces().get(0), s);//FIXME : getCapa } } /** * Check all the nodes belong to only one set. * * @throws gipad.exception.UnknownResultingStateException * if the state of an element is not defined * @throws gipad.exception.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 ManagedElementList<VirtualMachine> getFutureRunnings() { return this.runnings; } @Override public ManagedElementList<VirtualMachine> getFutureWaitings() { return this.waitings; } @Override public ManagedElementList<VirtualMachine> getFutureSleepings() { return this.sleepings; } @Override public ManagedElementList<VirtualMachine> getFutureTerminated() { return this.terminated; } @Override public ManagedElementList<Node> getFutureOnlines() { return this.onlines; } @Override public ManagedElementList<Node> getFutureOfflines() { return this.offlines; } @Override public IntVar<?> getStart() { return this.start; } @Override public IntVar<?> 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; } /** FIXME duration evaluator * Create all the basic action that manipulate the state of the virtual * machine and the nodes. creation de l'arraylist qui contient l'ensemble * des actions possibles pour chaque VM * * @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(ActionModelUtils.extractDemandingSlices(getVirtualMachineActions())); this.demandingSlices.addAll(ActionModelUtils.extractDemandingSlices(getNodeMachineActions())); this.consumingSlices = new ArrayList<ConsumingSlice>(); this.consumingSlices.addAll(ActionModelUtils.extractConsumingSlices(getVirtualMachineActions())); this.consumingSlices.addAll(ActionModelUtils.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 IntVar<?> getFreeCPU(Node n) { return this.cpuCapacities[getNode(n)]; } @Override public IntVar<?> getFreeMem(Node n) { return this.memCapacities[getNode(n)]; } @Override public IntVar<?> getVMGroup(ManagedElementList<VirtualMachine> vms) { IntVar<?> v = this.vmsGrp.get(vms); if (v != null) { return v; } v = VF.enumerated("vmset" + vms.toString(), 0, MAX_NB_GRP, s); for (VirtualMachine vm : vms) { this.vmGrp.set(getVirtualMachine(vm), v); } this.vmsGrp.put(vms, v); return v; } @Override public IntVar<?> makeGroup(ManagedElementList<VirtualMachine> vms, List<ManagedElementList<Node>> nodes) { int[] values = new int[nodes.size()]; for (int i = 0; i < values.length; i++) { values[i] = getGroup(nodes.get(i)); } IntVar<?> v = VF.enumerated("vmset" + vms.toString(), /*0, MAX_NB_GRP*/values,s); this.vmsGrp.put(vms, v); //System.err.println(vmsGrp.size()); return v; } @Override public IntVar<?> getAssociatedGroup(VirtualMachine vm) { return this.vmGrp.get(getVirtualMachine(vm)); } @Override public Set<ManagedElementList<VirtualMachine>> getVMGroups() { return this.vmsGrp.keySet(); } @Override public int getGroup(ManagedElementList<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<ManagedElementList<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 ManagedElementList<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(ManagedElementList<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<IntVar> implies(SConstraint<IntVar> c1, SConstraint<IntVar> c2) { //implies: or(not(c1),c2) IntVar bC1 = createBooleanVar("bC1"); post(ReifiedFactory.builder(bC1, c1, this)); IntVar bC2 = createBooleanVar("bC2"); post(ReifiedFactory.builder(bC2, c2, this)); SConstraint cNotC1 = BooleanFactory.not(bC1); IntVar bNotC1 = createBooleanVar("!c1"); post(ReifiedFactory.builder(bNotC1, cNotC1, this)); return BooleanFactory.or(getEnvironment(), bNotC1, bC2); } @Override public SConstraint<IntVar> implies(IntVar b1, SConstraint<IntVar> c2) { //implies: or(not(c1),c2) IntVar bC2 = createBooleanVar("bC2"); post(ReifiedFactory.builder(bC2, c2, this)); IntVar notB1 = createBooleanVar("!b1"); post(neq(b1, notB1)); return BooleanFactory.or(getEnvironment(), notB1, bC2); } @Override public SConstraint ifOnlyIf(IntVar b1, SConstraint c2) { //and(or(b1, non b2), or(non b1, b2)) IntVar notBC1 = createBooleanVar("!(" + b1.pretty() + ")"); post(neq(b1, notBC1)); IntVar bC2 = createBooleanVar("boolean(" + c2.pretty() + ")"); post(ReifiedFactory.builder(bC2, c2, this)); IntVar notBC2 = createBooleanVar("!(" + c2.pretty() + ")"); post(neq(notBC2, bC2)); IntVar or1 = createBooleanVar("or1"); post(ReifiedFactory.builder(or1, BooleanFactory.or(getEnvironment(), b1, notBC2), this)); IntVar 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.getValue()) { Plan.logger.error("Theoretical duration (" + getEnd().getValue() + ") 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; // } /*FIXME @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(); // } // }; @Override public Solver getSolver() { return s; } public IntVar[] getCpuCapacities() { return cpuCapacities; } public IntVar[] getMemCapacities() { return memCapacities; } public IntVar[] getNetInCapacities() { return netInCapacities; } public IntVar[] getNetOutCapacities() { return netOutCapacities; } }