/*
* Copyright (c) 2010 Ecole des Mines de Nantes.
*
* 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.ArrayList;
import java.util.List;
import choco.cp.solver.constraints.integer.Element;
import choco.kernel.solver.constraints.SConstraint;
import choco.kernel.solver.variables.integer.IntDomainVar;
import entropy.configuration.Configuration;
import entropy.configuration.ManagedElementSet;
import entropy.configuration.Node;
import entropy.configuration.VirtualMachine;
import entropy.plan.choco.ReconfigurationProblem;
/**
* A constraint to enforce a set of virtual machines
* to be hosted on a single group of physical elements among those give in parameters.
*
* @author Fabien Hermenier
*/
public class OneOf implements PlacementConstraint {
/**
* The list of possible groups of nodes.
*/
private VJobMultiSet<Node> groups;
/**
* The list of VMs involved in the constraint.
*/
private VJobSet<VirtualMachine> vms;
/**
* Make a new constraint that enforce all the virtual machines
* to be hosted on a single group of node among those given in parameters.
*
* @param vms the set of VMs to assign.
* @param groups the list of possible groups of nodes.
*/
public OneOf(VJobSet<VirtualMachine> vms, VJobMultiSet<Node> groups) {
this.vms = vms;
this.groups = groups;
}
/**
* Get the virtual machines involved in the constraint.
*
* @return a set of VMs. should not be empty
*/
@Override
public ExplodedSet<VirtualMachine> getAllVirtualMachines() {
return this.vms.flatten();
}
@Override
public ExplodedSet<Node> getNodes() {
return new ExplodedSet<Node>(this.groups.getElements());
}
/**
* Get the different groups of nodes involved in the constraint.
*
* @return a set of groups. May be empty
*/
public VJobMultiSet<Node> getGroups() {
return this.groups;
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append("oneOf(").append(vms.pretty());
buffer.append(", ").append(groups.pretty()).append(")");
return buffer.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OneOf that = (OneOf) o;
return groups.equals(that.groups) && getAllVirtualMachines().equals(that.getAllVirtualMachines());
}
@Override
public int hashCode() {
int result = getAllVirtualMachines().hashCode();
result = 31 * result + groups.hashCode();
return result;
}
@Override
public void inject(ReconfigurationProblem core) {
if (getGroups().size() == 0) {
VJob.logger.debug("Ignoring " + this + ", no groups were specified");
return;
}
//Get only the future running VMs
ManagedElementSet<VirtualMachine> runnings = getAllVirtualMachines().clone();
runnings.retainAll(core.getFutureRunnings());
VJob.logger.debug(this + " only consider " + runnings);
//Now, we create a group variable & all that stuff
IntDomainVar vmGrpId = null;
List<ManagedElementSet<Node>> grps = new ArrayList<ManagedElementSet<Node>>();
List<ExplodedSet<Node>> ns = getGroups().expand();
for (int i = 0; i < ns.size(); i++) {
grps.add(ns.get(i));
}
vmGrpId = core.makeGroup(getAllVirtualMachines(), grps);
//ChocoCustomizablePlannerModule.LOGGER.debug(vmGrpId + " " + Arrays.toString(toKeep));
//ManagedElementSet<Node> toKeep = groups.getElements();
//new Fence(getAllVirtualMachines(), new ExplodedList<Node>(toKeep)).inject(core);
//model.addConstraint(Choco.among(vmGrpId, toKeep));
List<ExplodedSet<Node>> all = getGroups().expand();
ManagedElementSet<Node> involved = getGroups().getElements();
//Must be the position of the VM in the context of this group
int[] values = new int[involved.size()];
for (int i = 0; i < values.length; i++) {
Node n = core.getNode(i);
//Get the group that belong to each node
for (int j = 0; j < getGroups().size(); j++) {
ManagedElementSet<Node> grp = all.get(j);
if (grp.contains(n)) {
values[i] = j;
break;
}
}
}
for (VirtualMachine vm : runnings) {
IntDomainVar assign = core.getAssociatedAction(vm).getDemandingSlice().hoster();
SConstraint c = new Element(assign, /*values*/core.getNodesGroupId(), vmGrpId);
//if (vm.getName().equals("VM518")) {
//System.err.println(System.currentTimeMillis() + " " + assign.pretty());
//System.err.println(c.pretty());
//System.err.flush();
//}
core.post(c);
}
}
/**
* Check that the constraint is satisfied in a configuration.
*
* @param cfg the configuration to check
* @return true if the running VMs are hosted on more than one group
*/
@Override
public boolean isSatisfied(Configuration cfg) {
if (getGroups().size() == 0) {
VJob.logger.error("No group of nodes was specified");
return false;
}
VirtualMachine vm1 = null;
for (VirtualMachine vm : vms) {
if (cfg.isRunning(vm)) {
vm1 = vm;
break;
}
}
if (vm1 == null) {
//No running VMs, no need to check
return true;
}
Node n1 = cfg.getLocation(vm1);
VJobSet<Node> selectedGroup = null;
for (VJobSet<Node> grp : getGroups().expand()) {
if (grp.contains(n1)) {
selectedGroup = grp;
break;
}
}
if (selectedGroup == null) {
VJob.logger.error(this + ": " + vm1.getName() + " is running on an invalid group");
return false;
}
for (VirtualMachine vm : getAllVirtualMachines()) {
Node n = cfg.getLocation(vm);
if (cfg.isRunning(vm)) {
if (!selectedGroup.contains(n)) {
//ChocoCustomizablePlannerModule.LOGGER.error(this +": " + vm.getName() + " is not running into " + selectedGroup + " instead: " + n.getName());
return false;
}
}
}
return true;
}
@Override
public ExplodedSet<VirtualMachine> getMisPlaced(Configuration cfg) {
if (!isSatisfied(cfg)) {
return getAllVirtualMachines();
}
return new ExplodedSet<VirtualMachine>();
}
}