/**
* 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.monitor.hyperv;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.abiquo.vsm.events.VMEvent;
import com.abiquo.vsm.events.VMEventType;
import com.abiquo.vsm.exception.MonitorException;
import com.abiquo.vsm.model.PhysicalMachine;
import com.abiquo.vsm.model.VirtualMachinesCache;
import com.abiquo.vsm.monitor.AbstractMonitor;
import com.abiquo.vsm.monitor.Monitor;
import com.abiquo.vsm.monitor.Monitor.Type;
import com.abiquo.vsm.monitor.executor.AbstractTask;
import com.abiquo.vsm.monitor.executor.PeriodicalExecutor;
import com.abiquo.vsm.monitor.hyperv.util.HyperVUtils;
import com.hyper9.jwbem.msvm.virtualsystem.MsvmComputerSystem;
/**
* The HyperV monitor.
*
* @author ibarrera
*/
@Monitor(type = Type.HYPERV_301)
public class HyperVMonitor extends AbstractMonitor
{
/** The logger. */
private final static Logger LOGGER = LoggerFactory.getLogger(HyperVMonitor.class);
/** Maximum number of machines this monitor can manage. */
public static final int MAX_MONITORED_MACHINES = 1;
/** HyperV WMI based connector */
private HyperVWMIConnector hyperv;
// Polling stuff
/** The executor */
private PeriodicalExecutor executor;
/** The poller */
private Poller poller;
/**
* Creates the HyperV monitor.
*/
public HyperVMonitor()
{
hyperv = new HyperVWMIConnector();
poller = new Poller();
executor = createExecutor(poller, Poller.POLLING_INTERVAL);
}
@Override
public int getMaxNumberOfHypervisors()
{
return MAX_MONITORED_MACHINES;
}
@Override
public void shutdown()
{
String physicalmachines = StringUtils.join(monitoredMachines, ", ");
LOGGER.debug("Stopping HyperV monitor for: {}", physicalmachines);
executor.stop();
}
@Override
public void start()
{
LOGGER.debug("Starting HyperV monitor");
executor.start();
}
@Override
public void publishState(final String physicalMachineAddress, final String virtualMachineName)
throws MonitorException
{
super.publishState(physicalMachineAddress, virtualMachineName);
// Connect to the hypervisor
PhysicalMachine pm = getPhysicalMachine(physicalMachineAddress);
hyperv.connect(physicalMachineAddress, pm.getUsername(), pm.getPassword());
// Get concrete virtual machine state
VMEventType state = VMEventType.UNKNOWN;
try
{
int stateValue = hyperv.getState(virtualMachineName);
state = HyperVUtils.translateEvent(stateValue);
}
finally
{
hyperv.disconnect(physicalMachineAddress);
}
// Publish the event
this.notify(new VMEvent(state, physicalMachineAddress, virtualMachineName));
}
/**
* Creates and initializes the {@link PeriodicalExecutor}.
*
* @param poller The poller to be executed.
* @param pollInterval The polling execution interval.
* @return The PeriodicalExecutor.
*/
private PeriodicalExecutor createExecutor(final Poller poller, final int pollInterval)
{
return new PeriodicalExecutor(poller, pollInterval)
{
@Override
public void executionFailure(final Throwable t)
{
// [ABICLOUDPREMIUM-283] After an error in physical machine, Hyper-V is not
// monitorized anymore
// stopMonitoring();
LOGGER.trace("An unexpected error occured while performing monitor tasks", t);
}
};
}
/**
* Performs synchronous polling calls to get the state of the monitored virtual machines.
*
* @author ibarrera
*/
private class Poller extends AbstractTask
{
/** The polling interval. */
public static final int POLLING_INTERVAL = 5000;
@Override
public void execute() throws Exception
{
// It is important to synchronize the monitoredMachines list to avoid errors if a
// machine is added or removed while iterating the list
synchronized (monitoredMachines)
{
for (String physicalMachineAddress : monitoredMachines)
{
LOGGER.trace("Getting information from: {}", physicalMachineAddress);
PhysicalMachine pm = getPhysicalMachine(physicalMachineAddress);
VirtualMachinesCache cache = pm.getVirtualMachines();
hyperv.connect(physicalMachineAddress, pm.getUsername(), pm.getPassword());
// Current VMs in the hypervisor. Used to detect CREATE and DESTROY events
Set<String> currentVMs = new HashSet<String>();
try
{
LOGGER.trace("Getting information from the current VMS...");
// Get states
Iterable<MsvmComputerSystem> vms = hyperv.getAllVMs();
for (MsvmComputerSystem vm : vms)
{
// Save the VM in the list of current VMs
String vmName = vm.getElementName();
currentVMs.add(vmName);
// Get the new state of the VM
VMEventType state = HyperVUtils.translateEvent(vm.getEnabledState());
LOGGER.trace("Found VM {} in state {}", vmName, state.name());
VMEvent event = new VMEvent(state, physicalMachineAddress, vmName);
// Propagate the event. RedisSubscriber will decide if it must be
// notified, based on subscription information
HyperVMonitor.this.notify(event);
}
if (LOGGER.isTraceEnabled())
{
String cacheStr = StringUtils.join(cache.getCache(), ", ");
LOGGER.trace(
"Cache for Machine {} before generating CREATE/DESTROY is: {}",
physicalMachineAddress, cacheStr);
}
// Propagate create and destroy events
propagateCreateAndDestroyEvents(pm, currentVMs);
// Update the physical machine with the current machines in the hypervisor
cache.getCache().clear();
cache.getCache().addAll(currentVMs);
if (LOGGER.isTraceEnabled())
{
String cacheStr = StringUtils.join(cache.getCache(), ", ");
LOGGER.trace(
"Cache for Machine {} after generating CREATE/DESTROY is: {}",
physicalMachineAddress, cacheStr);
}
}
finally
{
hyperv.disconnect(physicalMachineAddress);
}
}
}
}
/**
* Propagates events for each created and destroyed virtual machine.
*
* @param pm The physical machine being monitored.
* @param currentVMs The current virtual machines in the hypervisor.
*/
private void propagateCreateAndDestroyEvents(final PhysicalMachine pm,
final Set<String> currentVMs)
{
// Propagate DESTROY events
Set<String> removedVMs = pm.getVirtualMachines().getCache();
removedVMs.removeAll(currentVMs);
for (String removed : removedVMs)
{
VMEvent event = new VMEvent(VMEventType.DESTROYED, pm.getAddress(), removed);
LOGGER.trace("Removed VM {} from {}", removed, pm.getAddress());
HyperVMonitor.this.notify(event);
}
// Propagate CREATE events
Set<String> createdVMs = new HashSet<String>(currentVMs);
createdVMs.removeAll(pm.getVirtualMachines().getCache());
for (String created : createdVMs)
{
VMEvent event = new VMEvent(VMEventType.CREATED, pm.getAddress(), created);
LOGGER.trace("New VM {} at {}", created, pm.getAddress());
HyperVMonitor.this.notify(event);
}
}
}
}