/** * 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.eventing; import java.util.HashMap; import java.util.Map; import javax.persistence.EntityManager; 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.services.RemoteServiceService; import com.abiquo.api.services.VirtualMachineAllocatorService; import com.abiquo.api.services.stub.VsmServiceStub; import com.abiquo.api.tracer.TracerLogger; import com.abiquo.commons.amqp.impl.vsm.VSMCallback; import com.abiquo.commons.amqp.impl.vsm.domain.VirtualSystemEvent; import com.abiquo.scheduler.SchedulerLock; import com.abiquo.server.core.cloud.NodeVirtualImage; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.cloud.VirtualMachineRep; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.infrastructure.Datacenter; import com.abiquo.server.core.infrastructure.RemoteService; import com.abiquo.tracer.ComponentType; import com.abiquo.tracer.EventType; import com.abiquo.tracer.SeverityType; import com.abiquo.vsm.events.VMEventType; /** * This listener {@link VSMCallback} receives the events from the all Virtual System Monitors in * each datacenter and updates the state of virtual machines and virtual appliances in database. * * @author eruiz@abiquo.com */ @Service public class VSMEventProcessor implements VSMCallback { private final static Logger LOGGER = LoggerFactory.getLogger(VSMEventProcessor.class); @Autowired protected VirtualMachineRep virtualMachineRep; @Autowired protected TracerLogger tracer; @Autowired protected VirtualMachineAllocatorService allocatorService; @Autowired protected RemoteServiceService remoteServiceService; @Autowired protected VsmServiceStub vsm; /** Event to virtual machine state translations */ protected final Map<VMEventType, VirtualMachineState> stateByEvent = new HashMap<VMEventType, VirtualMachineState>() { { put(VMEventType.POWER_OFF, VirtualMachineState.OFF); put(VMEventType.POWER_ON, VirtualMachineState.ON); put(VMEventType.PAUSED, VirtualMachineState.PAUSED); put(VMEventType.RESUMED, VirtualMachineState.ON); put(VMEventType.DESTROYED, VirtualMachineState.NOT_ALLOCATED); put(VMEventType.MOVED, VirtualMachineState.LOCKED); // TODO Move to enterprise } }; /** Event to trace-event virtual machine translations */ protected final Map<VMEventType, EventType> traceEventByEvent = new HashMap<VMEventType, EventType>() { { put(VMEventType.POWER_OFF, EventType.VM_POWEROFF); put(VMEventType.POWER_ON, EventType.VM_POWERON); put(VMEventType.PAUSED, EventType.VM_PAUSED); put(VMEventType.RESUMED, EventType.VM_RESUMED); put(VMEventType.DESTROYED, EventType.VM_DESTROY); put(VMEventType.MOVED, EventType.VM_MOVED); } }; /** * Constructor for test purposes only. * * @param em The entity manager to use. */ public VSMEventProcessor(final EntityManager em) { this.virtualMachineRep = new VirtualMachineRep(em); this.tracer = new TracerLogger(); this.allocatorService = new VirtualMachineAllocatorService(em); this.remoteServiceService = new RemoteServiceService(em); this.vsm = new VsmServiceStub(); } public VSMEventProcessor() { } @Override @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void onEvent(final VirtualSystemEvent notification) { LOGGER.debug("New notification received, {}.", notification.toString()); // Check if notification contains a valid event VMEventType event = eventFromString(notification.getEventType()); if (stateByEvent.get(event) == null) { LOGGER.error("Unknown event {}. Just ignoring it.", notification.getEventType()); return; } // Update virtual machine state VirtualMachine machine = virtualMachineRep.findByName(notification.getVirtualSystemId()); if (machine != null) { virtualMachineRep.update(updateMachineState(machine, notification)); } } /** * Process the given notification and if it affects to the virtual machine, updates the state of * a virtual machine instance. * * @param machine The instance to update. * @param notification The notification. * @return The virtual machine instance. */ protected VirtualMachine updateMachineState(final VirtualMachine machine, final VirtualSystemEvent notification) { return processEvent(machine, eventFromString(notification.getEventType()), notification); } /** * Process the given notification and if it affects to the virtual machine, updates the state of * a virtual machine instance. * * @param virtualMachine The instance to update. * @param event The event notified. * @param notification The complete notification. * @return The virtual machine instance. */ protected VirtualMachine processEvent(final VirtualMachine virtualMachine, final VMEventType event, final VirtualSystemEvent notification) { switch (event) { case PAUSED: case POWER_OFF: case POWER_ON: case RESUMED: case SAVED: virtualMachine.setState(stateByEvent.get(event)); break; case DESTROYED: onVMDestroyedEvent(virtualMachine, event, notification); break; default: LOGGER.warn("Ignoring {} event.", event); break; } return virtualMachine; } protected void logAndTraceVirtualMachineStateUpdated(final VirtualMachine machine, final VMEventType event, final VirtualSystemEvent notification) { String message = String.format("Processed %s event in machine %s, the current machine state is %s.", event.name(), machine.getName(), machine.getState().name()); if (!machine.isManaged()) { message = String .format( "Processed %s event in machine %s. The machine does not exist in the hypervisor.", event.name(), machine.getName()); } traceVirtualMachineStateUpdated(notification, message); LOGGER.debug(message); } /** * Publish an INFO system log to tracer system with VIRTUAL_MACHINE component type. * * @param notification The received notification. * @param message The message to publish */ protected void traceVirtualMachineStateUpdated(final VirtualSystemEvent notification, final String message) { VMEventType event = eventFromString(notification.getEventType()); if (traceEventByEvent.containsKey(event)) { tracer.systemLog(SeverityType.INFO, ComponentType.VIRTUAL_MACHINE, traceEventByEvent.get(event), message); } } protected VMEventType eventFromString(final String name) { try { return VMEventType.valueOf(name); } catch (IllegalArgumentException e) { return null; } } /** * Fires on Virtual Machine Destroyed event detection. - Sets VM state to NOT_ALLOCATED - * Resources ARE freed * * @param virtualMachine virtual machine that has been destroyed */ protected void onVMDestroyedEvent(final VirtualMachine virtualMachine, final VMEventType event, final VirtualSystemEvent notification) { if (virtualMachine.getState().existsInHypervisor()) { unsubscribeVirtualMachine(virtualMachine); final String lockMessage = "DESTROY event for virtualmachine '" + virtualMachine.getName() + "'"; try { SchedulerLock.acquire(lockMessage); allocatorService.deallocateVirtualMachine(virtualMachine); } finally { SchedulerLock.release(lockMessage); } if (virtualMachine.isCaptured()) { NodeVirtualImage node = virtualMachineRep.findNodeVirtualImageByVm(virtualMachine); virtualMachineRep.deleteNodeVirtualImage(node); virtualMachineRep.deleteVirtualMachine(virtualMachine); } } logAndTraceVirtualMachineStateUpdated(virtualMachine, event, notification); } /** * Performs an unsubscribe when a DESTROYED event is detected * * @param virtualMachine The virtual machine to unsubscribe * @return True if the virtual machine is successfully unsubscribed. Otherwise false. */ protected void unsubscribeVirtualMachine(final VirtualMachine virtualMachine) { try { Datacenter datacenter = virtualMachine.getHypervisor().getMachine().getDatacenter(); RemoteService remoteService = remoteServiceService.getVSMRemoteService(datacenter); if (vsm.isVirtualMachineSubscribed(remoteService, virtualMachine.getName())) { vsm.unsubscribe(remoteService, virtualMachine); } } catch (Exception e) { LOGGER .error( "There was a problem processing a DESTROY event on Virtual Machine {}: Unsubscribing to VSM failed {}", new Object[] {virtualMachine.getName(), e.getMessage()}); tracer.systemLog(SeverityType.MAJOR, ComponentType.VIRTUAL_MACHINE, EventType.VM_DESTROY, "virtualMachine.destroyed.unsubscribeFailed", new Object[] { virtualMachine.getName(), e.getMessage()}); } } }