/** * Abiquo community edition * cloud management application for hybrid clouds * Copyright (C) 2008-2010 - Abiquo Holdings S.L. * * This application 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 under * version 3 of the License * * This software 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 v.3 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package com.abiquo.api.services.stub; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.exceptions.APIException; import com.abiquo.api.exceptions.NotFoundException; import com.abiquo.api.services.DefaultApiService; import com.abiquo.api.services.InfrastructureService; import com.abiquo.api.services.RemoteServiceService; import com.abiquo.api.services.TaskService; import com.abiquo.api.services.UserService; import com.abiquo.api.tasks.util.DatacenterTaskBuilder; import com.abiquo.api.tracer.TracerLogger; import com.abiquo.api.util.snapshot.SnapshotUtils; import com.abiquo.api.util.snapshot.SnapshotUtils.SnapshotType; import com.abiquo.commons.amqp.impl.tarantino.TarantinoRequestProducer; import com.abiquo.commons.amqp.impl.tarantino.domain.DiskSnapshot; import com.abiquo.commons.amqp.impl.tarantino.domain.HypervisorConnection; import com.abiquo.commons.amqp.impl.tarantino.domain.VirtualMachineDefinition; import com.abiquo.commons.amqp.impl.tarantino.domain.builder.VirtualMachineDescriptionBuilder; import com.abiquo.commons.amqp.impl.tarantino.domain.dto.DatacenterTasks; import com.abiquo.model.enumerator.RemoteServiceType; import com.abiquo.server.core.appslibrary.VirtualMachineTemplate; import com.abiquo.server.core.cloud.Hypervisor; import com.abiquo.server.core.cloud.VirtualAppliance; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.cloud.VirtualMachineStateTransition; import com.abiquo.server.core.infrastructure.Datacenter; import com.abiquo.server.core.infrastructure.RemoteService; import com.abiquo.server.core.task.Task; import com.abiquo.server.core.task.enums.TaskType; import com.abiquo.tracer.ComponentType; import com.abiquo.tracer.EventType; import com.abiquo.tracer.SeverityType; /** * Utility methods to send jobs to Tarantino. * * @author Ignasi Barrera */ @Service public class TarantinoService extends DefaultApiService { /** The logger object **/ private final static Logger logger = LoggerFactory.getLogger(TarantinoService.class); @Autowired protected RemoteServiceService remoteServiceService; @Autowired protected VsmServiceStub vsm; @Autowired protected TarantinoJobCreator jobCreator; @Autowired protected UserService userService; @Autowired protected TaskService taskService; @Autowired protected InfrastructureService infrastructureService; public TarantinoService() { } public TarantinoService(final EntityManager em) { this.tracer = new TracerLogger(); // TODO super(em) remoteServiceService = new RemoteServiceService(em); jobCreator = new TarantinoJobCreator(em); vsm = new VsmServiceStub(); taskService = new TaskService(); } /** * Persist the {@link Task} for progress tracking and send the given {@link DatacenterTasks} to * tarantino. Steps: * <ol> * <li>Persist the {@link Task} to redis</li> * <li>Send {@link DatacenterTasks} to Tarantino using AMQP</li> * <li>If can not connect to AMQP broker, the {@link Task} is deleted from redis</li> * </ol> * * @param datacenter The {@link Datacenter} where the tasks will be sent to. * @param task The {@link Task} to persist. * @param tasks The {@link DatacenterTasks} to send. * @param eventType The {@link EventType} associated to the task (power on, reconfigure, etc). */ protected void enqueueTask(final Datacenter datacenter, final Task task, final DatacenterTasks tasks, final EventType eventType) { try { taskService.addTask(task); } catch (RuntimeException e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, eventType, "redis.error.user"); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, eventType, e, "redis.persistTaskError", e.getMessage()); addServiceUnavailableErrors(APIError.REDIS_CONNECTION_FAILED); flushErrors(); } TarantinoRequestProducer producer = getTarantinoProducer(datacenter); try { producer.openChannel(); producer.publish(tasks); } catch (Exception e) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, eventType, APIError.RABBITMQ_CONNECTION_FAILED.getMessage()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, eventType, e, "tarantino.sendError", e.getMessage()); try { // Delete redis stored task taskService.deleteTask(task); } catch (RuntimeException r) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, eventType, "redis.error.user"); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, eventType, r, "redis.deleteTaskError", r.getMessage()); } addServiceUnavailableErrors(APIError.RABBITMQ_CONNECTION_FAILED); flushErrors(); } finally { closeProducerChannel(producer, eventType); } tracer.log(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, eventType, "tarantino.taskEnqueued"); } /** * Unsubscribe the {@link VirtualMachine} from VSM and call to * {@link TarantinoService#enqueueTask(Datacenter, Task, DatacenterTasks, EventType)}. * * @param virtualMachine The {@link VirtualMachine} to instance. * @param redisTask {@link Task} to persist * @param tarantinoTask {@link DatacenterTasks} to send * @return The {@link Task} UUID for progress tracking */ protected String unsubscribeVirtualMachineAndEnqueueTask(final VirtualMachine virtualMachine, final Task redisTask, final DatacenterTasks tarantinoTask, final EventType eventType) { // Unsubscribe the virtual machine to prevent unlock Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); RemoteService service = remoteServiceService.getVSMRemoteService(datacenter); if (vsm.isVirtualMachineSubscribed(service, virtualMachine.getName())) { logger.debug("Unsubscribing virtual machine {} from VSM", virtualMachine.getName()); vsm.unsubscribe(service, virtualMachine); logger.debug("Virtual machine {} unsubscribed from VSM", virtualMachine.getName()); } else { // The machine must be subscribed logger .error( "Unsubscribing virtual machine {} from VSM: Error: the virtual machine was not subscribed. Was the Subscription deleted manually outside Abiquo?", virtualMachine.getName()); } try { // Add Redis task for progress tracking and send the tarantino task logger.debug("Enqueuing task for virtual machine {}", virtualMachine.getName()); enqueueTask(datacenter, redisTask, tarantinoTask, eventType); logger.debug("Task for virtual machine {} enqueued", virtualMachine.getName()); } catch (APIException e) { // Restore the virtual machine subscription on error logger.debug("Subscribing virtual machine {} to VSM due an send error.", virtualMachine.getName()); vsm.subscribe(service, virtualMachine); logger.debug("Virtual machine {} subscribed to VSM", virtualMachine.getName()); throw e; } return tarantinoTask.getId(); } private TarantinoRequestProducer getTarantinoProducer(final Datacenter datacenter) { final String datacenterQueueId = datacenter.getUuid(); if (StringUtils.isEmpty(datacenterQueueId)) { tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNKNOWN, null, "tarantino.sendError", APIError.DATACENTER_QUEUE_NOT_CONFIGURED.getMessage()); addNotFoundErrors(APIError.DATACENTER_QUEUE_NOT_CONFIGURED); flushErrors(); } return new TarantinoRequestProducer(datacenterQueueId); } /** * Close the producer channel. * * @param producer The channel to close. * @param event The event being processed (power on, reconfigure, etc). */ private void closeProducerChannel(final TarantinoRequestProducer producer, final EventType event) { try { producer.closeChannel(); } catch (IOException ex) { tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, event, APIError.GENERIC_OPERATION_ERROR.getMessage()); tracer.systemError(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, event, ex, "tarantino.closeProducer", ex.getMessage()); } } /** * Creates and sends a deploy operation. * * @param virtualMachine The virtual machine to reconfigure. * @param originalConfig The original configuration for the virtual machine. * @param newConfig The new configuration for the virtual machine. * @return The identifier of the reconfigure task. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String deployVirtualMachine(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder virtualMachineDesciptionBuilder) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); try { HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(virtualMachineDesciptionBuilder.build(), conn, userService.getCurrentUser().getNick()); DatacenterTasks deployTask = builder.add(VirtualMachineStateTransition.CONFIGURE) .add(VirtualMachineStateTransition.POWERON).buildTarantinoTask(); enqueueTask(datacenter, builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.DEPLOY), deployTask, EventType.VM_DEPLOY); return deployTask.getId(); } catch (RuntimeException e) { logger.error("Error enqueuing the deploy task dto to the virtual factory with error ", e); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "tarantino.deployVMError", e.getMessage()); // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } /** * Creates and sends a reconfigure operation. Unsubscribe el VirtualMachine * * @param vm The virtual machine to reconfigure. * @param originalConfig The original configuration for the virtual machine. * @param newConfig The new configuration for the virtual machine. * @return The identifier of the reconfigure task. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String reconfigureVirtualMachine(final VirtualMachine vm, final VirtualMachineDescriptionBuilder originalConfig, final VirtualMachineDescriptionBuilder newConfig) { Datacenter datacenter = vm.getHypervisor().getMachine().getDatacenter(); RemoteService vsmRS = remoteServiceService.getRemoteService(datacenter.getId(), RemoteServiceType.VIRTUAL_SYSTEM_MONITOR); if (vsm.isVirtualMachineSubscribed(vsmRS, vm.getName())) { logger.debug("Unsubscribing virtual machine {} from VSM", vm.getName()); vsm.unsubscribe(vsmRS, vm); logger.debug("Virtual machine {} unsubscribed from VSM", vm.getName()); } else { // The machine must be subscribed logger .error( "Unsubscribing virtual machine {} from VSM: Error: the virtual machine was not subscribed. Was the Subscription deleted manually outside Abiquo?", vm.getName()); } try { HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(vm.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(originalConfig.build(), conn, userService .getCurrentUser().getNick()); DatacenterTasks reconfigTask = builder.addReconfigure(newConfig.build()).buildTarantinoTask(); enqueueTask(datacenter, builder.buildAsyncTask(String.valueOf(vm.getId()), TaskType.RECONFIGURE), builder.buildTarantinoTask(), EventType.VM_RECONFIGURE); return reconfigTask.getId(); } catch (NotFoundException e) { logger .debug("Error enqueuing the reconfiguretask dto to the virtual factory with error: " + e.getMessage() + " unmonitoring the machine: " + vm.getName()); vsm.subscribe(vsmRS, vm); throw e; } catch (RuntimeException e) { logger .error("Error enqueuing the reconfigure task dto to the virtual factory with error: " + e.getMessage()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_RECONFIGURE, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_RECONFIGURE, "tarantino.reconfigureVMError", e.getMessage()); // We need to re subscribe to the machine vsm.subscribe(vsmRS, vm); // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } /** * Sends a Deploy operation originated by HA move * * @param virtualMachine * @param virtualMachineDesciptionBuilder * @param originalVMState * @return */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String deployVirtualMachineHA(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder virtualMachineDesciptionBuilder, final boolean originalVMStateON, final Map<String, String> extraData) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); try { HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(virtualMachineDesciptionBuilder.build(), conn, "admin"); // XXX: This should be system user DatacenterTasks deployTask = null; if (originalVMStateON) { deployTask = builder.add(VirtualMachineStateTransition.CONFIGURE, extraData) .add(VirtualMachineStateTransition.POWERON).buildTarantinoTask(); } else { deployTask = builder.add(VirtualMachineStateTransition.CONFIGURE, extraData) .buildTarantinoTask(); } enqueueTask(datacenter, builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.HA_DEPLOY), deployTask, EventType.VM_MOVING_BY_HA); return deployTask.getId(); } catch (NotFoundException e) { // No need to unsuscribe the machine // logger.debug("Error enqueuing the HA deploy task dto to Tarantino with error: " // + e.getMessage() + " unmonitoring the machine: " + virtualMachine.getName()); // vsm.unsubscribe(remoteService, virtualMachine); throw e; } catch (RuntimeException e) { logger .error("Error enqueuing the HA deploy task dto to the virtual factory with error: " + e.getMessage()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_MOVING_BY_HA, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_MOVING_BY_HA, "tarantino.deployVMError", e.getMessage()); // No need to unsuscribe the machine // logger.debug("Error enqueuing the deploy task dto to Tarantino with error: " // + e.getMessage() + " unmonitoring the machine: " + virtualMachine.getName()); // vsm.unsubscribe(remoteService, virtualMachine); // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } /** * Undeploys VM after a Re-enable HA operation * * @param virtualMachine * @param virtualMachineDesciptionBuilder * @param currentState * @param sourceHypervisor VM is undeployed from this hypervisor, not the one we have in DB * @return */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String undeployVirtualMachineHA(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder virtualMachineDesciptionBuilder, final VirtualMachineState currentState, final Hypervisor originalHypervisor) { Map<String, String> extraData = new HashMap<String, String>(); extraData.put("isHA", Boolean.TRUE.toString()); try { // VM is undeployed from this hypervisor, not the one we have in DB HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(originalHypervisor); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(virtualMachineDesciptionBuilder.build(), conn, /* userService.getCurrentUser().getNick() */"admin"); // XXX: This should be system user if (VirtualMachineState.ON.equals(currentState)) { builder.add(VirtualMachineStateTransition.POWEROFF); } DatacenterTasks tarantinoTask = builder.add(VirtualMachineStateTransition.DECONFIGURE, extraData) .buildTarantinoTask(); Task redisTask = builder .buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.HA_UNDEPLOY); Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); // Add Redis task for progress tracking and send the tarantino task logger.debug("Enqueuing task for virtual machine {}", virtualMachine.getName()); enqueueTask(datacenter, redisTask, tarantinoTask, EventType.VM_UNDEPLOY); logger.debug("Task for virtual machine {} enqueued", virtualMachine.getName()); return tarantinoTask.getId(); } catch (NotFoundException e) { throw e; } catch (RuntimeException e) { logger .error("Error enqueuing the undeploy task dto to the virtual factory with error: " + e.getMessage()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "tarantino.undeployVMError", e.getMessage()); // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } /** * Creates and sends a deploy operation. * * @param virtualMachine The virtual machine to reconfigure. * @param originalConfig The original configuration for the virtual machine. * @param newConfig The new configuration for the virtual machine. * @return The identifier of the reconfigure task. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String undeployVirtualMachine(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder virtualMachineDesciptionBuilder, final VirtualMachineState currentState) { try { HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(virtualMachineDesciptionBuilder.build(), conn, userService.getCurrentUser().getNick()); if (mustPowerOffToUndeploy(currentState)) { builder.add(VirtualMachineStateTransition.POWEROFF); } DatacenterTasks tarantinoTask = builder.add(VirtualMachineStateTransition.DECONFIGURE).buildTarantinoTask(); Task redisTask = builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.UNDEPLOY); unsubscribeVirtualMachineAndEnqueueTask(virtualMachine, redisTask, tarantinoTask, EventType.VM_UNDEPLOY); return tarantinoTask.getId(); } catch (NotFoundException e) { throw e; } catch (RuntimeException e) { logger .error("Error enqueuing the undeploy task dto to the virtual factory with error: " + e.getMessage()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "tarantino.undeployVMError", e.getMessage()); // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } /** * Creates and sends a deploy operation. <br> * Also adds the flag for deletion of the {@link VirtualMachine}. * * @param virtualMachine The virtual machine to reconfigure. * @param originalConfig The original configuration for the virtual machine. * @param newConfig The new configuration for the virtual machine. * @return The identifier of the reconfigure task. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String undeployVirtualMachineAndDelete(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder virtualMachineDesciptionBuilder, final VirtualMachineState currentState) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); try { HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(virtualMachineDesciptionBuilder.build(), conn, userService.getCurrentUser().getNick()); if (mustPowerOffToUndeploy(currentState)) { builder.add(VirtualMachineStateTransition.POWEROFF); } // This method must delete the virtual machine in the handler Map<String, String> extraData = new HashMap<String, String>(); extraData.put("delete", Boolean.TRUE.toString()); DatacenterTasks tarantinoTask = builder.add(VirtualMachineStateTransition.DECONFIGURE, extraData) .buildTarantinoTask(); Task redisTask = builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.UNDEPLOY); unsubscribeVirtualMachineAndEnqueueTask(virtualMachine, redisTask, tarantinoTask, EventType.VM_UNDEPLOY); return tarantinoTask.getId(); } catch (NotFoundException e) { throw e; } catch (RuntimeException e) { logger .error("Error enqueuing the undeploy task dto to the virtual factory with error: " + e.getMessage()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_UNDEPLOY, "tarantino.undeployVMError", e.getMessage()); // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } protected boolean mustPowerOffToUndeploy(final VirtualMachineState currentState) { return VirtualMachineState.ON.equals(currentState) || VirtualMachineState.PAUSED.equals(currentState); } /** * Creates and sends a deploy operation. * * @param virtualMachine The virtual machine to reconfigure. * @param originalConfig The original configuration for the virtual machine. * @param newConfig The new configuration for the virtual machine. * @return The identifier of the reconfigure task. */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String applyVirtualMachineState(final VirtualMachine virtualMachine, final VirtualMachineDescriptionBuilder virtualMachineDesciptionBuilder, final VirtualMachineStateTransition machineStateTransition) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); // ignoreVSMEventsIfNecessary(datacenter, virtualMachine); // Some hypervisors may keep the virtual machine in ON state during the reset operation. // This means that the VSM will not notify any state change event. To prevent this, we // invalidate the last known state for the virtual machine in the VSM to force it to notify // the next state change event. if (machineStateTransition == VirtualMachineStateTransition.RESET) { invalidateLastKnownState(virtualMachine); } try { HypervisorConnection conn = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(virtualMachineDesciptionBuilder.build(), conn, userService.getCurrentUser().getNick()); DatacenterTasks deployTask = builder.add(machineStateTransition).buildTarantinoTask(); enqueueTask(datacenter, builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), getTaskTypeFromTransition(machineStateTransition)), deployTask, EventType.VM_STATE); return deployTask.getId(); } catch (NotFoundException e) { // We need to unsuscribe the machine logger .debug("Error enqueuing the state change task dto to the virtual factory with error: " + e.getMessage() + " machine: " + virtualMachine.getName()); // If we invalidated the last known state and something fails, ask VSM for the state of // the virtual machine to recover it. if (machineStateTransition == VirtualMachineStateTransition.RESET) { refreshState(virtualMachine); } throw e; } catch (RuntimeException e) { logger .error("Error enqueuing the state change task dto to the virtual factory with error: " + e.getMessage()); tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_STATE, APIError.GENERIC_OPERATION_ERROR.getMessage()); // For the Admin to know all errors tracer.systemLog(SeverityType.CRITICAL, ComponentType.VIRTUAL_MACHINE, EventType.VM_DEPLOY, "tarantino.applyChangesVMError", e.getMessage()); // If we invalidated the last known state and something fails, ask VSM for the state of // the virtual machine to recover it. if (machineStateTransition == VirtualMachineStateTransition.RESET) { refreshState(virtualMachine); } // There is no point in continue addUnexpectedErrors(APIError.GENERIC_OPERATION_ERROR); flushErrors(); } return null; } /** * Wrapper of * {@link TarantinoService#snapshotVirtualMachine(VirtualAppliance, VirtualMachine, VirtualMachineState, String, String, String)} * that uses {@link SnapshotUtils#formatSnapshotPath(VirtualMachineTemplate)} and * {@link SnapshotUtils#formatSnapshotFilename(VirtualMachineTemplate)} to build the instance * path and filename. * * @param virtualAppliance The {@link VirtualAppliance} where the {@link VirtualMachine} is * located. * @param virtualMachine The {@link VirtualMachine} to instance. * @param originalState The original {@link VirtualMachineState}. * @param snapshotName The final name of the {@link VirtualMachineTemplate}. * @return The {@link Task} UUID for progress tracking */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String snapshotVirtualMachine(final VirtualAppliance virtualAppliance, final VirtualMachine virtualMachine, final VirtualMachineState originalState, final String snapshotName) { VirtualMachineTemplate template = virtualMachine.getVirtualMachineTemplate(); return snapshotVirtualMachine(virtualAppliance, virtualMachine, originalState, snapshotName, SnapshotUtils.formatSnapshotPath(template), SnapshotUtils.formatSnapshotFilename(template)); } /** * Builds a {@link DiskSnapshot} from a {@link VirtualMachine} and call to * {@link TarantinoService#createAndSendVirtualMachineInstanceOperations(VirtualAppliance, VirtualMachine, VirtualMachineState, DiskSnapshot)} * to start the instance process. * * @param virtualAppliance The {@link VirtualAppliance} where the {@link VirtualMachine} is * located. * @param virtualMachine The {@link VirtualMachine} to instance. * @param originalState The original {@link VirtualMachineState}. * @param snapshotName The final name of the {@link VirtualMachineTemplate}. * @param snapshotPath Path where the instance will be created. * @param snapshotFilename Filename of the instance. * @return The {@link Task} UUID for progress tracking */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String snapshotVirtualMachine(final VirtualAppliance virtualAppliance, final VirtualMachine virtualMachine, final VirtualMachineState originalState, final String snapshotName, final String snapshotPath, final String snapshotFilename) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); DiskSnapshot destinationDisk = new DiskSnapshot(); destinationDisk.setRepository(infrastructureService.getRepository(datacenter).getUrl()); destinationDisk.setPath(snapshotPath); destinationDisk.setSnapshotFilename(snapshotFilename); destinationDisk.setName(snapshotName); destinationDisk.setRepositoryManagerAddress(remoteServiceService.getAMRemoteService( datacenter).getUri()); return createAndSendVirtualMachineInstanceOperations(virtualAppliance, virtualMachine, originalState, destinationDisk); } /** * Refresh the resources of the given virtual machine. * * @param virtualMachine The virtual machine. * @return The {@link Task} UUID for progress tracking */ @Transactional(readOnly = true, propagation = Propagation.REQUIRED) public String refreshVirtualMachineResources(final VirtualMachine virtualMachine, final VirtualAppliance virtualAppliance) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); // Build the job sequence VirtualMachineDescriptionBuilder definitionBuilder = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); VirtualMachineDefinition definition = definitionBuilder.build(); HypervisorConnection connection = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(definition, connection, userService.getCurrentUser() .getNick()); builder.addRefreshResources(); // Build redis task Task redisTask = builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.REFRESH); // Build tarantino task DatacenterTasks tarantinoTask = builder.buildTarantinoTask(); // Add Redis task for progress tracking and send the tarantino task return unsubscribeVirtualMachineAndEnqueueTask(virtualMachine, redisTask, tarantinoTask, EventType.VM_REFRESH_RESOURCES); } /** * Creates and sends the DTOs for an instance of type {@link SnapshotType#FROM_STATEFUL_DISK}. * * @param virtualAppliance The {@link VirtualAppliance} where the {@link VirtualMachine} is * located. * @param virtualMachine The {@link VirtualMachine} to instance. * @param originalState The original {@link VirtualMachineState}. * @param snapshotName The final name of the {@link VirtualMachineTemplate}. * @return The {@link Task} UUID for progress tracking */ public String instanceStatefulVirtualMachine(final VirtualAppliance virtualAppliance, final VirtualMachine virtualMachine, final VirtualMachineState originalState, final String snapshotName) { logger.debug("Unsupported instance type {} on community version.", SnapshotType.FROM_STATEFUL_DISK.name()); addConflictErrors(APIError.STATUS_CONFLICT); flushErrors(); return null; } /** * Creates and sends the DTOs for an instance operation. * * @param virtualAppliance The {@link VirtualAppliance} where the {@link VirtualMachine} is * located. * @param virtualMachine The {@link VirtualMachine} to instance. * @param originalState The original {@link VirtualMachineState}. * @param destinationDisk The destination {@link DiskSnapshot}. * @return The {@link Task} UUID for progress tracking */ private String createAndSendVirtualMachineInstanceOperations( final VirtualAppliance virtualAppliance, final VirtualMachine virtualMachine, final VirtualMachineState originalState, final DiskSnapshot destinationDisk) { // Build the job sequence VirtualMachineDescriptionBuilder definitionBuilder = jobCreator.toTarantinoDto(virtualMachine, virtualAppliance); VirtualMachineDefinition definition = definitionBuilder.build(); HypervisorConnection connection = jobCreator.hypervisorConnectionConfiguration(virtualMachine.getHypervisor()); DatacenterTaskBuilder builder = new DatacenterTaskBuilder(definition, connection, userService.getCurrentUser() .getNick()); if (SnapshotUtils.mustPowerOffToSnapshot(originalState)) { logger.debug("Instance of virtual machine {} requires a power off", virtualMachine.getName()); builder.add(VirtualMachineStateTransition.POWEROFF); builder.addSnapshot(destinationDisk); builder.add(VirtualMachineStateTransition.POWERON); } else { builder.addSnapshot(destinationDisk); } // Build redis task Task redisTask = builder.buildAsyncTask(String.valueOf(virtualMachine.getId()), TaskType.SNAPSHOT); // Build tarantino task DatacenterTasks tarantinoTask = builder.buildTarantinoTask(); return unsubscribeVirtualMachineAndEnqueueTask(virtualMachine, redisTask, tarantinoTask, EventType.VM_INSTANCE); } public String changeVirtualMachineStateWhileStatefulInstance( final VirtualAppliance virtualAppliance, final VirtualMachine virtualMachine, final Map<String, String> data, final VirtualMachineStateTransition transition, final String creationUser, final boolean unsubscribe) { return null; } /** * Return the {@link TaskType} that is related to this {@link VirtualMachineStateTransition}. <br> * <br> * Null if empty. * * @param machineStateTransition the current. * @return JobType */ private TaskType getTaskTypeFromTransition( final VirtualMachineStateTransition machineStateTransition) { switch (machineStateTransition) { case CONFIGURE: { return TaskType.DEPLOY; } case DECONFIGURE: { return TaskType.UNDEPLOY; } case POWEROFF: { return TaskType.POWER_OFF; } case POWERON: { return TaskType.POWER_ON; } case PAUSE: { return TaskType.PAUSE; } case RESUME: { return TaskType.RESET; } case SNAPSHOT: { return TaskType.SNAPSHOT; } case RECONFIGURE: { return TaskType.RECONFIGURE; } case RESET: { return TaskType.RESET; } default: { logger.error("Error unknown transition: " + machineStateTransition); } return null; } } /** * Invalidates the last known state of the given virtual machine in the VSM. * <p> * This method will force the VSM to notify the state of the virtual machine the next time an * event is produced in a virtual machine. * * @param virtualMachine The virtual machine. */ protected void invalidateLastKnownState(final VirtualMachine virtualMachine) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); RemoteService service = remoteServiceService.getVSMRemoteService(datacenter); logger.debug("Invalidating last known state for virtual machine {} in VSM", virtualMachine.getName()); vsm.invalidateLastKnownVirtualMachineState(service, virtualMachine); logger.debug("State for virtual machine {} invalidated in VSM", virtualMachine.getName()); } /** * Refreshes the state of the given virtual machine in the VSM. * <p> * This method will force the VSM to notify the state of the virtual machine. * * @param virtualMachine The virtual machine. */ protected void refreshState(final VirtualMachine virtualMachine) { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); RemoteService service = remoteServiceService.getVSMRemoteService(datacenter); logger.debug("Refreshing state for virtual machine {} in VSM", virtualMachine.getName()); vsm.refreshVirtualMachineState(service, virtualMachine); logger.debug("State for virtual machine {} refreshed in VSM", virtualMachine.getName()); } }