/**
* 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.groupmanager;
import java.net.BindException;
import org.inria.myriads.snoozecommon.communication.NetworkAddress;
import org.inria.myriads.snoozecommon.communication.NodeRole;
import org.inria.myriads.snoozecommon.communication.groupmanager.GroupManagerDescription;
import org.inria.myriads.snoozecommon.guard.Guard;
import org.inria.myriads.snoozenode.configurator.api.NodeConfiguration;
import org.inria.myriads.snoozenode.configurator.faulttolerance.ZooKeeperSettings;
import org.inria.myriads.snoozenode.configurator.networking.NetworkingSettings;
import org.inria.myriads.snoozenode.exception.GroupManagerInitException;
import org.inria.myriads.snoozenode.groupmanager.init.GroupLeaderInit;
import org.inria.myriads.snoozenode.groupmanager.init.GroupManagerInit;
import org.inria.myriads.snoozenode.groupmanager.leadelection.LeaderElectionFactory;
import org.inria.myriads.snoozenode.groupmanager.leadelection.api.LeaderElection;
import org.inria.myriads.snoozenode.groupmanager.leadelection.listener.LeaderElectionListener;
import org.inria.myriads.snoozenode.heartbeat.HeartbeatFactory;
import org.inria.myriads.snoozenode.heartbeat.listener.HeartbeatListener;
import org.inria.myriads.snoozenode.heartbeat.message.HeartbeatMessage;
import org.inria.myriads.snoozenode.heartbeat.receiver.HeartbeatMulticastReceiver;
import org.inria.myriads.snoozenode.message.SystemMessage;
import org.inria.myriads.snoozenode.message.SystemMessageType;
import org.inria.myriads.snoozenode.util.ExternalNotifierUtils;
import org.inria.myriads.snoozenode.util.ManagementUtils;
import org.inria.snoozenode.external.notifier.ExternalNotificationType;
import org.inria.snoozenode.external.notifier.ExternalNotifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Group manager backend logic.
*
* @author Eugen Feller
*/
public final class GroupManagerBackend
implements LeaderElectionListener, HeartbeatListener
{
/** Define the logger. */
private static final Logger log_ = LoggerFactory.getLogger(GroupManagerBackend.class);
/** Group leader initialization. */
private GroupLeaderInit groupLeaderInit_;
/** Group manager logic. */
private GroupManagerInit groupManagerInit_;
/** Nodeparams reference. */
private NodeConfiguration nodeConfiguration_;
/** Group manager description. */
private GroupManagerDescription groupManagerDescription_;
/** Heartbeat listener thread. */
private HeartbeatMulticastReceiver heartbeatListener_;
/** Heartbeat messsage. */
private HeartbeatMessage heartbeat_;
/** Indicates successfull assignement. */
private boolean isAssigned_;
/** External Notifier.*/
private ExternalNotifier externalNotifier_;
/**
* Constructor.
*
* @param nodeConfiguration The node parameters
* @throws Exception The exception
*/
public GroupManagerBackend(NodeConfiguration nodeConfiguration)
throws Exception
{
Guard.check(nodeConfiguration);
log_.debug("Starting group manager backend");
nodeConfiguration_ = nodeConfiguration;
initializeExternalNotifier();
createGroupManagerDescription();
initializeLeaderElection();
}
/**
* Initializes external notifier.
*/
private void initializeExternalNotifier()
{
externalNotifier_ = new ExternalNotifier(nodeConfiguration_);
}
/**
* Creates group manager description.
*/
private void createGroupManagerDescription()
{
NodeRole nodeRole = nodeConfiguration_.getNode().getRole();
NetworkingSettings networkingSettings = nodeConfiguration_.getNetworking();
groupManagerDescription_ = ManagementUtils.createGroupManagerDescription(nodeRole, networkingSettings);
}
/**
* Starts the heartbeat multicast listener.
*
* @throws Exception Exception
*/
public void onInitGroupManager()
throws Exception
{
if (heartbeatListener_ == null)
{
log_.debug("Starting the group leader multicast listener");
NetworkAddress address =
nodeConfiguration_.getNetworking().getMulticast().getGroupLeaderHeartbeatAddress();
int timeout = nodeConfiguration_.getFaultTolerance().getHeartbeat().getTimeout();
heartbeatListener_ = HeartbeatFactory.newHeartbeatMulticastListener(address, timeout, this);
new Thread(heartbeatListener_, "HeartBeatListener").start();
}
}
/**
* Starts the leader elector.
* @throws Exception
*/
private void initializeLeaderElection()
throws Exception
{
ZooKeeperSettings settings = nodeConfiguration_.getFaultTolerance().getZooKeeper();
LeaderElection leaderElection = LeaderElectionFactory.newLeaderElection(settings,
groupManagerDescription_,
this);
leaderElection.start();
}
/**
* Returns the group leader initialization.
*
* @return The group leader initialization
*/
public GroupLeaderInit getGroupLeaderInit()
{
return groupLeaderInit_;
}
/**
* Returns the group maanger initialization.
*
* @return The group manager initialization
*/
public GroupManagerInit getGroupManagerInit()
{
return groupManagerInit_;
}
/**
* Returns the node parameters.
*
* @return The node parameters
*/
public NodeConfiguration getNodeConfiguration()
{
return nodeConfiguration_;
}
/**
* Returns the group manager description.
*
* @return The group manager description
*/
public GroupManagerDescription getGroupManagerDescription()
{
return groupManagerDescription_;
}
/**
* Prepares the group leader switch.
*
* @return true if preparation was ok, false otherwise
*/
private boolean prepareGroupLeaderSwitch()
{
log_.debug("Preparing group leader switch");
try
{
if (heartbeatListener_ != null)
{
heartbeatListener_.terminate();
}
if (groupManagerInit_ != null)
{
groupManagerInit_.getRepository().fillGroupManagerDescription(groupManagerDescription_);
groupManagerInit_.stopServices();
}
}
catch (Exception exception)
{
log_.error("Exception during group leader switch preparation", exception);
return false;
}
return true;
}
/**
* Called by the leader election algorithm if current group manager becomes group leader.
*
* @return true if everything ok, false otherwise
*/
public boolean onInitGroupLeader()
{
log_.debug("Starting the group leader logic");
boolean isPrepared = prepareGroupLeaderSwitch();
if (!isPrepared)
{
return false;
}
try
{
groupLeaderInit_ = new GroupLeaderInit(nodeConfiguration_, groupManagerDescription_, externalNotifier_);
}
catch (Exception exception)
{
log_.error("Exception", exception);
}
finally
{
if (groupLeaderInit_ != null)
{
log_.debug("Group leader logic started successfully!");
return true;
}
}
return false;
}
/**
* Heartbeat event arrival event.
*
* @param heartbeat Heartbeat message
*/
public void onHeartbeatArrival(HeartbeatMessage heartbeat)
{
Guard.check(heartbeat);
log_.debug(String.format("Received group leader heartbeat message from: %s, port: %d",
heartbeat.getListenSettings().getControlDataAddress().getAddress(),
heartbeat.getListenSettings().getControlDataAddress().getPort()));
if (heartbeat_ == null || heartbeat_.getId().compareTo(heartbeat.getId()) != 0)
{
log_.debug("Updating global heartbeat information!");
heartbeat_ = heartbeat;
}
else if (groupManagerDescription_.getIsAssigned())
{
log_.debug("Ignoring heartbeat message! Already assigned to working group leader!");
return;
}
boolean hasJoined = false;
try
{
if (groupManagerInit_ == null)
{
groupManagerInit_ =
new GroupManagerInit(nodeConfiguration_, groupManagerDescription_, externalNotifier_);
}
GroupManagerDescription groupLeader = ManagementUtils.createGroupLeaderDescriptionFromHeartbeat(heartbeat);
hasJoined = groupManagerInit_.onGroupLeaderJoin(groupLeader);
}
catch (GroupManagerInitException exception)
{
log_.error(String.format("Group manager initialization error: %s", exception.getMessage()));
}
catch (BindException exception)
{
log_.error(String.format("Binding error: %s", exception.getMessage()));
}
catch (Exception exception)
{
log_.error("Exception", exception);
}
finally
{
if (hasJoined)
{
log_.debug("Group leader joined successfully!");
groupManagerDescription_.setIsAssigned(true);
} else
{
log_.debug("Failed to join the group leader!");
}
}
}
/**
* Called when heartbeat message was not received for timeout.
*/
public void onHeartbeatFailure()
{
log_.debug("Failed to receive group leader heartbeat message!");
groupManagerDescription_.setIsAssigned(false);
ExternalNotifierUtils.send(
externalNotifier_,
ExternalNotificationType.SYSTEM,
new SystemMessage(SystemMessageType.GL_FAILED, groupManagerDescription_),
"groupmanager." + groupManagerDescription_.getId());
}
}