/** * Copyright (C) 2010-2013 Eugen Feller, INRIA <eugen.feller@inria.fr> * * This file is part of Snooze, a scalable, autonomic, and * energy-aware virtual machine (VM) management framework. * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses>. */ package org.inria.myriads.snoozenode.groupmanager.energysaver.saver; import java.util.ArrayList; import java.util.List; import org.inria.myriads.snoozecommon.communication.localcontroller.LocalControllerDescription; import org.inria.myriads.snoozecommon.util.TimeUtils; import org.inria.myriads.snoozenode.configurator.energymanagement.EnergyManagementSettings; import org.inria.myriads.snoozenode.database.api.GroupManagerRepository; import org.inria.myriads.snoozenode.groupmanager.statemachine.api.StateMachine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements the energy saving logic. * * @author Eugen Feller */ public final class EnergySaver implements Runnable { /** Define the logger. */ private static final Logger log_ = LoggerFactory.getLogger(EnergySaver.class); /** Number of monitoring entries. */ private static int NUMBER_OF_MONITORING_ENTRIES; /** Energy savings. */ private EnergyManagementSettings energySettings_; /** Repository. */ private GroupManagerRepository repository_; /** State machine. */ private StateMachine stateMachine_; /** Lock object. */ private Object lockObject_; /** Used to suspend the saver. */ private boolean isSuspended_; /** Terminated. */ private boolean isTerminated_; /** * Energy saver constructor. * * @param energySettings The energy settings * @param repository The group manager repository * @param stateMachine The state machine */ public EnergySaver(EnergyManagementSettings energySettings, GroupManagerRepository repository, StateMachine stateMachine) { log_.debug("Initializing the energy saver!"); energySettings_ = energySettings; repository_ = repository; stateMachine_ = stateMachine; lockObject_ = new Object(); } /** Run. */ public void run() { int idleTimeThreshold = energySettings_.getThresholds().getIdleTime(); List<LocalControllerDescription> localControllers; try { while (true) { log_.debug(String.format("Waiting for: %s seconds", idleTimeThreshold)); localControllers = repository_.getLocalControllerDescriptions(NUMBER_OF_MONITORING_ENTRIES, true, true); synchronized (lockObject_) { lockObject_.wait(TimeUtils.convertSecondsToMilliseconds(idleTimeThreshold)); } if (isTerminated_) { break; } suspend(); if (stateMachine_.isBusy()) { log_.debug("System is BUSY! Skipping energy savings!"); continue; } int numberOfReservedNodes = energySettings_.getNumberOfReservedNodes(); List<LocalControllerDescription> idleResources = getIdleLocalControllers(localControllers, numberOfReservedNodes); int numberOfIdleNodes = idleResources.size(); log_.debug(String.format("Number of local controllers to power cycle: %d", numberOfIdleNodes)); if (numberOfIdleNodes == 0) { log_.debug("Not enough idle resources to perform energy savings!"); continue; } stateMachine_.onEnergySavingsEnabled(idleResources); } } catch (Exception exception) { log_.error("Energy saver was interrupted", exception); } log_.debug("Energy saver is stopped!"); } /** * Terminate routine. */ public void terminate() { log_.debug("Terminating the energy saver"); isTerminated_ = true; synchronized (lockObject_) { lockObject_.notify(); } } /** * Can be used to suspend the saver. */ public void setSuspend() { isSuspended_ = true; } /** * Wakeup the saver. */ public void wakeup() { isSuspended_ = false; synchronized (lockObject_) { lockObject_.notify(); } } /** * Returns the number of virtual machines. * * @param localControllerId The local controller identifier * @param localControllers The list of local controllers * @return The number of virtual machines */ private int getNumberOfVirtualMachines(String localControllerId, List<LocalControllerDescription> localControllers) { int numberOfVirtualMachines = 0; for (LocalControllerDescription localController : localControllers) { if (localController.getId().equals(localControllerId)) { return localController.getVirtualMachineMetaData().size(); } } return numberOfVirtualMachines; } /** * Computes a list of idle resources. * * @param oldLocalControllers The old list of local controllers * @param numberOfReservedNodes The number of reserved nodes * @return The list of idle local controllers */ private List<LocalControllerDescription> getIdleLocalControllers(List<LocalControllerDescription> oldLocalControllers, int numberOfReservedNodes) { log_.debug("Computing list of idle local controllers!"); List<LocalControllerDescription> idleLocalControllers = new ArrayList<LocalControllerDescription>(); List<LocalControllerDescription> localControllers = repository_.getLocalControllerDescriptions(NUMBER_OF_MONITORING_ENTRIES, true, true); if (localControllers.size() <= numberOfReservedNodes) { log_.debug("Number of active local controllers is less/equal the number of reserved nodes!"); return idleLocalControllers; } if (oldLocalControllers.size() != localControllers.size()) { log_.debug(String.format("Number of local controllers does not match! Old one: %d, new one: %d!", oldLocalControllers.size(), localControllers.size())); return idleLocalControllers; } for (LocalControllerDescription localController : localControllers) { String localControllerId = localController.getId(); int oldNumberOfVirtualMachines = getNumberOfVirtualMachines(localControllerId, oldLocalControllers); int newNumberOfVirtualMachines = localController.getVirtualMachineMetaData().size(); boolean isEqual = oldNumberOfVirtualMachines == 0 && newNumberOfVirtualMachines == 0; if (!isEqual) { log_.debug(String.format("Local controller %s is BUSY! Old and new number of VMs are: %d / %d", localControllerId, oldNumberOfVirtualMachines, newNumberOfVirtualMachines)); continue; } log_.debug(String.format("Local controller: %s is IDLE", localControllerId)); idleLocalControllers.add(localController); } /* * Check if enough active nodes will be online after power cycling idle LCs! If yes all idle detected LCs * can be power cycled!(e.g. 10 active, 5 idle, 3 reserved => Shutdown all idle) */ int numberOfActiveLocalControllers = localControllers.size() - idleLocalControllers.size(); boolean isAllowed = numberOfActiveLocalControllers >= numberOfReservedNodes; if (isAllowed) { return idleLocalControllers; } /* * If newNumberOfLocalControllers < numberOfReservedNodes * we can only power cycle numberOfIdleLocalControllers - * numberOfReserved LCs! Hence, remove some LCs from the * idle list! (e.g. 10 active, 8 idle, 3 reserved => Shutdown * 5 only) */ for (int i = 0; i < numberOfReservedNodes; i++) { log_.debug(String.format("Removing reserved node: %d from idle nodes: %d", i, idleLocalControllers.size())); idleLocalControllers.remove(0); } return idleLocalControllers; } /** * Suspends the saver. * * @throws InterruptedException The interrupted exception */ private void suspend() throws InterruptedException { if (isSuspended_) { log_.debug("Energy saver suspending"); synchronized (lockObject_) { lockObject_.wait(); } log_.debug("Energy saver waking up"); } } }