/* * 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.configuration; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; /** * Default implementation of Configuration. * * @author Fabien Hermenier */ public class SimpleConfiguration implements Configuration, Cloneable { private static final int IDX_SHIFT = -1; private static final int RUNNINGS = 1; private static final int SLEEPINGS = 2; private static final int WAITINGS = 3; private static final int ONLINES = 1; private static final int OFFLINES = 2; private ManagedElementSet<Node> allNodes; private ManagedElementSet<VirtualMachine> allVMs; private ManagedElementSet<Node>[] nodesByState; private ManagedElementSet<VirtualMachine>[] vmsByState; private TIntIntHashMap vmState; private TIntIntHashMap nodeState; private TIntObjectHashMap<Node> vmPlace; private TIntObjectHashMap<ManagedElementSet<VirtualMachine>>[] hosted; /** * Build an empty configuration. */ public SimpleConfiguration() { this.vmsByState = new ManagedElementSet[3]; this.vmState = new TIntIntHashMap(); this.nodeState = new TIntIntHashMap(); nodesByState = new ManagedElementSet[2]; for (int i = 0; i < nodesByState.length; i++) { nodesByState[i] = new SimpleManagedElementSet<Node>(); } for (int i = 0; i < vmsByState.length; i++) { vmsByState[i] = new SimpleManagedElementSet<VirtualMachine>(); } this.hosted = new TIntObjectHashMap[2]; for (int i = 0; i < hosted.length; i++) { this.hosted[i] = new TIntObjectHashMap<ManagedElementSet<VirtualMachine>>(); } this.allNodes = new SimpleManagedElementSet<Node>(); this.allVMs = new SimpleManagedElementSet<VirtualMachine>(); this.vmPlace = new TIntObjectHashMap<Node>(); } private boolean switchState(Node n, int newState) { int curState = nodeState.get(n.hashCode()); if (curState == newState) { return true; } if (curState > 0) { nodesByState[curState + IDX_SHIFT].remove(n); } nodeState.put(n.hashCode(), newState); return nodesByState[newState + IDX_SHIFT].add(n); } private boolean replace(VirtualMachine vm, Node newNode, int newState) { Node oldNode = vmPlace.put(vm.hashCode(), newNode); int oldState = vmState.get(vm.hashCode()); if (oldState == 0) { //Unknown VM allVMs.add(vm); } if (oldState == 0 || (oldState != newState)) { if (oldState != 0) { vmsByState[oldState + IDX_SHIFT].remove(vm); } vmsByState[newState + IDX_SHIFT].add(vm); vmState.put(vm.hashCode(), newState); } //Change the state if (oldNode != null) { hosted[oldState + IDX_SHIFT].get(oldNode.hashCode()).remove(vm); } hosted[newState + IDX_SHIFT].get(newNode.hashCode()).add(vm); return true; } @Override public boolean setRunOn(VirtualMachine vm, Node node) { if (nodeState.get(node.hashCode()) == ONLINES) { return replace(vm, node, RUNNINGS); } return false; } @Override public boolean setSleepOn(VirtualMachine vm, Node node) { if (nodeState.get(node.hashCode()) == ONLINES) { return replace(vm, node, SLEEPINGS); } return false; } @Override public void addWaiting(VirtualMachine vm) { int curState = vmState.get(vm.hashCode()); if (curState == 0) { //Not in the configuration. allVMs.add(vm); vmState.put(vm.hashCode(), WAITINGS); vmsByState[WAITINGS + IDX_SHIFT].add(vm); } if (curState > 0 && (curState + IDX_SHIFT != WAITINGS)) { //Already in and non-waiting //Change state Node oldNode = vmPlace.remove(vm.hashCode()); vmsByState[curState + IDX_SHIFT].remove(vm); vmsByState[WAITINGS + IDX_SHIFT].add(vm); vmState.put(vm.hashCode(), WAITINGS); //Change hoster hosted[curState + IDX_SHIFT].get(oldNode.hashCode()).remove(vm); } } @Override public void remove(VirtualMachine vm) { int curState = vmState.remove(vm.hashCode()); if (curState > 0) { vmsByState[curState + IDX_SHIFT].remove(vm); allVMs.remove(vm); Node oldNode = vmPlace.remove(vm.hashCode()); if (oldNode != null) { hosted[curState + IDX_SHIFT].get(oldNode.hashCode()).remove(vm); } } } @Override public boolean remove(Node n) { int curState = nodeState.remove(n.hashCode()); if (curState > 0) { if (isUsed(n)) { return false; } for (int i = 0; i < hosted.length; i++) { hosted[i].remove(n.hashCode()); } nodesByState[curState + IDX_SHIFT].remove(n); } return true; } @Override public void addOnline(Node n) { if (nodeState.get(n.hashCode()) != ONLINES) { hosted[RUNNINGS + IDX_SHIFT].put(n.hashCode(), new SimpleManagedElementSet<VirtualMachine>()); hosted[SLEEPINGS + IDX_SHIFT].put(n.hashCode(), new SimpleManagedElementSet<VirtualMachine>()); } this.allNodes.add(n); switchState(n, ONLINES); } /** * Check whether a node is hosting a virtual machine or not. * * @param n the node to check * @return {@code true} if the node host running or sleeping virtual machines. */ private boolean isUsed(Node n) { for (int i = 0; i < hosted.length; i++) { ManagedElementSet<VirtualMachine> s = hosted[i].get(n.hashCode()); if (s != null && s.size() > 0) { return true; } } return false; } @Override public boolean addOffline(Node n) { if (isUsed(n)) { return false; } hosted[RUNNINGS + IDX_SHIFT].put(n.hashCode(), new SimpleManagedElementSet<VirtualMachine>()); hosted[SLEEPINGS + IDX_SHIFT].put(n.hashCode(), new SimpleManagedElementSet<VirtualMachine>()); allNodes.add(n); return switchState(n, OFFLINES); } @Override public ManagedElementSet<Node> getOnlines() { return nodesByState[ONLINES + IDX_SHIFT]; } @Override public ManagedElementSet<Node> getOfflines() { return nodesByState[OFFLINES + IDX_SHIFT]; } @Override public ManagedElementSet<VirtualMachine> getRunnings() { return vmsByState[RUNNINGS + IDX_SHIFT]; } @Override public ManagedElementSet<VirtualMachine> getSleepings() { return vmsByState[SLEEPINGS + IDX_SHIFT]; } @Override public ManagedElementSet<VirtualMachine> getWaitings() { return vmsByState[WAITINGS + IDX_SHIFT]; } @Override public ManagedElementSet<VirtualMachine> getSleepings(Node n) { return hosted[SLEEPINGS + IDX_SHIFT].get(n.hashCode()); } @Override public ManagedElementSet<VirtualMachine> getRunnings(Node n) { return hosted[RUNNINGS + IDX_SHIFT].get(n.hashCode()); } @Override public ManagedElementSet<VirtualMachine> getRunnings(ManagedElementSet<Node> ns) { ManagedElementSet<VirtualMachine> vms = new SimpleManagedElementSet<VirtualMachine>(); for (Node n : ns) { vms.addAll(getRunnings(n)); } return vms; } @Override public ManagedElementSet<VirtualMachine> getAllVirtualMachines() { return allVMs; } @Override public ManagedElementSet<Node> getAllNodes() { return allNodes; } @Override public Node getSleepingLocation(VirtualMachine vm) { throw new UnsupportedOperationException(); } @Override public Node getRunningLocation(VirtualMachine vm) { throw new UnsupportedOperationException(); } @Override public Node getLocation(VirtualMachine vm) { return vmPlace.get(vm.hashCode()); } @Override public boolean isOnline(Node n) { return nodeState.get(n.hashCode()) == ONLINES; } @Override public boolean isOffline(Node n) { return nodeState.get(n.hashCode()) == OFFLINES; } @Override public boolean isRunning(VirtualMachine vm) { return vmState.get(vm.hashCode()) == RUNNINGS; } @Override public boolean isWaiting(VirtualMachine vm) { return vmState.get(vm.hashCode()) == WAITINGS; } @Override public boolean isSleeping(VirtualMachine vm) { return vmState.get(vm.hashCode()) == SLEEPINGS; } @Override public Configuration clone() { final SimpleConfiguration c = new SimpleConfiguration(); for (Node n : getOfflines()) { c.addOffline(n); } for (VirtualMachine vm : getWaitings()) { c.addWaiting(vm); } for (Node n : getOnlines()) { c.addOnline(n); for (VirtualMachine vm : getRunnings(n)) { c.setRunOn(vm, n); } for (VirtualMachine vm : getSleepings(n)) { c.setSleepOn(vm, n); } } return c; } @Override public boolean contains(Node n) { return nodeState.get(n.hashCode()) > 0; } @Override public boolean contains(VirtualMachine vm) { return vmState.get(vm.hashCode()) > 0; } @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; if (!ref.getOfflines().equals(getOfflines()) || !ref.getOnlines().equals(getOnlines()) || !ref.getWaitings().equals(getWaitings())) { return false; } for (Node n : ref.getOnlines()) { if (!ref.getRunnings(n).equals(getRunnings(n)) || !ref.getSleepings(n).equals(getSleepings(n))) { return false; } } return true; } return false; } /** * Textual representation of the configuration. * * @return the textual representation */ @Override public String toString() { StringBuilder buf = new StringBuilder(); for (Node n : allNodes) { if (nodeState.get(n.hashCode()) == OFFLINES) { buf.append("(").append(n.getName()).append(")"); } else { buf.append(n.getName()); } buf.append(":"); for (VirtualMachine vm : this.getRunnings(n)) { buf.append(" "); buf.append(vm.getName()); } for (VirtualMachine vm : this.getSleepings(n)) { buf.append(" ("); buf.append(vm.getName()); buf.append(")"); } buf.append("\n"); } buf.append("FARM"); for (VirtualMachine vm : this.getWaitings()) { buf.append(" "); buf.append(vm.getName()); } buf.append("\n"); return buf.toString(); } }