Bump odlparent to 5.0.0
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / role / RoleContextImpl.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.role;
9
10 import com.google.common.util.concurrent.FutureCallback;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.JdkFutureAdapters;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import io.netty.util.HashedWheelTimer;
16 import io.netty.util.Timeout;
17 import io.netty.util.TimerTask;
18 import java.util.Collection;
19 import java.util.HashSet;
20 import java.util.concurrent.CancellationException;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23 import java.util.concurrent.atomic.AtomicReference;
24 import javax.annotation.Nonnull;
25 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
26 import org.opendaylight.openflowplugin.api.OFConstants;
27 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
28 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
29 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainMastershipState;
30 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainMastershipWatcher;
31 import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
32 import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
33 import org.opendaylight.openflowplugin.impl.services.util.RequestContextUtil;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfig;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class RoleContextImpl implements RoleContext {
46     private static final Logger LOG = LoggerFactory.getLogger(RoleContextImpl.class);
47
48     // Timeout  after what we will give up on propagating role
49     private static final long SET_ROLE_TIMEOUT = 10000;
50
51     private final DeviceInfo deviceInfo;
52     private final HashedWheelTimer timer;
53     private final AtomicReference<ListenableFuture<RpcResult<SetRoleOutput>>> lastRoleFuture = new AtomicReference<>();
54     private final Collection<RequestContext<?>> requestContexts = new HashSet<>();
55     private final Timeout slaveTask;
56     private final OpenflowProviderConfig config;
57     private ContextChainMastershipWatcher contextChainMastershipWatcher;
58     private SalRoleService roleService;
59
60     RoleContextImpl(@Nonnull final DeviceInfo deviceInfo,
61                     @Nonnull final HashedWheelTimer timer,
62                     final long checkRoleMasterTimeout,
63                     final OpenflowProviderConfig config) {
64         this.deviceInfo = deviceInfo;
65         this.timer = timer;
66         this.config = config;
67         slaveTask = timer.newTimeout((timerTask) -> makeDeviceSlave(), checkRoleMasterTimeout, TimeUnit.MILLISECONDS);
68
69         LOG.info("Started timer for setting SLAVE role on device {} if no role will be set in {}s.",
70                 deviceInfo,
71                 checkRoleMasterTimeout / 1000L);
72     }
73
74     @Override
75     public DeviceInfo getDeviceInfo() {
76         return deviceInfo;
77     }
78
79     @Override
80     public void setRoleService(final SalRoleService salRoleService) {
81         roleService = salRoleService;
82     }
83
84     @Override
85     public void registerMastershipWatcher(@Nonnull final ContextChainMastershipWatcher newWatcher) {
86         this.contextChainMastershipWatcher = newWatcher;
87     }
88
89     @Override
90     public void close() {
91         changeLastRoleFuture(null);
92         requestContexts.forEach(requestContext -> RequestContextUtil
93                 .closeRequestContextWithRpcError(requestContext, "Connection closed."));
94         requestContexts.clear();
95     }
96
97     @Override
98     public void instantiateServiceInstance() {
99         final ListenableFuture<RpcResult<SetRoleOutput>> future = sendRoleChangeToDevice(OfpRole.BECOMEMASTER);
100         changeLastRoleFuture(future);
101         Futures.addCallback(future, new MasterRoleCallback(), MoreExecutors.directExecutor());
102     }
103
104     @Override
105     public ListenableFuture<Void> closeServiceInstance() {
106         changeLastRoleFuture(null);
107         return Futures.immediateFuture(null);
108     }
109
110     @Override
111     public <T> RequestContext<T> createRequestContext() {
112         final AbstractRequestContext<T> ret = new AbstractRequestContext<T>(deviceInfo.reserveXidForDeviceMessage()) {
113             @Override
114             public void close() {
115                 requestContexts.remove(this);
116             }
117         };
118
119         requestContexts.add(ret);
120         return ret;
121     }
122
123     @Nonnull
124     @Override
125     public ServiceGroupIdentifier getIdentifier() {
126         return deviceInfo.getServiceIdentifier();
127     }
128
129     private void changeLastRoleFuture(final ListenableFuture<RpcResult<SetRoleOutput>> newFuture) {
130         slaveTask.cancel();
131         lastRoleFuture.getAndUpdate(lastFuture -> {
132             if (lastFuture != null && !lastFuture.isCancelled() && !lastFuture.isDone()) {
133                 lastFuture.cancel(true);
134             }
135
136             return newFuture;
137         });
138     }
139
140     private ListenableFuture<RpcResult<SetRoleOutput>> makeDeviceSlave() {
141         final ListenableFuture<RpcResult<SetRoleOutput>> future = sendRoleChangeToDevice(OfpRole.BECOMESLAVE);
142         changeLastRoleFuture(future);
143         Futures.addCallback(future, new SlaveRoleCallback(), MoreExecutors.directExecutor());
144         return future;
145     }
146
147     private ListenableFuture<RpcResult<SetRoleOutput>> sendRoleChangeToDevice(final OfpRole newRole) {
148         final Boolean isEqualRole = config.isEnableEqualRole();
149         if (isEqualRole) {
150             LOG.warn("Skip sending role change request to device {} as user enabled"
151                     + " equal role for controller", deviceInfo);
152             return Futures.immediateFuture(null);
153         }
154         LOG.debug("Sending new role {} to device {}", newRole, deviceInfo);
155
156         if (deviceInfo.getVersion() >= OFConstants.OFP_VERSION_1_3) {
157             final SetRoleInput setRoleInput = new SetRoleInputBuilder()
158                     .setControllerRole(newRole)
159                     .setNode(new NodeRef(deviceInfo.getNodeInstanceIdentifier()))
160                     .build();
161
162             final Future<RpcResult<SetRoleOutput>> setRoleOutputFuture = roleService.setRole(setRoleInput);
163
164             final TimerTask timerTask = timeout -> {
165                 if (!setRoleOutputFuture.isDone()) {
166                     LOG.warn("New role {} was not propagated to device {} during {} sec", newRole,
167                             deviceInfo, SET_ROLE_TIMEOUT);
168                     setRoleOutputFuture.cancel(true);
169                 }
170             };
171
172             timer.newTimeout(timerTask, SET_ROLE_TIMEOUT, TimeUnit.MILLISECONDS);
173             return JdkFutureAdapters.listenInPoolThread(setRoleOutputFuture);
174         }
175
176         LOG.info("Device: {} with version: {} does not support role {}", deviceInfo, deviceInfo.getVersion(), newRole);
177         return Futures.immediateFuture(null);
178     }
179
180     private final class MasterRoleCallback implements FutureCallback<RpcResult<SetRoleOutput>> {
181         @Override
182         public void onSuccess(RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
183             contextChainMastershipWatcher.onMasterRoleAcquired(
184                     deviceInfo,
185                     ContextChainMastershipState.MASTER_ON_DEVICE);
186             LOG.debug("Role MASTER was successfully set on device, node {}", deviceInfo);
187         }
188
189         @Override
190         public void onFailure(final Throwable throwable) {
191             if (!(throwable instanceof CancellationException)) {
192                 contextChainMastershipWatcher.onNotAbleToStartMastershipMandatory(
193                         deviceInfo,
194                         "Was not able to propagate MASTER role on device. Error: " + throwable.toString());
195             }
196         }
197     }
198
199     private final class SlaveRoleCallback implements FutureCallback<RpcResult<SetRoleOutput>> {
200         @Override
201         public void onSuccess(final RpcResult<SetRoleOutput> result) {
202             contextChainMastershipWatcher.onSlaveRoleAcquired(deviceInfo);
203             LOG.debug("Role SLAVE was successfully set on device, node {}", deviceInfo);
204         }
205
206         @Override
207         public void onFailure(final Throwable throwable) {
208             if (!(throwable instanceof CancellationException)) {
209                 contextChainMastershipWatcher.onSlaveRoleNotAcquired(deviceInfo,
210                         "Was not able to propagate SLAVE role on device. Error: " + throwable.toString());
211             }
212         }
213     }
214 }