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.Function;
11 import com.google.common.util.concurrent.CheckedFuture;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.ListeningExecutorService;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import com.google.common.util.concurrent.SettableFuture;
17 import java.math.BigInteger;
18 import java.util.concurrent.Callable;
19 import java.util.concurrent.ExecutionException;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.TimeoutException;
24 import java.util.concurrent.atomic.AtomicReference;
25 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
26 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
27 import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
28 import org.opendaylight.openflowplugin.api.openflow.device.Xid;
29 import org.opendaylight.openflowplugin.impl.role.RoleChangeException;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
37 import org.opendaylight.yangtools.yang.common.RpcResult;
38 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 public class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput, SetRoleOutput> implements SalRoleService {
45 private static final Logger LOG = LoggerFactory.getLogger(SalRoleServiceImpl.class);
47 private static final BigInteger MAX_GENERATION_ID = new BigInteger("ffffffffffffffff", 16);
49 private static final int MAX_RETRIES = 42;
51 private final DeviceContext deviceContext;
52 private final RoleService roleService;
53 private final AtomicReference<OfpRole> lastKnownRoleRef = new AtomicReference<>(OfpRole.NOCHANGE);
54 private final ListeningExecutorService listeningExecutorService;
55 private final NodeId nodeId;
56 private final Short version;
58 public SalRoleServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
59 super(requestContextStack, deviceContext, SetRoleOutput.class);
60 this.deviceContext = deviceContext;
61 this.roleService = new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
62 nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
63 version = deviceContext.getPrimaryConnectionContext().getFeatures().getVersion();
64 listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
69 protected OfHeader buildRequest(Xid xid, SetRoleInput input) {
73 public static BigInteger getNextGenerationId(BigInteger generationId) {
74 BigInteger nextGenerationId = null;
75 if (generationId.compareTo(MAX_GENERATION_ID) < 0) {
76 nextGenerationId = generationId.add(BigInteger.ONE);
78 nextGenerationId = BigInteger.ZERO;
81 return nextGenerationId;
86 public Future<RpcResult<SetRoleOutput>> setRole(final SetRoleInput input) {
87 LOG.info("SetRole called with input:{}", input);
88 OfpRole lastKnownRole = lastKnownRoleRef.get();
90 // compare with last known role and set if different. If they are same, then return.
91 if (lastKnownRoleRef.compareAndSet(input.getControllerRole(), input.getControllerRole())) {
92 LOG.info("Role to be set is same as the last known role for the device:{}. Hence ignoring.", input.getControllerRole());
93 SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.create();
94 resultFuture.set(RpcResultBuilder.<SetRoleOutput>success().build());
98 final SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.create();
100 RoleChangeTask roleChangeTask = new RoleChangeTask(nodeId, input.getControllerRole(), version, roleService);
103 ListenableFuture<RpcResult<SetRoleOutput>> deviceCheck = deviceConnectionCheck();
104 if (deviceCheck != null) {
105 LOG.info("Device {} is disconnected or state is not valid. Giving up on role change", input.getNode());
109 ListenableFuture<SetRoleOutput> taskFuture = listeningExecutorService.submit(roleChangeTask);
110 LOG.info("RoleChangeTask submitted for execution");
111 CheckedFuture<SetRoleOutput, RoleChangeException> taskFutureChecked = makeCheckedFuture(taskFuture);
113 SetRoleOutput setRoleOutput = taskFutureChecked.checkedGet(10, TimeUnit.SECONDS);
114 LOG.info("setRoleOutput received after roleChangeTask execution:{}", setRoleOutput);
115 resultFuture.set(RpcResultBuilder.<SetRoleOutput>success().withResult(setRoleOutput).build());
116 lastKnownRoleRef.set(input.getControllerRole());
119 } catch (TimeoutException | RoleChangeException e) {
120 roleChangeTask.incrementRetryCounter();
121 LOG.info("Exception in setRole(), will retry:" + (MAX_RETRIES - roleChangeTask.getRetryCounter()) + " times.", e);
124 } while (roleChangeTask.getRetryCounter() < MAX_RETRIES);
126 resultFuture.setException(new RoleChangeException("Set Role failed after " + MAX_RETRIES + "tries on device " + input.getNode().getValue()));
131 private ListenableFuture<RpcResult<SetRoleOutput>> deviceConnectionCheck() {
132 if (!ConnectionContext.CONNECTION_STATE.WORKING.equals(deviceContext.getPrimaryConnectionContext().getConnectionState())) {
133 ListenableFuture<RpcResult<SetRoleOutput>> resultingFuture = SettableFuture.create();
134 switch (deviceContext.getPrimaryConnectionContext().getConnectionState()) {
136 final String errMsg = String.format("Device connection doesn't exist anymore. Primary connection status : %s",
137 deviceContext.getPrimaryConnectionContext().getConnectionState());
138 resultingFuture = Futures.immediateFailedFuture(new Throwable(errMsg));
141 resultingFuture = Futures.immediateCheckedFuture(RpcResultBuilder.<SetRoleOutput>failed().build());
144 return resultingFuture;
149 class RoleChangeTask implements Callable<SetRoleOutput> {
151 private final NodeId nodeId;
152 private final OfpRole ofpRole;
153 private final Short version;
154 private final RoleService roleService;
155 private int retryCounter = 0;
157 public RoleChangeTask(NodeId nodeId, OfpRole ofpRole, Short version, RoleService roleService) {
158 this.nodeId = nodeId;
159 this.ofpRole = ofpRole;
160 this.version = version;
161 this.roleService = roleService;
165 public SetRoleOutput call() throws RoleChangeException {
166 LOG.info("RoleChangeTask called on device:{} OFPRole:{}", this.nodeId.getValue(), ofpRole);
168 // we cannot move ahead without having the generation id, so block the thread till we get it.
169 BigInteger generationId = null;
170 SetRoleOutput setRoleOutput = null;
173 generationId = this.roleService.getGenerationIdFromDevice(version).get(10, TimeUnit.SECONDS);
174 LOG.info("RoleChangeTask, GenerationIdFromDevice from device is {}", generationId);
176 } catch (Exception e ) {
177 LOG.info("Exception in getting generationId for device:{}. Ex:{}" + this.nodeId.getValue(), e);
178 throw new RoleChangeException("Exception in getting generationId for device:"+ this.nodeId.getValue(), e);
182 LOG.info("GenerationId received from device:{} is {}", nodeId.getValue(), generationId);
184 final BigInteger nextGenerationId = getNextGenerationId(generationId);
186 LOG.info("nextGenerationId received from device:{} is {}", nodeId.getValue(), nextGenerationId);
189 setRoleOutput = roleService.submitRoleChange(ofpRole, version, nextGenerationId).get(10 , TimeUnit.SECONDS);
190 LOG.info("setRoleOutput after submitRoleChange:{}", setRoleOutput);
192 } catch (InterruptedException | ExecutionException | TimeoutException e) {
193 LOG.error("Exception in making role change for device", e);
194 throw new RoleChangeException("Exception in making role change for device:" + nodeId.getValue());
197 return setRoleOutput;
201 public void incrementRetryCounter() {
202 this.retryCounter = retryCounter + 1;
205 public int getRetryCounter() {
210 public static CheckedFuture<SetRoleOutput, RoleChangeException> makeCheckedFuture(ListenableFuture<SetRoleOutput> rolePushResult) {
211 return Futures.makeChecked(rolePushResult,
212 new Function<Exception, RoleChangeException>() {
214 public RoleChangeException apply(Exception input) {
215 RoleChangeException output = null;
216 if (input instanceof ExecutionException) {
217 if (input.getCause() instanceof RoleChangeException) {
218 output = (RoleChangeException) input.getCause();
222 if (output == null) {
223 output = new RoleChangeException(input.getMessage(), input);