/* * 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.configuration; import java.util.HashMap; import java.util.Map; /** * A configuration is an overview of a cluster. It shows all the virtual * machines and the nodes involved in the configuration and the assignment * of the virtual machines to the nodes. * <br/> * Each of the node is either <b>online</b> or <b>offline</b>. * <br/> * Each of the virtual machines can be either: * <ul> * <li><b>running</b> on an online node</li> * <li><b>waiting</b> on a <i>farm</i> to be launched</li> * <li><b>sleeping</b> on a node. In this state, the image of the virtual machine is stored on * the node</li> * </ul> * <p/> * TODO: Better getOverloadedNodesX(). * * @author Fabien Hermenier */ public class DefaultConfiguration implements Configuration { /** * The VirtualMachines running on each node. */ private Map<Node, ManagedElementSet<VirtualMachine>> invRunnings; /** * The VirtualMachines sleeping on each node. */ private Map<Node, ManagedElementSet<VirtualMachine>> invSleepings; /** * The Node that host each VirtualMachine. */ private Map<VirtualMachine, Node> affects; /** * List of nodes that are onlines. */ private ManagedElementSet<Node> onlines; /** * List of nodes that are offlines. */ private ManagedElementSet<Node> offlines; /** * List of virtual machines that are waiting. */ private ManagedElementSet<VirtualMachine> waitings; /** * List of virtual machines that are running. */ private ManagedElementSet<VirtualMachine> runnings; /** * List of the virtual machines that are sleeping. */ private ManagedElementSet<VirtualMachine> sleepings; /** * The current list associated to each virtual machine. */ private Map<VirtualMachine, ManagedElementSet<VirtualMachine>> currentList; /** * Instantiate a new empty Configuration. */ public DefaultConfiguration() { this.currentList = new HashMap<VirtualMachine, ManagedElementSet<VirtualMachine>>(); this.invRunnings = new HashMap<Node, ManagedElementSet<VirtualMachine>>(); this.invSleepings = new HashMap<Node, ManagedElementSet<VirtualMachine>>(); this.affects = new HashMap<VirtualMachine, Node>(); this.onlines = new DefaultManagedElementSet<Node>(); this.offlines = new DefaultManagedElementSet<Node>(); this.waitings = new DefaultManagedElementSet<VirtualMachine>(); this.runnings = new DefaultManagedElementSet<VirtualMachine>(); this.sleepings = new DefaultManagedElementSet<VirtualMachine>(); } /** * Copy constructor. * Only the assignements are cloned * * @param ref The reference Configuration */ public DefaultConfiguration(Configuration ref) { this(); for (Node n : ref.getOfflines()) { addOffline(n); } for (Node n : ref.getOnlines()) { addOnline(n); for (VirtualMachine vm : ref.getRunnings(n)) { setRunOn(vm, n); } for (VirtualMachine vm : ref.getSleepings(n)) { setSleepOn(vm, n); } for (VirtualMachine vm : ref.getWaitings()) { addWaiting(vm); } } } /** * Set a virtual machine running on a node. The node must already be online. * If the virtual machine is already in a other location or state in the configuration, it is updated * * @param vm the virtual machine * @param node the node that will host the virtual machine. Must be considered as online. * @return true if the vm is assigned on the node. False otherwise */ @Override public boolean setRunOn(VirtualMachine vm, Node node) { if (this.getOnlines().contains(node)) { ManagedElementSet<VirtualMachine> list = this.currentList.get(vm); if (list != null) { Node oldNode = this.affects.get(vm); if (list == this.runnings) { this.getRunnings(oldNode).remove(vm); } else if (list == this.sleepings) { this.sleepings.remove(vm); this.getSleepings(oldNode).remove(vm); } else if (list == this.waitings) { this.waitings.remove(vm); } } //Impact on the reverse affectations this.runnings.add(vm); this.currentList.put(vm, this.runnings); this.affects.put(vm, node); this.getRunnings(node).add(vm); return true; } return false; } /** * Set a virtual machine sleeping on a node. * If the virtual machine is already in a other location or state in the configuration, it is updated * * @param vm the virtual machine * @param node the node that will host the virtual machine. Must be considered as online. * @return false if the hosting node is offline or unknown */ @Override public boolean setSleepOn(VirtualMachine vm, Node node) { if (this.getOnlines().contains(node)) { ManagedElementSet<VirtualMachine> list = this.currentList.get(vm); if (list != null) { Node oldNode = this.affects.get(vm); if (list == this.sleepings) { this.getSleepings(oldNode).remove(vm); } else if (list == this.runnings) { this.getRunnings(oldNode).remove(vm); this.getRunnings().remove(vm); } else if (list == this.waitings) { this.waitings.remove(vm); } } this.sleepings.add(vm); this.currentList.put(vm, this.sleepings); this.affects.put(vm, node); this.getSleepings(node).add(vm); return true; } return false; } /** * Set a virtual machine waiting. * If the virtual machine is already in a other location or state in the configuration, it is updated * * @param vm the virtual machine */ @Override public void addWaiting(VirtualMachine vm) { ManagedElementSet<VirtualMachine> list = this.currentList.get(vm); if (list != null) { //We have to remove reverse assignment for consistency if (list == this.runnings) { this.invRunnings.get(this.affects.get(vm)).remove(vm); } else if (list == this.sleepings) { this.invSleepings.get(this.affects.get(vm)).remove(vm); } //No need to remove the key in this.affects. list.remove(vm); } this.waitings.add(vm); this.currentList.put(vm, this.waitings); } /** * Remove a virtual machine. * * @param vm the virtual machine to remove */ @Override public void remove(VirtualMachine vm) { ManagedElementSet<VirtualMachine> list = this.currentList.get(vm); if (list != null) { //We have to remove reverse assignment for consistency if (list == this.runnings) { this.invRunnings.get(this.affects.get(vm)).remove(vm); } else if (list == this.sleepings) { this.invSleepings.get(this.affects.get(vm)).remove(vm); } //No need to remove the key in this.affects. list.remove(vm); } this.runnings.remove(vm); this.currentList.remove(vm); } /** * Get the list of nodes that are online. * * @return a list, may be empty */ @Override public ManagedElementSet<Node> getOnlines() { return this.onlines; } /** * Set a node online. If the node is already in the configuration but in an another state, it is updated. * * @param node the node to add */ @Override public void addOnline(Node node) { this.offlines.remove(node); this.onlines.add(node); if (this.invRunnings.get(node) == null) { this.invRunnings.put(node, new DefaultManagedElementSet<VirtualMachine>()); } if (this.invSleepings.get(node) == null) { this.invSleepings.put(node, new DefaultManagedElementSet<VirtualMachine>()); } } /** * Set a node offline. If the node is already in the configuration but in an another state, it is updated. * The node must not host any virtual machines * * @param node the node * @return true if the node is offline. False otherwise */ @Override public boolean addOffline(Node node) { if (this.invRunnings.get(node) == null) { this.invRunnings.put(node, new DefaultManagedElementSet<VirtualMachine>()); } if (this.invSleepings.get(node) == null) { this.invSleepings.put(node, new DefaultManagedElementSet<VirtualMachine>()); } if (this.getRunnings(node).size() == 0 && this.getSleepings(node).size() == 0) { this.onlines.remove(node); return this.offlines.add(node); } else { return false; } } /** * Get the nodes that are offline. * * @return a list of nodes, may be empty */ @Override public ManagedElementSet<Node> getOfflines() { return this.offlines; } /** * Get the virtual machines that are running. * * @return a set of VirtualMachines, may be empty */ @Override public ManagedElementSet<VirtualMachine> getRunnings() { return this.runnings; } /** * Get the virtual machines that are sleeping. * * @return a set of virtual machines, may be empty */ @Override public ManagedElementSet<VirtualMachine> getSleepings() { return this.sleepings; } /** * Get the virtual machines that are sleeping on a node. * * @param n the node * @return a set of virtual machines, may be empty */ @Override public ManagedElementSet<VirtualMachine> getSleepings(Node n) { return this.invSleepings.get(n); } /** * Get the virtual machines that are running on a node. * * @param n the node * @return a set of virtual machines, may be empty */ @Override public ManagedElementSet<VirtualMachine> getRunnings(Node n) { return this.invRunnings.get(n); } /** * Get the location of a sleeping virtual machine. * * @param vm the virtual machine * @return its host, or null if the virtual machine is not defined as sleeping */ @Override public Node getSleepingLocation(VirtualMachine vm) { if (this.sleepings.contains(vm)) { return this.affects.get(vm); } return null; } /** * Get the virtual machines that are waiting. * * @return a list, may be empty */ @Override public ManagedElementSet<VirtualMachine> getWaitings() { return this.waitings; } /** * Get all the virtual machines involved in the configuration. * * @return a set, may be empty */ @Override public ManagedElementSet<VirtualMachine> getAllVirtualMachines() { ManagedElementSet<VirtualMachine> all = new DefaultManagedElementSet<VirtualMachine>(); all.addAll(getRunnings()); all.addAll(getWaitings()); all.addAll(getSleepings()); return all; } /** * Get all the nodes involved in the configuration. * * @return a set, may be empty */ @Override public ManagedElementSet<Node> getAllNodes() { ManagedElementSet<Node> all = new DefaultManagedElementSet<Node>(); all.addAll(getOnlines()); all.addAll(getOfflines()); return all; } /** * Return the node that host a running virtual machine. * * @param vm The VirtualMachine * @return The node that host the VirtualMachine or null if * the virtual machine is not defined as running */ @Override public Node getRunningLocation(VirtualMachine vm) { if (this.runnings.contains(vm)) { return this.affects.get(vm); } return null; } /** * Return the subset of nodes that host at least one running VirtualMachine. * * @return subset of node that may be empty */ public ManagedElementSet<Node> getUsedNodes() { ManagedElementSet<Node> set = new DefaultManagedElementSet<Node>(); for (Node n : this.getOnlines()) { if (this.getRunnings(n).size() > 0) { set.add(n); } } return set; } /** * Return the subset of nodes that host no running VirtualMachine. * * @return a subset of node that may be empty */ public ManagedElementSet<Node> getUnusedNodes() { ManagedElementSet<Node> set = new DefaultManagedElementSet<Node>(); for (Node n : this.getOnlines()) { if (this.getRunnings(n).size() == 0) { set.add(n); } } return set; } /** * Retrun the subset of nodes that are currently overloaded. * A node is considered as overloaded if the total memory or CPU currently * consumed by the virtual machines it hosts is greater than its memory or CPU capacity. * * @return a subset of nodes, may be empty. */ public ManagedElementSet<Node> getCurrentlyOverloadedNodes() { ManagedElementSet<Node> nodes = new DefaultManagedElementSet<Node>(); for (Node n : this.getOnlines()) { int cpuCapa = n.getCPUCapacity(); int memCapa = n.getMemoryCapacity(); for (VirtualMachine vm : invRunnings.get(n)) { cpuCapa -= vm.getCPUConsumption(); memCapa -= vm.getMemoryConsumption(); if (cpuCapa < 0 || memCapa < 0) { nodes.add(n); break; } } } return nodes; } /** * Check wether the current configuration is overloaded or not. * * @return true if at least one node is overloaded */ public boolean isCurrentlyConsistent() { for (Node n : this.getOnlines()) { int cpuCapa = n.getCPUCapacity(); int memCapa = n.getMemoryCapacity(); for (VirtualMachine vm : invRunnings.get(n)) { cpuCapa -= vm.getCPUConsumption(); memCapa -= vm.getMemoryConsumption(); if (cpuCapa < 0 || memCapa < 0) { return false; } } } return true; } /** * Retuun the subset of nodes that can not satisfy the resource demand of the VMs. * A node is considered as overloaded if the total memory or CPU demand * of the virtual machines it hosts is greater than its memory or CPU capacity. * * @return a subset of nodes, may be empty. */ public ManagedElementSet<Node> getUnacceptableNodes() { ManagedElementSet<Node> nodes = new DefaultManagedElementSet<Node>(); for (Node n : this.getOnlines()) { int cpuCapa = n.getCPUCapacity(); int memCapa = n.getMemoryCapacity(); for (VirtualMachine vm : getRunnings(n)) { cpuCapa -= vm.getCPUDemand(); memCapa -= vm.getMemoryDemand(); if (cpuCapa < 0 || memCapa < 0) { nodes.add(n); break; } } } return nodes; } /** * Get a configuration that use all the nodes but only a subset of * virtual machines. * * @param vms the subset of virtual machines * @return a new Configuration */ public Configuration getSubConfiguration(ManagedElementSet<VirtualMachine> vms) { Configuration c = new DefaultConfiguration(); for (Node n : this.getOnlines()) { c.addOnline(n); } for (Node n : this.getOfflines()) { c.addOffline(n); } for (VirtualMachine vm : vms) { Node n = this.getRunningLocation(vm); if (n != null) { c.setRunOn(vm, n); } else { n = this.getSleepingLocation(vm); if (n != null) { c.setSleepOn(vm, n); } else { c.addWaiting(vm); } } } return c; } /** * Textual representation of the configuration. * * @return the textual representation */ @Override public String toString() { StringBuilder buf = new StringBuilder(); for (Node n : getOnlines()) { buf.append(n.getName()); buf.append(":"); for (VirtualMachine vm : this.getRunnings(n)) { buf.append(" "); buf.append(vm.getName()); } if (this.getSleepings(n).size() > 0) { for (VirtualMachine vm : this.getSleepings(n)) { buf.append(" ("); buf.append(vm.getName()); buf.append(")"); } } buf.append("\n"); } for (Node n : getOfflines()) { buf.append("("); buf.append(n.getName()); buf.append(")\n"); } buf.append("FARM"); for (VirtualMachine vm : this.getWaitings()) { buf.append(" "); buf.append(vm.getName()); } buf.append("\n"); return buf.toString(); } /** * Check the equality of two configuration. * * @param o The object to compare with * @return true if o is a Configuration, if both configuration have the same virtual machines and nodes * and if the assignment of the virtual machines is the same */ @Override public boolean equals(Object o) { if (o == null) { return false; } else if (o == this) { return true; } else if (o instanceof Configuration) { Configuration ref = (Configuration) o; //Check the states of each component //Check presence, not order! if (this.getRunnings().equals(ref.getRunnings()) && this.getSleepings().equals(ref.getSleepings()) && this.getWaitings().equals(ref.getWaitings()) && this.getOnlines().equals(ref.getOnlines()) && this.getOfflines().equals(ref.getOfflines()) ) { //Check the assignements of VMs for (Node n : this.getOnlines()) { if (!this.getRunnings(n).equals(ref.getRunnings(n))) { return false; } else if (!this.getSleepings(n).equals(ref.getSleepings(n))) { return false; } } return true; } } return false; } @Override public int hashCode() { int hashCode = 1; for (Node n : this.getOnlines()) { hashCode += getRunnings(n).hashCode() * 31; hashCode += getSleepings(n).hashCode() * 31; } hashCode += getWaitings().hashCode() * 31; return hashCode; } /** * Test if a node is online. * * @param n the node * @return true if the node is online */ @Override public boolean isOnline(Node n) { return this.getOnlines().contains(n); } /** * Test if a node is offline. * * @param n the node * @return true if the node is offline */ @Override public boolean isOffline(Node n) { return this.getOfflines().contains(n); } /** * Test if a virtual machine is running. * * @param vm the virtual machine * @return true if the virtual machine is running */ @Override public boolean isRunning(VirtualMachine vm) { return this.getRunnings().contains(vm); } /** * Test if a virtual machine is waiting. * * @param vm the virtual machine * @return true if the virtual machine is waiting */ @Override public boolean isWaiting(VirtualMachine vm) { return this.getWaitings().contains(vm); } /** * Test if a virtual machine is sleeping. * * @param vm the virtual machine * @return true if the virtual machine is sleeping */ @Override public boolean isSleeping(VirtualMachine vm) { return this.getSleepings().contains(vm); } /** * Get all the virtual machines running on a set of nodes. * * @param ns the set of nodes * @return a set of virtual machines, may be empty */ @Override public ManagedElementSet<VirtualMachine> getRunnings(ManagedElementSet<Node> ns) { ManagedElementSet<VirtualMachine> vms = new DefaultManagedElementSet<VirtualMachine>(); for (Node n : ns) { vms.addAll(getRunnings(n)); } return vms; } @Override public boolean remove(Node n) { throw new UnsupportedOperationException(); } @Override public Node getLocation(VirtualMachine vm) { if (isRunning(vm)) { return getRunningLocation(vm); } else if (isSleeping(vm)) { return getSleepingLocation(vm); } return null; } @Override public Configuration clone() { return new DefaultConfiguration(this); } @Override public boolean contains(Node n) { return getOnlines().contains(n) || getOfflines().contains(n); } @Override public boolean contains(VirtualMachine vm) { return getRunnings().contains(vm) || getWaitings().contains(vm) || getSleepings().contains(vm); } }