/**
* Copyright (c) 2015 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.impl.services.sal;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.math.BigInteger;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext.CONNECTION_STATE;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
import org.opendaylight.openflowplugin.api.openflow.device.Xid;
import org.opendaylight.openflowplugin.impl.role.RoleChangeException;
import org.opendaylight.openflowplugin.impl.services.AbstractSimpleService;
import org.opendaylight.openflowplugin.impl.services.RoleService;
import org.opendaylight.openflowplugin.impl.services.util.ServiceException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput, SetRoleOutput> implements SalRoleService {
private static final Logger LOG = LoggerFactory.getLogger(SalRoleServiceImpl.class);
private static final BigInteger MAX_GENERATION_ID = new BigInteger("ffffffffffffffff", 16);
private static final int MAX_RETRIES = 42;
private final DeviceContext deviceContext;
private final RoleService roleService;
public SalRoleServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
super(requestContextStack, deviceContext, SetRoleOutput.class);
this.deviceContext = Preconditions.checkNotNull(deviceContext);
this.roleService = new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
}
@Override
protected OfHeader buildRequest(final Xid xid, final SetRoleInput input) throws ServiceException {
return null;
}
@Override
public Future<RpcResult<SetRoleOutput>> setRole(final SetRoleInput input) {
LOG.info("SetRole called with input:{}", input);
final SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.create();
repeaterForChangeRole(resultFuture, input, 0);
/* Add Callback for release Guard */
Futures.addCallback(resultFuture, new FutureCallback<RpcResult<SetRoleOutput>>() {
@Override
public void onSuccess(final RpcResult<SetRoleOutput> result) {
LOG.debug("SetRoleService for Node: {} is ok Role: {}", input.getNode().getValue(),
input.getControllerRole());
}
@Override
public void onFailure(final Throwable t) {
LOG.error("SetRoleService set Role {} for Node: {} fail . Reason {}", input.getControllerRole(),
input.getNode().getValue(), t);
}
});
return resultFuture;
}
private void repeaterForChangeRole(final SettableFuture<RpcResult<SetRoleOutput>> future, final SetRoleInput input,
final int retryCounter) {
if (future.isCancelled()) {
future.setException(new RoleChangeException(String.format(
"Set Role for device %s stop because Future was canceled", input.getNode().getValue())));
return;
}
if (retryCounter >= MAX_RETRIES) {
future.setException(new RoleChangeException(String.format("Set Role failed after %s tries on device %s",
MAX_RETRIES, input.getNode().getValue())));
return;
}
// Check current connection state
final CONNECTION_STATE state = deviceContext.getPrimaryConnectionContext().getConnectionState();
switch (state) {
case RIP:
LOG.info("Device {} has been disconnected", input.getNode());
future.setException(new Exception(String.format(
"Device connection doesn't exist anymore. Primary connection status : %s", state)));
return;
case WORKING:
// We can proceed
LOG.trace("Device {} has been working", input.getNode());
break;
default:
LOG.warn("Device {} is in state {}, role change is not allowed", input.getNode(), state);
future.setException(new Exception(String.format("Unexcpected device connection status : %s", state)));
return;
}
LOG.info("Requesting state change to {}", input.getControllerRole());
final ListenableFuture<RpcResult<SetRoleOutput>> changeRoleFuture = tryToChangeRole(input.getControllerRole());
Futures.addCallback(changeRoleFuture, new FutureCallback<RpcResult<SetRoleOutput>>() {
@Override
public void onSuccess(final RpcResult<SetRoleOutput> result) {
if (result.isSuccessful()) {
LOG.debug("setRoleOutput received after roleChangeTask execution:{}", result);
future.set(RpcResultBuilder.<SetRoleOutput> success().withResult(result.getResult()).build());
} else {
LOG.error("setRole() failed with errors, will retry: {} times.", MAX_RETRIES - retryCounter);
repeaterForChangeRole(future, input, (retryCounter + 1));
}
}
@Override
public void onFailure(final Throwable t) {
LOG.error("Exception in setRole(), will retry: {} times.", t, MAX_RETRIES - retryCounter);
repeaterForChangeRole(future, input, (retryCounter + 1));
}
});
}
private ListenableFuture<RpcResult<SetRoleOutput>> tryToChangeRole(final OfpRole role) {
LOG.info("RoleChangeTask called on device:{} OFPRole:{}", getDeviceInfo().getNodeId().getValue(), role);
final Future<BigInteger> generationFuture = roleService.getGenerationIdFromDevice(getVersion());
return Futures.transform(JdkFutureAdapters.listenInPoolThread(generationFuture), (AsyncFunction<BigInteger, RpcResult<SetRoleOutput>>) generationId -> {
LOG.debug("RoleChangeTask, GenerationIdFromDevice from device {} is {}", getDeviceInfo().getNodeId().getValue(), generationId);
final BigInteger nextGenerationId = getNextGenerationId(generationId);
LOG.debug("nextGenerationId received from device:{} is {}", getDeviceInfo().getNodeId().getValue(), nextGenerationId);
final Future<RpcResult<SetRoleOutput>> submitRoleFuture = roleService.submitRoleChange(role, getVersion(), nextGenerationId);
return JdkFutureAdapters.listenInPoolThread(submitRoleFuture);
});
}
private static BigInteger getNextGenerationId(final BigInteger generationId) {
if (generationId.compareTo(MAX_GENERATION_ID) < 0) {
return generationId.add(BigInteger.ONE);
} else {
return BigInteger.ZERO;
}
}
}