/** * 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.localcontroller; import java.util.ArrayList; import java.util.HashMap; import org.inria.myriads.snoozecommon.communication.NetworkAddress; import org.inria.myriads.snoozecommon.communication.groupmanager.GroupManagerDescription; import org.inria.myriads.snoozecommon.communication.localcontroller.AssignedGroupManager; import org.inria.myriads.snoozecommon.communication.localcontroller.LocalControllerDescription; import org.inria.myriads.snoozecommon.communication.localcontroller.LocalControllerLocation; import org.inria.myriads.snoozecommon.communication.localcontroller.hypervisor.HypervisorSettings; import org.inria.myriads.snoozecommon.communication.rest.CommunicatorFactory; import org.inria.myriads.snoozecommon.communication.rest.api.GroupManagerAPI; import org.inria.myriads.snoozecommon.communication.virtualcluster.VirtualMachineMetaData; import org.inria.myriads.snoozecommon.communication.virtualcluster.monitoring.NetworkDemand; import org.inria.myriads.snoozecommon.guard.Guard; import org.inria.myriads.snoozenode.configurator.api.NodeConfiguration; import org.inria.myriads.snoozenode.configurator.energymanagement.enums.PowerSavingAction; import org.inria.myriads.snoozenode.configurator.energymanagement.enums.ShutdownDriver; import org.inria.myriads.snoozenode.configurator.energymanagement.enums.SuspendDriver; import org.inria.myriads.snoozenode.configurator.imagerepository.ImageRepositorySettings; import org.inria.myriads.snoozenode.configurator.monitoring.external.ExternalNotifierSettings; import org.inria.myriads.snoozenode.configurator.provisioner.ProvisionerSettings; import org.inria.myriads.snoozenode.database.DatabaseFactory; import org.inria.myriads.snoozenode.database.api.LocalControllerRepository; import org.inria.myriads.snoozenode.database.enums.DatabaseType; import org.inria.myriads.snoozenode.exception.HostMonitoringException; import org.inria.myriads.snoozenode.exception.VirtualMachineMonitoringException; import org.inria.myriads.snoozenode.executor.ShellCommandExecuter; import org.inria.myriads.snoozenode.heartbeat.HeartbeatFactory; import org.inria.myriads.snoozenode.heartbeat.discovery.GroupLeaderDiscovery; import org.inria.myriads.snoozenode.heartbeat.listener.GroupLeaderHeartbeatArrivalListener; import org.inria.myriads.snoozenode.heartbeat.listener.GroupManagerHeartbeatFailureListener; import org.inria.myriads.snoozenode.localcontroller.actuator.ActuatorFactory; import org.inria.myriads.snoozenode.localcontroller.actuator.api.VirtualMachineActuator; import org.inria.myriads.snoozenode.localcontroller.connector.Connector; import org.inria.myriads.snoozenode.localcontroller.imagemanager.ImageManagerFactory; import org.inria.myriads.snoozenode.localcontroller.imagemanager.api.ImageManager; import org.inria.myriads.snoozenode.localcontroller.monitoring.MonitoringFactory; import org.inria.myriads.snoozenode.localcontroller.monitoring.api.HostMonitor; import org.inria.myriads.snoozenode.localcontroller.monitoring.api.VirtualMachineMonitor; import org.inria.myriads.snoozenode.localcontroller.monitoring.service.InfrastructureMonitoring; import org.inria.myriads.snoozenode.localcontroller.monitoring.service.VirtualMachineMonitoringService; import org.inria.myriads.snoozenode.localcontroller.powermanagement.PowerManagementFactory; import org.inria.myriads.snoozenode.localcontroller.powermanagement.shutdown.Shutdown; import org.inria.myriads.snoozenode.localcontroller.powermanagement.suspend.Suspend; import org.inria.myriads.snoozenode.localcontroller.provisioner.VirtualMachineProvisionerFactory; import org.inria.myriads.snoozenode.localcontroller.provisioner.api.VirtualMachineProvisioner; import org.inria.myriads.snoozenode.message.SystemMessage; import org.inria.myriads.snoozenode.message.SystemMessageType; import org.inria.myriads.snoozenode.util.ExternalNotifierUtils; import org.inria.myriads.snoozenode.util.ManagementUtils; import org.inria.snoozenode.external.notifier.ExternalNotificationType; import org.inria.snoozenode.external.notifier.ExternalNotifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Local controller backend. * * @author Eugen Feller */ public final class LocalControllerBackend implements GroupLeaderHeartbeatArrivalListener, GroupManagerHeartbeatFailureListener { /** Define the logger. */ private static final Logger log_ = LoggerFactory.getLogger(LocalControllerBackend.class); /** Local actuator. */ private VirtualMachineActuator virtualMachineActuator_; /** Monitoring management of virtual machines. */ private VirtualMachineMonitoringService virtualMachineMonitoringService_; /** Node parameters. */ private NodeConfiguration nodeConfiguration_; /** Group leader repository. */ private LocalControllerRepository localControllerRepository_; /** Controller description. */ private LocalControllerDescription localControllerDescription_; /** Resource monitoring .*/ private InfrastructureMonitoring resourceMonitoring_; /** Shutdown logic. */ private Shutdown shutdownLogic_; /** Suspend logic. */ private Suspend suspendLogic_; /** External Notifier. */ private ExternalNotifier externalNotifier_; /** VirtualMachine Provisioner. */ private VirtualMachineProvisioner virtualMachineProvisioner_; /** Image manager.*/ private ImageManager imageManager_; /** * Constructor. * * @param configuration The node configuration * @throws Exception The exception */ public LocalControllerBackend(NodeConfiguration configuration) throws Exception { Guard.check(configuration); log_.debug("Initializing the local controller backend"); nodeConfiguration_ = configuration; initializeExternalNotifier(); initializeDatabase(); initializePowerManagement(); initializeProvisioner(); initializeImageManager(); startHypervisorServices(); createLocalControllerDescription(); onGroupManagerHeartbeatFailure(); } /** * Initializes the image manager. */ private void initializeImageManager() { ImageRepositorySettings settings = nodeConfiguration_.getImageRepositorySettings(); imageManager_ = ImageManagerFactory.newImageManager(settings); } /** * Initializes the image provisioner. */ private void initializeProvisioner() { HypervisorSettings hypervisorSettings = nodeConfiguration_.getHypervisor(); ImageRepositorySettings imageSettings = nodeConfiguration_.getImageRepositorySettings(); ProvisionerSettings provisionerSettings = nodeConfiguration_.getProvisionerSettings(); virtualMachineProvisioner_ = VirtualMachineProvisionerFactory.newProvisioner( provisionerSettings, hypervisorSettings, imageSettings); } /** * Initializes the external notifier. * (should be static...) */ private void initializeExternalNotifier() { externalNotifier_ = new ExternalNotifier(nodeConfiguration_); } /** * Initializes the database. */ private void initializeDatabase() { DatabaseType type = nodeConfiguration_.getDatabase().getType(); ExternalNotifierSettings externalNotifierSettings = nodeConfiguration_.getExternalNotifier(); localControllerRepository_ = DatabaseFactory.newLocalControllerRepository(type, externalNotifier_); } /** * Initializes the power management. */ private void initializePowerManagement() { int commandExecutionTimeOut = nodeConfiguration_.getEnergyManagement().getCommandExecutionTimeout(); ShellCommandExecuter executer = new ShellCommandExecuter(commandExecutionTimeOut); SuspendDriver suspendDriver = nodeConfiguration_.getEnergyManagement().getDrivers().getSuspend(); suspendLogic_ = PowerManagementFactory.newSuspendLogic(suspendDriver, executer); ShutdownDriver shutdownDriver = nodeConfiguration_.getEnergyManagement().getDrivers().getShutdown(); shutdownLogic_ = PowerManagementFactory.newShutdownLogic(shutdownDriver, executer); } /** * Initializes the hypervisor related services. * * @throws Exception The exception */ private void startHypervisorServices() throws Exception { log_.debug("Initializing the hypervisor services"); String address = nodeConfiguration_.getNetworking().getListen().getControlDataAddress().getAddress(); HypervisorSettings settings = nodeConfiguration_.getHypervisor(); Connector connector = ActuatorFactory.newHypervisorConnector(address, settings); virtualMachineActuator_ = ActuatorFactory.newVirtualMachineActuator(connector); createInfrastructureMonitor(connector); } /** * Creates infrastructure monitor. * * @param connector The connector * @throws HostMonitoringException The host monitoring exception * @throws VirtualMachineMonitoringException The virtual machine monitoring exception */ private void createInfrastructureMonitor(Connector connector) throws HostMonitoringException, VirtualMachineMonitoringException { NetworkDemand networkCapacity = nodeConfiguration_.getNode().getNetworkCapacity(); HostMonitor hostMonitor = MonitoringFactory.newHostMonitoring(connector, networkCapacity); VirtualMachineMonitor virtualMachineMonitor = MonitoringFactory.newVirtualMachineMonitor(connector); resourceMonitoring_ = new InfrastructureMonitoring(virtualMachineMonitor, hostMonitor, nodeConfiguration_.getMonitoring(), nodeConfiguration_.getExternalNotifier()); } /** * Creates the local controller description. * * @throws HostMonitoringException The host monitoring exception */ private void createLocalControllerDescription() throws HostMonitoringException { ArrayList<Double> totalCapacity = resourceMonitoring_.getHostMonitor().getTotalCapacity(); localControllerDescription_ = ManagementUtils.createLocalController(nodeConfiguration_, totalCapacity); } /** * Starts the virtual machine monitoring service. * * @param groupManager The group manager description * @throws Exception The exception */ private void startVirtualMachineMonitoringService(GroupManagerDescription groupManager) throws Exception { if (virtualMachineMonitoringService_ == null) { virtualMachineMonitoringService_ = new VirtualMachineMonitoringService(localControllerDescription_, localControllerRepository_, resourceMonitoring_, nodeConfiguration_.getDatabase() ); } virtualMachineMonitoringService_.startService(groupManager.getListenSettings().getMonitoringDataAddress()); } /** * Initializes the group leader discovery. * * @throws Exception The exception */ @Override public void onGroupManagerHeartbeatFailure() throws Exception { log_.debug("Initializing the group leader discovery"); if (virtualMachineMonitoringService_ != null) { virtualMachineMonitoringService_.stopService(); } ExternalNotifierUtils.send( externalNotifier_, ExternalNotificationType.SYSTEM, new SystemMessage(SystemMessageType.GM_FAILED, localControllerDescription_), "localcontroller." + localControllerDescription_.getId()); new GroupLeaderDiscovery(nodeConfiguration_.getNetworking().getMulticast().getGroupLeaderHeartbeatAddress(), nodeConfiguration_.getFaultTolerance().getHeartbeat().getTimeout(), this); } /** * Called upon group leader heartbeat arrival. * * @param groupLeaderDescription The group leader description * @return true if everything ok, false otherwise * @throws Exception The exception */ @Override public synchronized boolean onGroupLeaderHeartbeatArrival(GroupManagerDescription groupLeaderDescription) throws Exception { log_.debug("Starting the join procedure"); AssignedGroupManager assignment = assignLocalController(groupLeaderDescription); if (assignment == null) { log_.debug("Unable to assign the local controller! No group manager available yet?"); return false; } GroupManagerDescription groupManager = assignment.getGroupManager(); if (groupManager == null) { log_.debug("No group manager description available in the assignment!"); return false; } String localControllerId = assignment.getLocalControllerId(); if (localControllerId != null) { log_.debug("Updating local controller identifier!"); localControllerDescription_.setId(localControllerId); } updateRepositoryInformation(groupManager); boolean hasJoined = joinGroupManager(groupManager); if (!hasJoined) { log_.error("Unable to join the assigned group manager!"); return false; } startSystemServices(groupManager); return true; } /** * Updates the repository information. * * @param groupManagerDescription The group manager description */ private void updateRepositoryInformation(GroupManagerDescription groupManagerDescription) { log_.debug("Updating the virtual machine meta data information with new group manager description"); HashMap<String, VirtualMachineMetaData> metaData = localControllerRepository_.updateVirtualMachineMetaData(groupManagerDescription); localControllerDescription_.setVirtualMachineMetaData(metaData); log_.debug(String.format("Update local controller location with : \n" + "localControllerId : %s \n" + "groupManagerId : %s \n" + "groupManagerAddress : %s \n", localControllerDescription_.getId(), groupManagerDescription.getId(), groupManagerDescription.getListenSettings().getControlDataAddress().toString() )); //update also localcontroller location. LocalControllerLocation location = new LocalControllerLocation(); location.setLocalControllerId(localControllerDescription_.getId()); location.setGroupManagerId(groupManagerDescription.getId()); location.setGroupManagerControlDataAddress(groupManagerDescription.getListenSettings().getControlDataAddress()); localControllerDescription_.setLocation(location); } /** * Starts the system services. * * @param groupManager The group manager description * @throws Exception The exception */ private void startSystemServices(GroupManagerDescription groupManager) throws Exception { log_.debug("Starting the system services"); int heartbeatTimeout = nodeConfiguration_.getFaultTolerance().getHeartbeat().getTimeout(); HeartbeatFactory.newGroupManagerHeartbeatHandler(groupManager.getHeartbeatAddress(), groupManager.getId(), heartbeatTimeout, this); startVirtualMachineMonitoringService(groupManager); } /** * Try to get a group manager assigned. * * @param groupLeader The group leader description * @return The local controller assignment */ private AssignedGroupManager assignLocalController(GroupManagerDescription groupLeader) { log_.debug("Assigninng the local controller to a group manager"); NetworkAddress monitoringAddress = groupLeader.getListenSettings().getControlDataAddress(); GroupManagerAPI communicator = CommunicatorFactory.newGroupManagerCommunicator(monitoringAddress); AssignedGroupManager assignment = communicator.assignLocalController(localControllerDescription_); return assignment; } /** * Join the group manager. * * @param groupManager The group manager description * @return true if everything ok, false otherwise */ private boolean joinGroupManager(GroupManagerDescription groupManager) { log_.debug("Joining the assigned group manager"); NetworkAddress monitoringAddress = groupManager.getListenSettings().getControlDataAddress(); GroupManagerAPI communicator = CommunicatorFactory.newGroupManagerCommunicator(monitoringAddress); boolean hasJoined = communicator.joinGroupManager(localControllerDescription_); return hasJoined; } /** * Returns the local actuator. * * @return The virtual machine actuator */ public VirtualMachineActuator getVirtualMachineActuator() { return virtualMachineActuator_; } /** * Returns the local monitor. * * @return The virtual machine monitoring service */ public VirtualMachineMonitoringService getVirtualMachineMonitoringService() { return virtualMachineMonitoringService_; } /** * Returns the node parameters. * * @return The node parameters */ public NodeConfiguration getNodeParameters() { return nodeConfiguration_; } /** * Returns the local controller repository. * * @return The local controller repository */ public LocalControllerRepository getRepository() { return localControllerRepository_; } /** * Returns the local controller description. * * @return The local controller description */ public LocalControllerDescription getLocalControllerDescription() { return localControllerDescription_; } /** * Power cycles the local controller. * * @param powerSavingAction The power saving action * @return true if everything ok, false otherwise */ public boolean powerCycle(PowerSavingAction powerSavingAction) { boolean isPowerCycled = false; ExternalNotifierUtils.send( externalNotifier_, ExternalNotificationType.SYSTEM, new SystemMessage(SystemMessageType.ENERGY, localControllerDescription_), "localcontroller." + localControllerDescription_.getId()); switch (powerSavingAction) { case suspendToRam: isPowerCycled = suspendLogic_.suspendToRam(); break; case suspendToDisk: isPowerCycled = suspendLogic_.suspendToDisk(); break; case suspendToBoth: isPowerCycled = suspendLogic_.suspendToBoth(); break; case shutdown: isPowerCycled = shutdownLogic_.shutdown(); break; default: log_.error(String.format("This power cycling action is not supported: %s", powerSavingAction)); } return isPowerCycled; } /** * * Gets the virtual machine provisioner. * * @return The virtual machine provisioner. */ public VirtualMachineProvisioner getVirtualMachineProvisioner() { return virtualMachineProvisioner_; } /** * * Gets the image manager. * * @return The image manager. */ public ImageManager getImageManager() { return imageManager_; } }