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.base.Preconditions;
12 import com.google.common.util.concurrent.CheckedFuture;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.ListeningExecutorService;
16 import com.google.common.util.concurrent.MoreExecutors;
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.CONNECTION_STATE;
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 final 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 static final Function<Exception, RoleChangeException> EXCEPTION_FUNCTION = new Function<Exception, RoleChangeException>() {
53 public RoleChangeException apply(final Exception input) {
54 if (input instanceof ExecutionException) {
55 final Throwable cause = input.getCause();
56 if (cause instanceof RoleChangeException) {
57 return (RoleChangeException) cause;
59 } else if (input instanceof RoleChangeException) {
60 return (RoleChangeException) input;
63 return new RoleChangeException(input.getMessage(), input);
67 private final DeviceContext deviceContext;
68 private final RoleService roleService;
69 private final AtomicReference<OfpRole> lastKnownRoleRef = new AtomicReference<>(OfpRole.NOCHANGE);
70 private final ListeningExecutorService listeningExecutorService;
71 private final NodeId nodeId;
72 private final Short version;
74 public SalRoleServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
75 super(requestContextStack, deviceContext, SetRoleOutput.class);
76 this.deviceContext = Preconditions.checkNotNull(deviceContext);
77 this.roleService = new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
78 nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
79 version = deviceContext.getPrimaryConnectionContext().getFeatures().getVersion();
80 listeningExecutorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
84 protected OfHeader buildRequest(final Xid xid, final SetRoleInput input) {
89 public Future<RpcResult<SetRoleOutput>> setRole(final SetRoleInput input) {
90 LOG.info("SetRole called with input:{}", input);
92 // compare with last known role and set if different. If they are same, then return.
93 if (lastKnownRoleRef.compareAndSet(input.getControllerRole(), input.getControllerRole())) {
94 LOG.info("Role to be set is same as the last known role for the device:{}. Hence ignoring.", input.getControllerRole());
95 return Futures.immediateFuture(RpcResultBuilder.<SetRoleOutput>success().build());
98 RoleChangeTask roleChangeTask = new RoleChangeTask(input.getControllerRole());
101 // Check current connection state
102 final CONNECTION_STATE state = deviceContext.getPrimaryConnectionContext().getConnectionState();
105 LOG.info("Device {} has been disconnected", input.getNode());
106 return Futures.immediateFailedFuture(new Exception(String.format(
107 "Device connection doesn't exist anymore. Primary connection status : %s", state)));
112 LOG.info("Device {} is in state {}, role change is not allowed", input.getNode(), state);
113 return Futures.immediateCheckedFuture(RpcResultBuilder.<SetRoleOutput>failed().build());
116 ListenableFuture<SetRoleOutput> taskFuture = listeningExecutorService.submit(roleChangeTask);
117 LOG.info("RoleChangeTask submitted for execution");
118 CheckedFuture<SetRoleOutput, RoleChangeException> taskFutureChecked = Futures.makeChecked(taskFuture, EXCEPTION_FUNCTION);
120 SetRoleOutput setRoleOutput = taskFutureChecked.checkedGet(10, TimeUnit.SECONDS);
121 LOG.info("setRoleOutput received after roleChangeTask execution:{}", setRoleOutput);
122 lastKnownRoleRef.set(input.getControllerRole());
123 return Futures.immediateFuture(RpcResultBuilder.<SetRoleOutput>success().withResult(setRoleOutput).build());
125 } catch (TimeoutException | RoleChangeException e) {
126 roleChangeTask.incrementRetryCounter();
127 LOG.info("Exception in setRole(), will retry: {} times.",
128 MAX_RETRIES - roleChangeTask.getRetryCounter(), e);
131 } while (roleChangeTask.getRetryCounter() < MAX_RETRIES);
133 return Futures.immediateFailedFuture(new RoleChangeException(
134 "Set Role failed after " + MAX_RETRIES + "tries on device " + input.getNode().getValue()));
137 private static BigInteger getNextGenerationId(final BigInteger generationId) {
138 if (generationId.compareTo(MAX_GENERATION_ID) < 0) {
139 return generationId.add(BigInteger.ONE);
141 return BigInteger.ZERO;
145 private final class RoleChangeTask implements Callable<SetRoleOutput> {
147 private final OfpRole ofpRole;
148 private int retryCounter = 0;
150 RoleChangeTask(final OfpRole ofpRole) {
151 this.ofpRole = Preconditions.checkNotNull(ofpRole);
155 public SetRoleOutput call() throws RoleChangeException {
156 LOG.info("RoleChangeTask called on device:{} OFPRole:{}", nodeId.getValue(), ofpRole);
158 // we cannot move ahead without having the generation id, so block the thread till we get it.
159 final BigInteger generationId;
161 generationId = roleService.getGenerationIdFromDevice(version).get(10, TimeUnit.SECONDS);
162 LOG.info("RoleChangeTask, GenerationIdFromDevice from device is {}", generationId);
163 } catch (Exception e ) {
164 LOG.info("Exception in getting generationId for device:{}", nodeId.getValue(), e);
165 throw new RoleChangeException("Exception in getting generationId for device:"+ nodeId.getValue(), e);
168 LOG.info("GenerationId received from device:{} is {}", nodeId.getValue(), generationId);
169 final BigInteger nextGenerationId = getNextGenerationId(generationId);
170 LOG.info("nextGenerationId received from device:{} is {}", nodeId.getValue(), nextGenerationId);
172 final SetRoleOutput setRoleOutput;
174 setRoleOutput = roleService.submitRoleChange(ofpRole, version, nextGenerationId).get(10 , TimeUnit.SECONDS);
175 LOG.info("setRoleOutput after submitRoleChange:{}", setRoleOutput);
177 } catch (InterruptedException | ExecutionException | TimeoutException e) {
178 LOG.error("Exception in making role change for device", e);
179 throw new RoleChangeException("Exception in making role change for device:" + nodeId.getValue());
182 return setRoleOutput;
185 public void incrementRetryCounter() {
189 public int getRetryCounter() {