2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.openflowplugin.impl.services;
10 import com.google.common.base.Preconditions;
11 import com.google.common.util.concurrent.AsyncFunction;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.JdkFutureAdapters;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.SettableFuture;
17 import java.math.BigInteger;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.Semaphore;
20 import javax.annotation.concurrent.GuardedBy;
21 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext.CONNECTION_STATE;
22 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
23 import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
24 import org.opendaylight.openflowplugin.api.openflow.device.Xid;
25 import org.opendaylight.openflowplugin.impl.role.RoleChangeException;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
33 import org.opendaylight.yangtools.yang.common.RpcResult;
34 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput, SetRoleOutput> implements SalRoleService {
41 private static final Logger LOG = LoggerFactory.getLogger(SalRoleServiceImpl.class);
43 private static final BigInteger MAX_GENERATION_ID = new BigInteger("ffffffffffffffff", 16);
45 private static final int MAX_RETRIES = 42;
47 private final DeviceContext deviceContext;
48 private final RoleService roleService;
50 private final Semaphore currentRoleGuard = new Semaphore(1, true);
52 @GuardedBy("currentRoleGuard")
53 private OfpRole currentRole = OfpRole.NOCHANGE;
55 public SalRoleServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
56 super(requestContextStack, deviceContext, SetRoleOutput.class);
57 this.deviceContext = Preconditions.checkNotNull(deviceContext);
58 this.roleService = new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
62 protected OfHeader buildRequest(final Xid xid, final SetRoleInput input) {
67 public Future<RpcResult<SetRoleOutput>> setRole(final SetRoleInput input) {
68 LOG.info("SetRole called with input:{}", input);
70 currentRoleGuard.acquire();
71 LOG.trace("currentRole lock queue length: {} " + currentRoleGuard.getQueueLength());
72 } catch (final InterruptedException e) {
73 LOG.error("Unexpected exception {} for acquire semaphor for input {}", e, input);
74 return RpcResultBuilder.<SetRoleOutput> failed().buildFuture();
76 // compare with last known role and set if different. If they are same, then return.
77 if (currentRole.equals(input.getControllerRole())) {
78 LOG.info("Role to be set is same as the last known role for the device:{}. Hence ignoring.",
79 input.getControllerRole());
80 currentRoleGuard.release();
81 return RpcResultBuilder.<SetRoleOutput> success().buildFuture();
84 final SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.<RpcResult<SetRoleOutput>> create();
85 repeaterForChangeRole(resultFuture, input, 0);
86 /* Add Callback for release Guard */
87 Futures.addCallback(resultFuture, new FutureCallback<RpcResult<SetRoleOutput>>() {
90 public void onSuccess(final RpcResult<SetRoleOutput> result) {
91 LOG.debug("SetRoleService for Node: {} is ok Role: {}", input.getNode().getValue(),
92 input.getControllerRole());
93 currentRoleGuard.release();
97 public void onFailure(final Throwable t) {
98 LOG.error("SetRoleService set Role {} for Node: {} fail . Reason {}", input.getControllerRole(),
99 input.getNode().getValue(), t);
100 currentRoleGuard.release();
106 private void repeaterForChangeRole(final SettableFuture<RpcResult<SetRoleOutput>> future, final SetRoleInput input,
107 final int retryCounter) {
108 if (future.isCancelled()) {
109 future.setException(new RoleChangeException(String.format(
110 "Set Role for device %s stop because Future was canceled", input.getNode().getValue())));
113 if (retryCounter >= MAX_RETRIES) {
114 future.setException(new RoleChangeException(String.format("Set Role failed after %s tries on device %s",
115 MAX_RETRIES, input.getNode().getValue())));
118 // Check current connection state
119 final CONNECTION_STATE state = deviceContext.getPrimaryConnectionContext().getConnectionState();
122 LOG.info("Device {} has been disconnected", input.getNode());
123 future.setException(new Exception(String.format(
124 "Device connection doesn't exist anymore. Primary connection status : %s", state)));
128 LOG.trace("Device {} has been working", input.getNode());
131 LOG.warn("Device {} is in state {}, role change is not allowed", input.getNode(), state);
132 future.setException(new Exception(String.format("Unexcpected device connection status : %s", state)));
136 LOG.info("Requesting state change to {}", input.getControllerRole());
137 final ListenableFuture<RpcResult<SetRoleOutput>> changeRoleFuture = tryToChangeRole(input.getControllerRole());
138 Futures.addCallback(changeRoleFuture, new FutureCallback<RpcResult<SetRoleOutput>>() {
141 public void onSuccess(final RpcResult<SetRoleOutput> result) {
142 if (result.isSuccessful()) {
143 LOG.debug("setRoleOutput received after roleChangeTask execution:{}", result);
144 currentRole = input.getControllerRole();
145 future.set(RpcResultBuilder.<SetRoleOutput> success().withResult(result.getResult()).build());
147 LOG.error("setRole() failed with errors, will retry: {} times.", MAX_RETRIES - retryCounter);
148 repeaterForChangeRole(future, input, (retryCounter + 1));
153 public void onFailure(final Throwable t) {
154 LOG.error("Exception in setRole(), will retry: {} times.", t, MAX_RETRIES - retryCounter);
155 repeaterForChangeRole(future, input, (retryCounter + 1));
160 private ListenableFuture<RpcResult<SetRoleOutput>> tryToChangeRole(final OfpRole role) {
161 LOG.info("RoleChangeTask called on device:{} OFPRole:{}", getNodeId().getValue(), role);
163 final Future<BigInteger> generationFuture = roleService.getGenerationIdFromDevice(getVersion());
165 return Futures.transform(JdkFutureAdapters.listenInPoolThread(generationFuture), (AsyncFunction<BigInteger, RpcResult<SetRoleOutput>>) generationId -> {
166 LOG.debug("RoleChangeTask, GenerationIdFromDevice from device {} is {}", getNodeId().getValue(), generationId);
167 final BigInteger nextGenerationId = getNextGenerationId(generationId);
168 LOG.debug("nextGenerationId received from device:{} is {}", getNodeId().getValue(), nextGenerationId);
169 final Future<RpcResult<SetRoleOutput>> submitRoleFuture = roleService.submitRoleChange(role, getVersion(), nextGenerationId);
170 return JdkFutureAdapters.listenInPoolThread(submitRoleFuture);
174 private static BigInteger getNextGenerationId(final BigInteger generationId) {
175 if (generationId.compareTo(MAX_GENERATION_ID) < 0) {
176 return generationId.add(BigInteger.ONE);
178 return BigInteger.ZERO;