e6a26f88b6117f77ae1e465f782a5ccba547043f
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / services / sal / SalRoleServiceImpl.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.impl.services.sal;
9
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 org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext.CONNECTION_STATE;
20 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
21 import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
22 import org.opendaylight.openflowplugin.api.openflow.device.Xid;
23 import org.opendaylight.openflowplugin.impl.role.RoleChangeException;
24 import org.opendaylight.openflowplugin.impl.services.AbstractSimpleService;
25 import org.opendaylight.openflowplugin.impl.services.RoleService;
26 import org.opendaylight.openflowplugin.impl.services.util.ServiceException;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.ErrorType;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
34 import org.opendaylight.yangtools.yang.common.RpcError;
35 import org.opendaylight.yangtools.yang.common.RpcResult;
36 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40
41 public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput, SetRoleOutput> implements SalRoleService  {
42
43     private static final Logger LOG = LoggerFactory.getLogger(SalRoleServiceImpl.class);
44
45     private static final BigInteger MAX_GENERATION_ID = new BigInteger("ffffffffffffffff", 16);
46
47     private static final int MAX_RETRIES = 42;
48
49     private static final String ROLE_REQUEST_UNSUPPORTED = ErrorType.ROLEREQUESTFAILED.name().concat(" code UNSUP");
50
51     private final DeviceContext deviceContext;
52     private final RoleService roleService;
53
54     public SalRoleServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
55         super(requestContextStack, deviceContext, SetRoleOutput.class);
56         this.deviceContext = Preconditions.checkNotNull(deviceContext);
57         this.roleService =  new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
58     }
59
60     @Override
61     protected OfHeader buildRequest(final Xid xid, final SetRoleInput input) throws ServiceException {
62         return null;
63     }
64
65     @Override
66     public Future<RpcResult<SetRoleOutput>> setRole(final SetRoleInput input) {
67         LOG.info("SetRole called with input:{}", input);
68
69         final SettableFuture<RpcResult<SetRoleOutput>> resultFuture = SettableFuture.create();
70         repeaterForChangeRole(resultFuture, input, 0);
71         /* Add Callback for release Guard */
72         Futures.addCallback(resultFuture, new FutureCallback<RpcResult<SetRoleOutput>>() {
73
74             @Override
75             public void onSuccess(final RpcResult<SetRoleOutput> result) {
76                 LOG.debug("SetRoleService for Node: {} is ok Role: {}", input.getNode().getValue(),
77                         input.getControllerRole());
78             }
79
80             @Override
81             public void onFailure(final Throwable t) {
82                 LOG.error("SetRoleService set Role {} for Node: {} fail . Reason {}", input.getControllerRole(),
83                         input.getNode().getValue(), t);
84             }
85         });
86         return resultFuture;
87     }
88
89     private void repeaterForChangeRole(final SettableFuture<RpcResult<SetRoleOutput>> future, final SetRoleInput input,
90             final int retryCounter) {
91         if (future.isCancelled()) {
92             future.setException(new RoleChangeException(String.format(
93                     "Set Role for device %s stop because Future was canceled", input.getNode().getValue())));
94             return;
95         }
96         if (retryCounter >= MAX_RETRIES) {
97             future.setException(new RoleChangeException(String.format("Set Role failed after %s tries on device %s",
98                     MAX_RETRIES, input.getNode().getValue())));
99             return;
100         }
101         // Check current connection state
102         final CONNECTION_STATE state = deviceContext.getPrimaryConnectionContext().getConnectionState();
103         switch (state) {
104         case RIP:
105             LOG.info("Device {} has been disconnected", input.getNode());
106             future.setException(new Exception(String.format(
107                     "Device connection doesn't exist anymore. Primary connection status : %s", state)));
108             return;
109         case WORKING:
110             // We can proceed
111             LOG.trace("Device {} has been working", input.getNode());
112             break;
113         default:
114             LOG.warn("Device {} is in state {}, role change is not allowed", input.getNode(), state);
115             future.setException(new Exception(String.format("Unexcpected device connection status : %s", state)));
116             return;
117         }
118
119         LOG.info("Requesting state change to {}", input.getControllerRole());
120         final ListenableFuture<RpcResult<SetRoleOutput>> changeRoleFuture = tryToChangeRole(input.getControllerRole());
121         Futures.addCallback(changeRoleFuture, new FutureCallback<RpcResult<SetRoleOutput>>() {
122
123             @Override
124             public void onSuccess(final RpcResult<SetRoleOutput> result) {
125                 if (result.isSuccessful()) {
126                     LOG.debug("setRoleOutput received after roleChangeTask execution:{}", result);
127                     future.set(RpcResultBuilder.<SetRoleOutput> success().withResult(result.getResult()).build());
128                 } else {
129                     final boolean present = result
130                             .getErrors()
131                             .stream()
132                             .anyMatch(rpcError -> (rpcError.getMessage().contains(ROLE_REQUEST_UNSUPPORTED)));
133
134                     if (!present) {
135                         LOG.warn("setRole() failed with errors, will retry: {} times.", MAX_RETRIES - retryCounter);
136                         repeaterForChangeRole(future, input, (retryCounter + 1));
137                     } else {
138                         LOG.warn("setRole() failed with error - role request unsupported.");
139                         future.set(result);
140                     }
141                 }
142             }
143
144             @Override
145             public void onFailure(final Throwable t) {
146                 if (!t.getMessage().contains(ROLE_REQUEST_UNSUPPORTED)) {
147                     LOG.warn("Exception in setRole(), will retry: {} times.", t, MAX_RETRIES - retryCounter);
148                     repeaterForChangeRole(future, input, (retryCounter + 1));
149                 } else {
150                     LOG.warn("Exception in setRole() - role request unsupported.", t);
151                     future.set(RpcResultBuilder.<SetRoleOutput>failed()
152                             .withError(RpcError.ErrorType.APPLICATION, t.getMessage()).build());
153                 }
154             }
155         });
156     }
157
158     private ListenableFuture<RpcResult<SetRoleOutput>> tryToChangeRole(final OfpRole role) {
159         LOG.info("RoleChangeTask called on device:{} OFPRole:{}", getDeviceInfo().getNodeId().getValue(), role);
160
161         final Future<BigInteger> generationFuture = roleService.getGenerationIdFromDevice(getVersion());
162
163         return Futures.transform(JdkFutureAdapters.listenInPoolThread(generationFuture), (AsyncFunction<BigInteger, RpcResult<SetRoleOutput>>) generationId -> {
164             LOG.debug("RoleChangeTask, GenerationIdFromDevice from device {} is {}", getDeviceInfo().getNodeId().getValue(), generationId);
165             final BigInteger nextGenerationId = getNextGenerationId(generationId);
166             LOG.debug("nextGenerationId received from device:{} is {}", getDeviceInfo().getNodeId().getValue(), nextGenerationId);
167             final Future<RpcResult<SetRoleOutput>> submitRoleFuture = roleService.submitRoleChange(role, getVersion(), nextGenerationId);
168             return JdkFutureAdapters.listenInPoolThread(submitRoleFuture);
169         });
170     }
171
172     private static BigInteger getNextGenerationId(final BigInteger generationId) {
173         if (generationId.compareTo(MAX_GENERATION_ID) < 0) {
174             return generationId.add(BigInteger.ONE);
175         } else {
176             return BigInteger.ZERO;
177         }
178     }
179 }