/** * 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.vsm.redis.pubsub; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.JedisPubSub; import com.abiquo.commons.amqp.impl.vsm.VSMProducer; import com.abiquo.commons.amqp.impl.vsm.domain.VirtualSystemEvent; import com.abiquo.commons.amqp.util.RabbitMQUtils; import com.abiquo.vsm.events.VMEventType; import com.abiquo.vsm.model.PhysicalMachine; import com.abiquo.vsm.model.VirtualMachine; import com.abiquo.vsm.monitor.Monitor.Type; import com.abiquo.vsm.redis.dao.RedisDao; import com.abiquo.vsm.redis.dao.RedisDaoFactory; import com.abiquo.vsm.redis.pubsub.notifier.GenericNotifier; import com.abiquo.vsm.redis.pubsub.notifier.impl.ESXiNotifier; import com.abiquo.vsm.redis.pubsub.notifier.impl.HyperVNotifier; import com.abiquo.vsm.redis.pubsub.notifier.impl.KVMNotifier; import com.abiquo.vsm.redis.pubsub.notifier.impl.LibvirtNotifier; import com.abiquo.vsm.redis.pubsub.notifier.impl.VirtualBoxNotifier; import com.abiquo.vsm.redis.pubsub.notifier.impl.XenServerNotifier; /** * Responsible to process and send each published event. * * @see http://code.google.com/p/redis/wiki/PublishSubscribe * @see RedisSubscriberCallback * @see RedisPublisher.publishEvent * @see GenericNotifier * @author eruiz@abiquo.com */ public class RedisSubscriberCallback extends JedisPubSub { private final static Logger logger = LoggerFactory.getLogger(RedisSubscriberCallback.class); private VSMProducer broker; private RedisDao dao; private Map<String, GenericNotifier> notifiers; public RedisSubscriberCallback(String host, int port) { // RabbitMQ producer broker = new VSMProducer(); // DAO for redis persistence dao = RedisDaoFactory.getInstance(); // Instance all available types of notifiers notifiers = new HashMap<String, GenericNotifier>(); notifiers.put(Type.VMX_04.name(), new ESXiNotifier()); notifiers.put(Type.HYPERV_301.name(), new HyperVNotifier()); notifiers.put(Type.XENSERVER.name(), new XenServerNotifier()); notifiers.put(Type.KVM.name(), new KVMNotifier()); notifiers.put(Type.XEN_3.name(), new LibvirtNotifier()); notifiers.put(Type.VBOX.name(), new VirtualBoxNotifier()); } /** * Called when an event is published in redis channel. * * @param channel The channel name. * @param message The 'serialized' notification info. */ @Override public void onMessage(String channel, String message) { String fields[] = getMessageFields(message); if (fields != null) { String virtualMachineName = fields[0]; String eventName = fields[1]; String physicalMachineAddress = fields[2]; VirtualMachine virtualMachine = dao.findVirtualMachineByName(virtualMachineName); PhysicalMachine machine = dao.findPhysicalMachineByAddress(physicalMachineAddress); VMEventType event = VMEventType.valueOf(eventName); if (virtualMachine != null && virtualMachine.getPhysicalMachine() != null && machine != null) { notifyEvent(virtualMachine, machine, event); } else { if (virtualMachine == null) { logger.trace("Unable to find a virtual machine with name, {}.", virtualMachineName); } else if (virtualMachine.getPhysicalMachine() == null) { logger .trace( "Unable to find the physical machine {} referenced by virtual machine with name, {}.", physicalMachineAddress, virtualMachineName); } else if (machine == null) { logger.trace("Unable to find a physical machine with address, {}.", physicalMachineAddress); } } } else { logger.trace("Invalid message (skipped): {}", message); } } private String[] getMessageFields(final String message) { String fields[] = message.split(RedisPublisher.RegexSeparator); if (fields.length != 3) { logger.error("Malformed message {}", message); return null; } return fields; } /** * Process the event, update Redis database and send the notifications to RabbitMQ broker. * * @param virtualMachine Virtual machine affected. * @param machine Where the event was produced. * @param event The produced event. */ private VirtualMachine notifyEvent(VirtualMachine virtualMachine, final PhysicalMachine machine, final VMEventType event) { GenericNotifier notifier = notifiers.get(machine.getType()); if (notifier == null) { logger.error("Unknown type {} for the physical machine {}", machine.getType(), machine.getAddress()); return virtualMachine; } List<VirtualSystemEvent> notifications = notifier.processEvent(virtualMachine, machine, event); if (notifications.isEmpty()) { // There are no events to notify return virtualMachine; } if (enqueueNotifications(notifications)) { VirtualSystemEvent last = notifications.get(notifications.size() - 1); virtualMachine.setLastKnownState(last.getEventType()); if (containsMovedEvent(notifications)) { // Update the PhysicalMachine where the VirtualMachine is. virtualMachine.setPhysicalMachine(machine); } virtualMachine = dao.save(virtualMachine); } return virtualMachine; } private boolean containsMovedEvent(final List<VirtualSystemEvent> notifications) { for (VirtualSystemEvent notification : notifications) { if (notification.getEventType().equals(VMEventType.MOVED.name())) { return true; } } return false; } private boolean enqueueNotifications(final List<VirtualSystemEvent> notifications) { try { broker.openChannel(); for (VirtualSystemEvent n : notifications) { broker.publish(n); logger.info(String.format( "Published a %s event from virtual machine %s in hypervisor %s (%s).", n.getEventType(), n.getVirtualSystemId(), n.getVirtualSystemAddress(), n.getVirtualSystemType())); } broker.closeChannel(); return true; } catch (IOException e) { logger .error("The send of the following notifications may has failed. RabbitMQ is unreachable."); for (VirtualSystemEvent n : notifications) { logger.error("{} event on.", n.getEventType()); logger.error("\tVirtual machine name: {}", n.getVirtualSystemId()); logger.error("\tPhysical machine address: {}", n.getVirtualSystemAddress()); logger.error("\tPhysical machine type: {}", n.getVirtualSystemType()); } if (!RabbitMQUtils.pingRabbitMQ()) { logger .error("RabbitMQ is not alive, unsubscribing in order to stop the event consuming."); unsubscribe(); } return false; } } @Override public void onPMessage(String arg0, String arg1, String arg2) { // Auto-generated method stub } @Override public void onPSubscribe(String arg0, int arg1) { // Auto-generated method stub } @Override public void onPUnsubscribe(String arg0, int arg1) { // Auto-generated method stub } @Override public void onSubscribe(String channel, int arg) { logger.info("VSM subscribed to redis {} channel", channel); } @Override public void onUnsubscribe(String channel, int arg) { logger.info("VSM unsubscribed to redis {} channel", channel); } }