/** * 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.Arrays; import java.util.List; import org.inria.myriads.snoozecommon.communication.rest.api.LocalControllerAPI; import org.inria.myriads.snoozecommon.communication.virtualcluster.VirtualMachineMetaData; import org.inria.myriads.snoozecommon.communication.virtualcluster.migration.MigrationRequest; import org.inria.myriads.snoozecommon.communication.virtualcluster.status.VirtualMachineErrorCode; import org.inria.myriads.snoozecommon.communication.virtualcluster.status.VirtualMachineStatus; import org.inria.myriads.snoozecommon.communication.virtualcluster.submission.VirtualMachineLocation; import org.inria.myriads.snoozecommon.communication.virtualcluster.submission.VirtualMachineSubmissionRequest; import org.inria.myriads.snoozecommon.communication.virtualcluster.submission.VirtualMachineSubmissionResponse; import org.inria.myriads.snoozecommon.communication.virtualmachine.ResizeRequest; import org.inria.myriads.snoozecommon.globals.Globals; import org.inria.myriads.snoozecommon.guard.Guard; import org.inria.myriads.snoozecommon.virtualmachineimage.VirtualMachineImage; import org.inria.myriads.snoozenode.configurator.energymanagement.enums.PowerSavingAction; import org.inria.myriads.snoozenode.util.ManagementUtils; import org.restlet.resource.Post; import org.restlet.resource.ServerResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Local controller resource. * * @author Eugen Feller */ public final class LocalControllerResource extends ServerResource implements LocalControllerAPI { /** Define the logger. */ private static final Logger log_ = LoggerFactory.getLogger(LocalControllerResource.class); /** Backend reference holder. */ private LocalControllerBackend backend_; /** * Constructor. */ public LocalControllerResource() { log_.debug("Starting local controller resource"); backend_ = (LocalControllerBackend) getApplication().getContext().getAttributes().get("backend"); } /** * Starts the virtual machines. * (called by the group manager) * * @param submissionRequest The submission request * @return true if everything ok, "false" otherwise */ @Override public VirtualMachineSubmissionResponse startVirtualMachines(VirtualMachineSubmissionRequest submissionRequest) { Guard.check(submissionRequest); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return null; } ArrayList<VirtualMachineMetaData> virtualMachines = submissionRequest.getVirtualMachineMetaData(); log_.debug(String.format("Received a request to start %d virtual machines", virtualMachines.size())); for (VirtualMachineMetaData virtualMachine : submissionRequest.getVirtualMachineMetaData()) { VirtualMachineLocation location = virtualMachine.getVirtualMachineLocation(); log_.debug(String.format("Starting virtual machine: %s", location.getVirtualMachineId())); boolean isImageReady = backend_.getImageManager().fetchImage(virtualMachine); if (!isImageReady) { log_.error("Failed to provision virtual machine on hypervisor!"); ManagementUtils.updateVirtualMachineMetaData(virtualMachine, VirtualMachineStatus.ERROR, VirtualMachineErrorCode.FAILED_TO_START_ON_HYPERVISOR); continue; } // generate context iso here. // boolean isContextualized = backend_.getVirtualMachineContextualizor().contextualize(virtualMachine); // fill the xml desc here (domain type ... os type ...) boolean isProvisioned = backend_.getVirtualMachineProvisioner().provision(virtualMachine); if (!isProvisioned) { log_.error("Failed to provision virtual machine on hypervisor!"); ManagementUtils.updateVirtualMachineMetaData(virtualMachine, VirtualMachineStatus.ERROR, VirtualMachineErrorCode.FAILED_TO_START_ON_HYPERVISOR); continue; } boolean isStarted = backend_.getVirtualMachineActuator().start(virtualMachine.getXmlRepresentation()); if (!isStarted) { log_.error("Failed to start virtual machine on hypervisor!"); ManagementUtils.updateVirtualMachineMetaData(virtualMachine, VirtualMachineStatus.ERROR, VirtualMachineErrorCode.FAILED_TO_START_ON_HYPERVISOR); continue; } log_.debug("Virtual machine started! Now starting the monitoring!"); isStarted = backend_.getVirtualMachineMonitoringService().start(virtualMachine); if (!isStarted) { log_.error("Failed to start virtual machine monitoring!"); ManagementUtils.updateVirtualMachineMetaData(virtualMachine, VirtualMachineStatus.ERROR, VirtualMachineErrorCode.FAILED_TO_START_MONITORING); continue; } } VirtualMachineSubmissionResponse submissionResponse = new VirtualMachineSubmissionResponse(); submissionResponse.setVirtualMachineMetaData(submissionRequest.getVirtualMachineMetaData()); return submissionResponse; } /** * Routine to migrate a virtual machine. * * @param migrationRequest The migration request * @return true if everything ok, false otherwise */ @Override public boolean migrateVirtualMachine(MigrationRequest migrationRequest) { Guard.check(migrationRequest); String virtualMachineId = migrationRequest.getSourceVirtualMachineLocation().getVirtualMachineId(); log_.debug(String.format("Received virtual machine %s migration request", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized!"); return false; } VirtualMachineMetaData virtualMachine = backend_.getRepository().getVirtualMachineMetaData(virtualMachineId); if (virtualMachine == null) { log_.debug("Failed to find the virtual machine on this local controller"); return false; } boolean isStopped = backend_.getVirtualMachineMonitoringService().stop(virtualMachineId); if (!isStopped) { log_.debug("Failed to stop the virtual machine monitoring!"); return isStopped; } boolean isReady = backend_.getImageManager().prepareMigration( migrationRequest, backend_.getNodeParameters().getImageRepositorySettings(), virtualMachine.getImage() ); if (!isReady) { log_.debug("Failed to prepare virtual machine migration"); backend_.getVirtualMachineMonitoringService().restart(virtualMachineId); return isReady; } boolean isMigrated = backend_.getVirtualMachineActuator().migrate(migrationRequest); if (!isMigrated) { log_.debug("Failed to migrate virtual machine!"); backend_.getVirtualMachineMonitoringService().restart(virtualMachineId); return isMigrated; } boolean isDropped = backend_.getRepository().dropVirtualMachineMetaData(virtualMachineId); if (!isDropped) { log_.debug("Failed to drop the virtual machine meta data!"); return isDropped; } boolean isDiskRemoved = backend_.getImageManager().removeDisk( virtualMachine.getImage(), backend_.getNodeParameters().getImageRepositorySettings() ); if (!isDiskRemoved) { log_.debug("Unable to remove the disk after the migration"); } return isMigrated; } /** * Routine to suspend a virtual machine on migration. * * @param virtualMachineId The virtual machine identifier * @return true if everything ok, "false" otherwise */ @Override public boolean suspendVirtualMachineOnMigration(String virtualMachineId) { log_.debug(String.format("Suspending virtual machine: %s on hypervisor as part of migration", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isSuspended = backend_.getVirtualMachineActuator().suspend(virtualMachineId); if (!isSuspended) { log_.error("Failed to suspend virtual machine!"); return false; } return true; } /** * Routine to suspend a virtual machine. * * @param virtualMachineId The virtual machine identifier * @return true if everything ok, "false" otherwise */ @Override public boolean suspendVirtualMachineOnRequest(String virtualMachineId) { log_.debug(String.format("Suspending virtual machine: %s on hypervisor as part of request", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isSuspended = backend_.getVirtualMachineMonitoringService().suspend(virtualMachineId); if (!isSuspended) { log_.error("Failed to suspend virtual machine monitoring!"); return false; } isSuspended = backend_.getVirtualMachineActuator().suspend(virtualMachineId); if (!isSuspended) { log_.error("Failed to suspend virtual machine!"); return false; } boolean isChanged = backend_.getRepository().changeVirtualMachineStatus(virtualMachineId, VirtualMachineStatus.PAUSED); if (!isChanged) { log_.error("Failed to change virtual machine status!"); return false; } return true; } /** * Routine to resume a virtual machine. * * @param virtualMachineId The virtual machine identifier * @return true if everything ok, "false" otherwise */ @Override public boolean resumeVirtualMachine(String virtualMachineId) { log_.debug(String.format("Resuming virtual machine: %s on hypervisor", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isResumed = backend_.getVirtualMachineActuator().resume(virtualMachineId); if (!isResumed) { log_.error("Failed to resume virtual machine monitoring!"); return false; } isResumed = backend_.getVirtualMachineMonitoringService().resume(virtualMachineId); if (!isResumed) { log_.error("Failed to resume virtual machine!"); return false; } boolean isChanged = backend_.getRepository().changeVirtualMachineStatus(virtualMachineId, VirtualMachineStatus.RUNNING); if (!isChanged) { log_.error("Failed to change virtual machine status!"); return false; } return true; } /** * Routine to shutdown a virtual machine. * * @param virtualMachineId The virtual machine identifier * @return true if everything ok, false otherwise */ @Override public boolean shutdownVirtualMachine(String virtualMachineId) { log_.debug(String.format("Shutdown virtual machine: %s on hypervisor", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isShutdown = backend_.getVirtualMachineActuator().shutdown(virtualMachineId); if (!isShutdown) { log_.error("Unable to shutdown the virtual machine"); return false; } boolean isChanged = backend_.getRepository().changeVirtualMachineStatus(virtualMachineId, VirtualMachineStatus.SHUTDOWN_PENDING); if (!isChanged) { log_.error("Failed to change virtual machine status!"); return false; } return true; } /** * Routine to reboot a virtual machine. * * @param virtualMachineId The virtual machine identifier * @return true if everything ok, false otherwise */ @Override public boolean rebootVirtualMachine(String virtualMachineId) { log_.debug(String.format("Reboot virtual machine: %s on hypervisor", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isRebooted = backend_.getVirtualMachineActuator().reboot(virtualMachineId); if (!isRebooted) { log_.error("Unable to reboot the virtual machine"); return false; } boolean isChanged = backend_.getRepository().changeVirtualMachineStatus(virtualMachineId, VirtualMachineStatus.RUNNING); if (!isChanged) { log_.error("Failed to change virtual machine status!"); return false; } return true; } /** * Routine to destroy a virtual machine. * * @param virtualMachineId The virtual machine identifier * @return true if everything ok, false otherwise */ @Override public boolean destroyVirtualMachine(String virtualMachineId) { log_.debug(String.format("Destroy virtual machine: %s on hypervisor", virtualMachineId)); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } VirtualMachineMetaData virtualMachine = backend_.getRepository().getVirtualMachineMetaData(virtualMachineId); if (virtualMachine == null) { log_.debug(String.format("Unable to find the virtual machine %s"), virtualMachineId); return false; } boolean isDestroyed = backend_.getVirtualMachineActuator().destroy(virtualMachineId); if (!isDestroyed) { log_.error("Unable to destroy the virtual machine"); return false; } log_.debug("Removing the disk"); VirtualMachineImage image = virtualMachine.getImage(); boolean isRemoved = backend_.getImageManager().removeDisk( image, backend_.getNodeParameters().getImageRepositorySettings()); if (!isRemoved) { log_.error("Unable to remove the local disk image"); return false; } boolean isChanged = backend_.getRepository().changeVirtualMachineStatus(virtualMachineId, VirtualMachineStatus.SHUTDOWN_PENDING); if (!isChanged) { log_.error("Failed to change virtual machine status!"); return false; } return true; } /** * Routine to suspend the local controller to ram. * * @return true if everything ok, false otherwise */ @Override public boolean suspendNodeToRam() { log_.debug("Received suspend to ram request"); boolean isSuspended = backend_.powerCycle(PowerSavingAction.suspendToRam); return isSuspended; } /** * Routine to suspend the local controller to disk. * * @return true if everything ok, false otherwise */ @Override public boolean suspendNodeToDisk() { log_.debug("Received suspend to disk request"); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isSuspended = backend_.powerCycle(PowerSavingAction.suspendToDisk); return isSuspended; } /** * Routine to suspend the local controller to disk. * * @return true if everything ok, false otherwise */ @Override public boolean suspendNodeToBoth() { log_.debug("Received suspend to both request"); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isSuspended = backend_.powerCycle(PowerSavingAction.suspendToBoth); return isSuspended; } /** * Routine to shutdown the host. * * @return true if everything ok, false otherwise */ @Override public boolean shutdownNode() { log_.debug("Received request to shutdown the host"); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } boolean isShutdown = backend_.powerCycle(PowerSavingAction.shutdown); return isShutdown; } /** * Check backend activity. * * @return true if active, false otherwise */ private boolean isBackendActive() { if (backend_ == null) { return false; } return true; } /** * Starts virtual machine monitoring. * * @param virtualMachineMetaData The virtual machine meta data * @return true if started, false otherwise */ @Override public boolean startVirtualMachineMonitoring(VirtualMachineMetaData virtualMachineMetaData) { log_.debug("Start virtual machine monitoring request arrived"); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return false; } return backend_.getVirtualMachineMonitoringService().start(virtualMachineMetaData); } /** * * Resizes a virtual machine. * * @param resizeRequest resize request * @return the new virtual machine meta data * */ public VirtualMachineMetaData resizeVirtualMachine(ResizeRequest resizeRequest) { log_.debug("Soft resize virtual machine request arrived"); if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return null; } String virtualMachineId = resizeRequest.getVirtualMachineLocation().getVirtualMachineId(); VirtualMachineMetaData virtualMachine = backend_.getRepository().getVirtualMachineMetaData(virtualMachineId); //compute resize capacity Double memory = resizeRequest.getResizedCapacity().get(Globals.MEMORY_UTILIZATION_INDEX); if (memory <= 0d) { memory = virtualMachine.getRequestedCapacity().get(Globals.MEMORY_UTILIZATION_INDEX); } Double vcpus = resizeRequest.getResizedCapacity().get(Globals.CPU_UTILIZATION_INDEX); if (vcpus <= 0d) { vcpus = virtualMachine.getRequestedCapacity().get(Globals.CPU_UTILIZATION_INDEX); } Double rx = resizeRequest.getResizedCapacity().get(Globals.NETWORK_RX_UTILIZATION_INDEX); if (rx <= 0d) { rx = virtualMachine.getRequestedCapacity().get(Globals.NETWORK_RX_UTILIZATION_INDEX); } Double tx = resizeRequest.getResizedCapacity().get(Globals.NETWORK_TX_UTILIZATION_INDEX); if (rx <= 0d) { tx = virtualMachine.getRequestedCapacity().get(Globals.NETWORK_TX_UTILIZATION_INDEX); } ArrayList<Double> requestedCapacity = new ArrayList<Double>(Arrays.asList(vcpus, memory, tx, rx)); boolean isResizedMem = backend_.getVirtualMachineActuator().setMemory(virtualMachineId, memory.longValue()); if (!isResizedMem) { return null; } boolean isResizedCpu = backend_.getVirtualMachineActuator().setVcpu(virtualMachineId, vcpus.intValue()); if (!isResizedCpu) { return null; } virtualMachine.setRequestedCapacity(requestedCapacity); return virtualMachine; } @Override public List<VirtualMachineMetaData> getVirtualMachines(int numberOfMonitoringEntries) { if (!isBackendActive()) { log_.warn("Backend is not initialized yet!"); return null; } List<VirtualMachineMetaData> virtualMachines = new ArrayList<VirtualMachineMetaData>(); virtualMachines = backend_.getRepository().getVirtualMachines(numberOfMonitoringEntries); return virtualMachines; } @Override public boolean prepareMigration(VirtualMachineImage virtualMachineImage) { boolean isPrepared = backend_.getImageManager().prepareMigration(virtualMachineImage); return isPrepared; } }