/**
* Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowplugin.openflow.md.core.session;
import com.google.common.base.Preconditions;
import java.math.BigInteger;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionContext;
import org.opendaylight.openflowplugin.openflow.md.core.MessageFactory;
import org.opendaylight.openflowplugin.openflow.md.util.RoleUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.common.config.impl.rev140326.OfpRole;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
/**
* push role to device - basic step:
* <ul>
* <li>here we read generationId from device and</li>
* <li>push role request with incremented generationId</li>
* <li>{@link #call()} returns true if role request was successful</li>
* </ul>
*/
//final class RolePushTask implements Callable<Boolean> {
public class RolePushTask implements Callable<Boolean> {
private static final Logger LOG = LoggerFactory
.getLogger(RolePushTask.class);
public static final long TIMEOUT = 7000;
public static final TimeUnit TIMEOUT_UNIT = TimeUnit.MILLISECONDS;
private OfpRole role;
private SessionContext session;
private int priority;
private int retryCounter;
/**
* @param role openflow controller role
* @param session switch session context
*/
public RolePushTask(OfpRole role, SessionContext session) {
Preconditions.checkNotNull("OfpRole can not be empty.", role);
Preconditions.checkNotNull("Session context can not be empty.", session);
this.role = role;
this.session = session;
}
/**
* @return the retryCounter
*/
public int getRetryCounter() {
return retryCounter;
}
/**
* @return the priority
*/
public int getPriority() {
return priority;
}
/**
* @param priority the priority to set
*/
public void setPriority(int priority) {
this.priority = priority;
}
@Override
public Boolean call() throws RolePushException {
if (session.getPrimaryConductor().getVersion() == OFConstants.OFP_VERSION_1_0) {
LOG.info("OpenFlow 1.0 devices don't support multi controller features, skipping role push.");
return true;
}
if (!session.isValid()) {
String msg = "Giving up role change: current session is invalid";
LOG.error(msg);
throw new RolePushException(msg);
}
// adopt actual generationId from device (first shot failed and this is retry)
BigInteger generationId = null;
String dpId = new BigInteger(session.getSessionKey().getId()).toString();
LOG.info("Pushing {} role configuration to device openflow:{}",
role==OfpRole.BECOMEMASTER?"MASTER":"SLAVE", dpId);
try {
Date date = new Date();
Future<BigInteger> generationIdFuture = RoleUtil.readGenerationIdFromDevice(session);
// flush election result with barrier
BarrierInput barrierInput = MessageFactory.createBarrier(
session.getFeatures().getVersion(), session.getNextXid());
Future<RpcResult<BarrierOutput>> barrierResult = session.getPrimaryConductor().getConnectionAdapter().barrier(barrierInput);
try {
barrierResult.get(TIMEOUT, TIMEOUT_UNIT);
} catch (Exception e) {
String msg = String.format("Giving up role change: barrier after read generation-id failed : %s", e.getMessage());
LOG.warn(msg);
throw new RolePushException(msg);
}
try {
generationId = generationIdFuture.get(0, TIMEOUT_UNIT);
} catch (Exception e) {
String msg = String.format("Giving up role change: read generation-id failed %s", e.getMessage());
throw new RolePushException(msg);
}
LOG.info("Received generation-id {} for role change request from device {}",
generationId, dpId);
} catch (Exception e) {
LOG.error("Role push request failed for device {}",session.getSessionKey().getId(), e);
}
if (generationId == null) {
LOG.error("Generation ID is NULL for device {}",session.getSessionKey().getId());
String msg = "Giving up role change: current generation-id can not be read";
throw new RolePushException(msg);
}
generationId = RoleUtil.getNextGenerationId(generationId);
LOG.info("Pushing role change {} config request with generation-id {} to device {}",
role==OfpRole.BECOMEMASTER?"MASTER":"SLAVE", generationId, dpId);
// try to possess role on device
Future<RpcResult<RoleRequestOutput>> roleReply = RoleUtil.sendRoleChangeRequest(session, role, generationId);
// flush election result with barrier
BarrierInput barrierInput = MessageFactory.createBarrier(
session.getFeatures().getVersion(), session.getNextXid());
Future<RpcResult<BarrierOutput>> barrierResult = session.getPrimaryConductor().getConnectionAdapter().barrier(barrierInput);
try {
barrierResult.get(TIMEOUT, TIMEOUT_UNIT);
} catch (Exception e) {
String msg = String.format("Giving up role change: barrier after role change failed: %s", e.getMessage());
LOG.warn(msg);
throw new RolePushException(msg);
}
// after barrier replied there must be election result or error
try {
roleReply.get(0, TimeUnit.MILLISECONDS);
} catch (Exception e) {
// no election result received - let's retry
retryCounter += 1;
return false;
}
// here we expect that role on device is successfully possessed
LOG.info("Successfully pushing {} role to the device openflow:{}",
role==OfpRole.BECOMEMASTER?"MASTER":"SLAVE", dpId);
return true;
}
}